email_or_username: 'u6755dc4f',
pdpka5: "g6Rib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEgbyBuVXsJzAkRjK4mAmHNvtOKhyHKSonMiRWg7La+KI4Kp3BheWxvYWTFAbd7ImJvZHkiOnsiYXV0aCI6eyJub25jZSI6ImVkYTA5MjFhYjg5NzkzMGZiODc0OTFjZjlmOTczNGVmIiwic2Vzc2lvbiI6ImxnSFpJRFF4WVRGa09HSTJObUprWXpkall6aGtPRGswTnpCaVlXVmtNV1F6TkRFNXpsZ0ZkeTNOQ1dEQXhDQW1jN2QrcmNkSGZPYWRtUjJVN2xTRko2NzJtY1Q3RmxBNG5Vc2cycEhRNGc9PSJ9LCJrZXkiOnsiaG9zdCI6ImtleWJhc2UuaW8iLCJraWQiOiIwMTIwNmYyMDZlNTU3YjA5Y2MwOTExOGNhZTI2MDI2MWNkYmVkMzhhODcyMWNhNGE4OWNjODkxNWEwZWNiNmJlMjg4ZTBhIiwidWlkIjoiNDFhMWQ4YjY2YmRjN2NjOGQ4OTQ3MGJhZWQxZDM0MTkiLCJ1c2VybmFtZSI6InU2NzU1ZGM0ZiJ9LCJ0eXBlIjoiYXV0aCIsInZlcnNpb24iOjF9LCJjdGltZSI6MTQ3Njc1MzE5NywiZXhwaXJlX2luIjoxNTc2ODAwMDAsInRhZyI6InNpZ25hdHVyZSJ9o3NpZ8RALfJuyhIs/4CIIHi6WpF0sB1GFXH+yVGBztPp5QeqFAIZ4ycUPYGKmtLbR4NxcQHq2d4OTPblwHwoPWdrkawoC6hzaWdfdHlwZSCjdGFnzQICp3ZlcnNpb24B"
pdpka4: "g6Rib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEgTnrhJensoHhID/9vyD+KYm6e+9qDfdbFrB5sjg6YZDUKp3BheWxvYWTFAbd7ImJvZHkiOnsiYXV0aCI6eyJub25jZSI6IjE3ZGVkZTg2MjM1M2I5NWI3ODVlMTUyMDhiZWNmYTZjIiwic2Vzc2lvbiI6ImxnSFpJRFF4WVRGa09HSTJObUprWXpkall6aGtPRGswTnpCaVlXVmtNV1F6TkRFNXpsZ0ZkeTNOQ1dEQXhDQW1jN2QrcmNkSGZPYWRtUjJVN2xTRko2NzJtY1Q3RmxBNG5Vc2cycEhRNGc9PSJ9LCJrZXkiOnsiaG9zdCI6ImtleWJhc2UuaW8iLCJraWQiOiIwMTIwNGU3YWUxMjVlOWVjYTA3ODQ4MGZmZjZmYzgzZjhhNjI2ZTllZmJkYTgzN2RkNmM1YWMxZTZjOGUwZTk4NjQzNTBhIiwidWlkIjoiNDFhMWQ4YjY2YmRjN2NjOGQ4OTQ3MGJhZWQxZDM0MTkiLCJ1c2VybmFtZSI6InU2NzU1ZGM0ZiJ9LCJ0eXBlIjoiYXV0aCIsInZlcnNpb24iOjF9LCJjdGltZSI6MTQ3Njc1MzE5NywiZXhwaXJlX2luIjoxNTc2ODAwMDAsInRhZyI6InNpZ25hdHVyZSJ9o3NpZ8RAY24jVxf/661fILLrRwsfC6/dY102bGPiKCWcYTNLAYR6YZXBP7UstNktpkz7Ymjt9HVZwgVvPxtOpUO8Wne3BKhzaWdfdHlwZSCjdGFnzQICp3ZlcnNpb24B"
   "status": {
      "code": 0,
      "name": "OK"
   "session": "lgHZIDU1YzA3OWJmNWYx...",
   "me":      "/* {user object} */"

Round 2 of the 2-Round Login Protocol

In the first round, the client called getsalt to retrieve a salt and a login_session token.

In this, the second round of the protocol, the client computes the password hash. The password hash is a function of the UTF-8 password, and the binary encoding of the salt (which was returned in hex from the server). The first 192 bytes of output are used for secret-key encryption elsewhere in the site, so they are ignored here. Bytes 192 through 256 of this output are used to authenticate the user during login:

passphraseStream = scrypt(passphrase, unhex(salt), N=215, r=8, p=1, dkLen=256)
v4 = passphraseStream[192:224]
v5 = passphraseStream[224:256]

At first, let's cover version 5 of the protocol, and then we'll go back and cover version 4 below. The 32-byte slice of the passphrase stream (v5 above) is intepreted as an EdDSA private key. The server knows the corresponding public key. The client proves knowledge of this secret key by making a signature that the server can verify.

The client should sign a JSON blob of the form:

  "body": {
    "auth": {
      "nonce": "ab68b24b6bcff3dc6e0cdc558e3e043c",
      "session": "lgG5dGhlbWF4KzhkY2FhNjc4QGdtYWlsLmNvbc5YBShhzQlgwMQgtABibipP7sQIpLv/hO+akJ5mdrD64QkuhY08VdLwtW0="
    "key": {
      "host": "keybase.io",
      "kid": "0120fffa77faf7c189edbb82a942c5feef831335ced44e2fd3155673b023314719070a",
      "username": "u5c7d0817"
    "type": "auth",
    "version": 1
  "ctime": 1476733025,
  "expire_in": 157680000,
  "tag": "signature"
The relevant fields are:
  • nonce: a random 16-byte nonce generated by the client that once accepted cannot be replayed
  • session: returned from getsalt in the previous stage of the login flow
  • host: always keybase.io
  • kid: the key ID of the public key that will verify this signature
  • username: the username who is trying to login. If the login is done via email, this field will be absent and an email field will be present with the intended email address
  • ctime: the UTC-time when this signature was generated
  • expire_in: how long this signature is good for; after it expires, it will not be admissable for login

The client then computes the EdDSA signature of this JSON blob (stringified with minimal white space), and packages the result as a Keybase-style signature. The result is posted to the server as the pdpka5 parameter (note that "PDPKA" stands for Passphrase-Derived Public Key Authentication).

As for the slight complication we hinted at above, clients should log in with both Version 4 and Version 5 of the protocol at the same time. The only differences between the two are that: (1) the secret key is derived from a different slice of the passphrase stream --- v4 versus v5 above; and (2) that the result is posted as the HTTP parameter pdpka4 in the V4 login system and as pdpka5 in the V5 login system.

The first time a user logs in with this scheme, the server captures the V5 public key and requires this version for all subsequent logins. It checks V4 to make sure the user is authorized. We encourage all logins use V4 and V5 simultaneously, so the current login works, and the user is upgraded going forward.

Session cookie

A successful login will reply with a session cookie.

The user object "me"

Upon login, you'll get back a user object. Your user object is a large dictionary and contains pretty much everything about you and your account. (See the user objects page in the documentation for more info.)

Pssst, we're hiring.