Skip to content

Instantly share code, notes, and snippets.

@dannycoates
Last active December 19, 2015 12:59
Show Gist options
  • Save dannycoates/5958762 to your computer and use it in GitHub Desktop.
Save dannycoates/5958762 to your computer and use it in GitHub Desktop.

Key Server API

For details of the client/server server protocol and how each parameter is derived see the design.

Our SRP protocol order is slightly different from the sample on wikipedia, but the variables are the same.

Hawk is used to authenticate /sign and /resetAccount requests.

Request Format

All POST requests require a content-type of application/json with a JSON encoded body. All keys and binary data are base16 encoded strings.

Error Responses

All error responses include a JSON body in addition to the HTTP status code. For example:

{
  "code": 400,
  "error": "Bad Request",
  "message": "the value of salt is not allowed to be undefined"
}

Endpoints

A development server is available at http://idp.profileinthecloud.net for testing. All data stored there will be deleted periodically, and new code will be deployed regularly.

The API is accessible over HTTP on the development server only. Production servers will be HTTPS only

POST /create

Creates a user account.

Parameters

  • email - the primary email for this account
  • verifier - the derived SRP verifier
  • wrapKb - the wrapped kB key
  • salt - SPR salt
  • params
    • srp
      • alg - hash function for SRP (sha256)
      • N_bits - SPR group bits (2048)
    • stretch
      • rounds - number of rounds of password stretching

Request

curl -v \
-X POST \
-H "Content-Type: application/json" \
http://idp.profileinthecloud.net/create \
-d '{
  "email": "me2@example.com",
  "verifier": "7597c55064c73bf1b2735878cb8711c289fc8f1cfb3d633a4593b36a8c51dbd68b27f649949de27d1dcccf7ece1e1a42c5c6bdc3d209cf13a3813d333bfcadd2641a9a3e2eb4289788ed8510cc8f2f1061789d58aef38b9d21b81831413f55473f9fae9253549b2428a403d6fa51e6fb43d2f8a302e132cf902ffade52c02e6a4e0bda74fcaa2347be4664f553d332df8166278c0e2f8663aa9238a2429631f7afd11622e193747b57975c51bbb69bb11f60c1a5ba449d3119e70d1ec580212151f79b26e73a57dba313376f0ba7a2afc232146a3b1d68b2d0afc35ebb8699cb10b3a3f8e0d51cefc7ac29212b238fb7a87f2f61edc9cbff103e386f778925fe",
  "wrapKb": "129e25a048cdc37353ebbfe6aca8f7e427f483fab73c01e91b23c4a77186c705",
  "salt": "f9fae9253549b2428a403d6fa51e6fb43d2f8a302e132cf902ffade52c02e6a4",
  "params": {
    "srp": {
      "alg": "sha256",
      "N_bits": 2048
    },
    "stretch": {
      "rounds": 100000
    }
  }
}'

Response

{
  "created": true
}

POST /startLogin

Begin the login process

Parameters

  • email - user's email address

Request

curl -v \
-X POST \
-H "Content-Type: application/json" \
http://idp.profileinthecloud.net/startLogin \
-d '{
  "email": "me@example.com"
}'

Response

{
  "sessionId": "b223b00e-5a10-46a9-983c-1c346c0d1907",
  "stretch": {
    "rounds": 100000,
    "salt": "9e1a5712b22ea7ec06eb74422be67040e030a9f041fe258d8ed633d027271704"
  },
  "srp":
  {
    "N_bits": 2048,
    "alg": "sha256",
    "s": "739e25a048cdc37353ebbfe6aca8f7e427f483fab73c01e91b23c4a77186c718",
    "B": "3cd467e3afd4cc2d7abd913e322d76c245c667e9dffc6e28a1108ac02c5af9eee1148a0c735f52ed786c33add4936dd5534326794e03d1b48b77b347c728740288adf488a9f4f11d75bb60e9bb1e975cccd128e28115178de01702fd2e8715e7c33b02c142569669bb52cf167092fa79c3c03c81affc5c8d97fd3cb8d12605e5dd59f75e21376cfdc6536125650ff8559f1c5319a9bfbb5191238c1570d41dc43e880d213fa06ff9d2f6ca7f31e05aef6236ae3657450250c06145a346151c54f227996532bbdc6e1531456174975eded5404baae081b3ce7b42646b98baec1029082823a041aaace4ffa362d5ed42a4e5088c496dda8ba2a35e804e89597313"
  }
}

How to derive the values for the next step are explained in the SRP Client Calculation

POST /finishLogin

Get a signToken, kA, and wrapKb

Parameters

  • sessionId - the sessionId received from /startLogin
  • A - the derived SRP "A" value
  • M - the derived SRP "M" value

Request

curl -v \
-X POST \
-H "Content-Type: application/json" \
http://idp.profileinthecloud.net/finishLogin \
-d '{
  "sessionId": "4c352927-cd4f-4a4a-a03d-7d1893d950b8",
  "A": "024ba1bb53d42918dc34131b41548843e1fa533bd5952be3ec8884fba4aa5c3542ac161fa0d5587d1e694248573be8a1b18f7b0c132f74ddde08ac2a230f4db4a1d831eb74ee772c83121ecba80e51b9293942681655dca4f98a766408fbaf5c13c09d21b9d6d3dabea8024fbb658ca67e20bc63cb349cb9bea54d7b1f4990cfe45fad7e492ca90a578d7b559143eb0987825b48aa6bfbb684b7973c75e6e98011ffc3ba724797ea575d440fa3c052be978590f828d3f850a4ccdecbe8e4d2c6d2b981e3c75ee26d5cf477cda9273a60000d6e942d4eb27e027a8ca16f668862260a4c9d3ab6cd3139decf4976633844684b8371a68a7419f6beffd2fc078327",
  "M": "396a46b1aa63cd69856a2ec81cbf19d5c8a60471cc62df9ee15c2bf07838efba"
}'

Response

{
  "bundle": "d486e79c9f3214b0010fe31bfb50fa6c12e1d093f7770c81c6b1c19c7ee375a6558dd1ab38dbc5eba37bc3cfbd6ac040c0208a48ca4f777688a1017e98cedcc1c36ba9c4595088d28dcde5af04ae2215bce907aa6e74dd68481e3edc6315d47efa6c7b6536e8c0adff9ca426805e9479607b7c105050f1391dffed2a9826b8ad"
}

See decrypting the bundle for info on how to retrieve kA|wrapKb|signToken from the bundle.

POST /sign

Sign a public key

Parameters

  • publicKey - the key to sign (run bin/generate-keypair from jwcrypto)
    • algorithm - "RS" or "DS"
    • n - RS only
    • e - RS only
    • y - DS only
    • p - DS only
    • q - DS only
    • g - DS only
  • duration - time interval from now when the certificate will expire in milliseconds

Headers

The request must include a Hawk header that authenticates the request including payload

Request

curl -v \
-X POST \
-H "Host: idp.profileinthecloud.net" \
-H "Content-Type: application/json" \
-H 'Hawk id="d4c5b1e3f5791ef83896c27519979b93a45e6d0da34c7509c5632ac35b28b48d", ts="1373391043", nonce="ohQjqb", hash="vBODPWhDhiRWM4tmI9qp+np+3aoqEFzdGuGk0h7bh9w=", mac="LAnpP3P2PXelC6hUoUaHP72nCqY5Iibaa3eeiGBqIIU="' \
http://idp.profileinthecloud.net/sign \
-d '{
  "publicKey": {
    "algorithm":"RS",
    "n":"4759385967235610503571494339196749614544606692567785790953934768202714280652973091341316862993582789079872007974809511698859885077002492642203267408776123",
    "e":"65537"
  },
  "duration": 86400000
}'

Response

{
  "err": null,
  "cert": "eyJhbGciOiJEUzI1NiJ9.eyJwdWJsaWMta2V5Ijp7ImFsZ29yaXRobSI6IlJTIiwibiI6IjU3NjE1NTUwOTM3NjU1NDk2MDk4MjAyMjM2MDYyOTA3Mzg5ODMyMzI0MjUyMDY2Mzc4OTA0ODUyNDgyMjUzODg1MTA3MzQzMTY5MzI2OTEyNDkxNjY5NjQxNTQ3NzQ1OTM3NzAxNzYzMTk1NzQ3NDI1NTEyNjU5NjM2MDgwMzYzNjE3MTc1MzMzNjY5MzEyNTA2OTk1MzMyNDMiLCJlIjoiNjU1MzcifSwicHJpbmNpcGFsIjp7ImVtYWlsIjoiZm9vQGV4YW1wbGUuY29tIn0sImlhdCI6MTM3MzM5MjE4OTA5MywiZXhwIjoxMzczMzkyMjM5MDkzLCJpc3MiOiIxMjcuMC4wLjE6OTAwMCJ9.l5I6WSjsDIwCKIz_9d3juwHGlzVcvI90T2lv2maDlr8bvtMglUKFFWlN_JEzNyPBcMDrvNmu5hnhyN7vtwLu3Q"
}

GET /entropy

Get 32 bytes of random data

Request

curl -v http://idp.profileinthecloud.net/entropy

Response

{
  "data": "ac55c0520f2edfb026761443da0ab27b1fa18c98912af6291714e9600aa34991"
}

POST /startResetToken

Begin the reset process

Parameters

  • email - user's email address

Request

curl -v \
-X POST \
-H "Content-Type: application/json" \
http://idp.profileinthecloud.net/startResetToken \
-d '{
  "email": "me@example.com"
}'

Response

{
  "sessionId": "b223b00e-5a10-46a9-983c-1c346c0d1907",
  "stretch": {
    "rounds": 100000,
    "salt": "9e1a5712b22ea7ec06eb74422be67040e030a9f041fe258d8ed633d027271704"
  },
  "srp":
  {
    "N_bits": 2048,
    "alg": "sha256",
    "s": "739e25a048cdc37353ebbfe6aca8f7e427f483fab73c01e91b23c4a77186c718",
    "B": "3cd467e3afd4cc2d7abd913e322d76c245c667e9dffc6e28a1108ac02c5af9eee1148a0c735f52ed786c33add4936dd5534326794e03d1b48b77b347c728740288adf488a9f4f11d75bb60e9bb1e975cccd128e28115178de01702fd2e8715e7c33b02c142569669bb52cf167092fa79c3c03c81affc5c8d97fd3cb8d12605e5dd59f75e21376cfdc6536125650ff8559f1c5319a9bfbb5191238c1570d41dc43e880d213fa06ff9d2f6ca7f31e05aef6236ae3657450250c06145a346151c54f227996532bbdc6e1531456174975eded5404baae081b3ce7b42646b98baec1029082823a041aaace4ffa362d5ed42a4e5088c496dda8ba2a35e804e89597313"
  }
}

POST /finishResetToken

Get a resetToken, kA, and wrapKb

Parameters

  • sessionId - the sessionId received from /startLogin
  • A - the derived SRP "A" value
  • M - the derived SRP "M" value

Request

curl -v \
-X POST \
-H "Content-Type: application/json" \
http://idp.profileinthecloud.net/finishResetToken \
-d '{
  "sessionId": "4c352927-cd4f-4a4a-a03d-7d1893d950b8",
  "A": "024ba1bb53d42918dc34131b41548843e1fa533bd5952be3ec8884fba4aa5c3542ac161fa0d5587d1e694248573be8a1b18f7b0c132f74ddde08ac2a230f4db4a1d831eb74ee772c83121ecba80e51b9293942681655dca4f98a766408fbaf5c13c09d21b9d6d3dabea8024fbb658ca67e20bc63cb349cb9bea54d7b1f4990cfe45fad7e492ca90a578d7b559143eb0987825b48aa6bfbb684b7973c75e6e98011ffc3ba724797ea575d440fa3c052be978590f828d3f850a4ccdecbe8e4d2c6d2b981e3c75ee26d5cf477cda9273a60000d6e942d4eb27e027a8ca16f668862260a4c9d3ab6cd3139decf4976633844684b8371a68a7419f6beffd2fc078327",
  "M": "396a46b1aa63cd69856a2ec81cbf19d5c8a60471cc62df9ee15c2bf07838efba"
}'

Response

{
  "bundle": "d486e79c9f3214b0010fe31bfb50fa6c12e1d093f7770c81c6b1c19c7ee375a6558dd1ab38dbc5eba37bc3cfbd6ac040c0208a48ca4f777688a1017e98cedcc1c36ba9c4595088d28dcde5af04ae2215bce907aa6e74dd68481e3edc6315d47efa6c7b6536e8c0adff9ca426805e9479607b7c105050f1391dffed2a9826b8ad"
}

POST /resetAccount

See resetting the account

Request

Parameters

  • bundle - a base16 string of encoded kB|salt|verifier

Headers

The request must include a Hawk header that authenticates the request including payload

curl -v \
-X POST \
-H "Host: idp.profileinthecloud.net" \
-H "Content-Type: application/json" \
-H 'Hawk id="d4c5b1e3f5791ef83896c27519979b93a45e6d0da34c7509c5632ac35b28b48d", ts="1373391043", nonce="ohQjqb", hash="vBODPWhDhiRWM4tmI9qp+np+3aoqEFzdGuGk0h7bh9w=", mac="LAnpP3P2PXelC6hUoUaHP72nCqY5Iibaa3eeiGBqIIU="' \
http://idp.profileinthecloud.net/resetAccount \
-d '{
  "bundle": "a586e79c9f3214b0010fe31bfb50fa6c12e1d093f7770c81c6b1c19c7ee375a6558dd1ab38dbc5eba37bc3cfbd6ac040c0208a48ca4f777688a1017e98cedcc1c36ba9c4595088d28dcde5af04ae2215bce907aa6e74dd68481e3edc6315d47efa6c7b6536e8c0adff9ca426805e9479607b7c105050f1391dffed2a98264bdc"
}'

Response

{
  "reset": true
}

Reference Client

The git repo contains a reference implementation of the client side of the protocol in /client/index.js with sample usage in /client/example.js

@djc
Copy link

djc commented Jul 9, 2013

It might be nice, and more RESTy (HATEOAS), to replace the sessionId token with a URL. I.e., the sessionId key in the /startLogin response becomes "sessionConfirm": "https://idp.profileinthecloud.net/finishLogin/b223b00e-5a10-46a9-983c-1c346c0d1907".

And a nit: I don't think camelCase is that nice for URI components. To me, something like /login/start and /login/finish (or /login/confirm) looks much nicer, and nicely segregates different parts of the flow.

@dannycoates
Copy link
Author

Lets move this discussion to the repo. mozilla/fxa-auth-server#64

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment