Skip to content

Instantly share code, notes, and snippets.

@dannycoates
Last active September 29, 2015 04:20
Show Gist options
  • Save dannycoates/44b1965a6be42060e15f to your computer and use it in GitHub Desktop.
Save dannycoates/44b1965a6be42060e15f to your computer and use it in GitHub Desktop.

Device info

Here are the fields stored on the server for each device a user adds.

{
  // The user this device belongs to
  "uid": "4c352927cd4f4a4aa03d7d1893d950b8",
  // The active session for this device
  "sessionToken": "27cd4f4a4aa03d7d186a2ec81cbf19d5c8a604713362df9ee15c4f4a4aa03d7d",
  // A server assigned identifier for this device
  "id":"01ab",
  // A human identifier for this devices. Able to be updated by the user
  "name": "My Phone",
  // General type of device. Useful for device list icons and demographic metrics
  "type": "mobile",
  // Timestamp of when the device was first created, à la Date.now()
  "createdAt": 1442785364807,
  // The push notification endpoint for this device/user
  "callback": "https://updates.push.services.mozilla.com/update/abcdef01234567890abcdefabcdef01234567890abcdef"
}

POST /v1/account/login

New Device

curl -v \
-X POST \
-H "Content-Type: application/json" \
https://api-accounts.dev.lcip.org/v1/account/login?keys=true \
-d '{
  "email": "me@example.com",
  "authPW": "996bc6b1aa63cd69856a2ec81cbf19d5c8a604713362df9ee15c2bf07128efab",
  "device": {
    "name": "My Phone",
    "type": "mobile",
    "callback": "https://updates.push.services.mozilla.com/update/abcdef01234567890abcdefabcdef01234567890abcdef"
  }
}'

Returning Device

curl -v \
-X POST \
-H "Content-Type: application/json" \
https://api-accounts.dev.lcip.org/v1/account/login?keys=true \
-d '{
  "email": "me@example.com",
  "authPW": "996bc6b1aa63cd69856a2ec81cbf19d5c8a604713362df9ee15c2bf07128efab",
  "device": {
    "id": "3d7d"
  }
}'

Response

{
  "uid": "4c352927cd4f4a4aa03d7d1893d950b8",
  "sessionToken": "27cd4f4a4aa03d7d186a2ec81cbf19d5c8a604713362df9ee15c4f4a4aa03d7d",
  "keyFetchToken": "7d1893d950b8cd69856a2ec81cbfd7d1893d950b3362df9e56a2ec81cbf19d5c",
  "verified": true,
  "authAt": 1392144866,
  "device": {
    "id": "3d7d",
    "name": "My Phone",
    "type": "mobile",
    "callback": "https://updates.push.services.mozilla.com/update/abcdef01234567890abcdefabcdef01234567890abcdef"
  }
}

POST /v1/account/create

New Device

curl -v \
-X POST \
-H "Content-Type: application/json" \
"https://api-accounts.dev.lcip.org/v1/account/create?keys=true" \
-d '{
  "email": "me@example.com",
  "authPW": "996bc6b1aa63cd69856a2ec81cbf19d5c8a604713362df9ee15c2bf07128efab",
  "device": {
    "name": "My Phone",
    "type": "mobile",
    "callback": "https://updates.push.services.mozilla.com/update/abcdef01234567890abcdefabcdef01234567890abcdef"
  }
}'

Returning Device

If a device id exists for this device, the request may include it, but it must also include the other info as well since device ids are not globally unique.

curl -v \
-X POST \
-H "Content-Type: application/json" \
"https://api-accounts.dev.lcip.org/v1/account/create?keys=true" \
-d '{
  "email": "me@example.com",
  "authPW": "996bc6b1aa63cd69856a2ec81cbf19d5c8a604713362df9ee15c2bf07128efab",
  "device": {
    "id": "3d7d",
    "name": "My Phone",
    "type": "mobile",
    "callback": "https://updates.push.services.mozilla.com/update/abcdef01234567890abcdefabcdef01234567890abcdef"
  }
}'

Response

{
  "uid": "4c352927cd4f4a4aa03d7d1893d950b8",
  "sessionToken": "27cd4f4a4aa03d7d186a2ec81cbf19d5c8a604713362df9ee15c4f4a4aa03d7d",
  "keyFetchToken": "7d1893d950b8cd69856a2ec81cbfd7d1893d950b3362df9e56a2ec81cbf19d5c",
  "authAt": 1392144866,
  "device": {
    "id": "3d7d",
    "name": "My Phone",
    "type": "mobile",
    "callback": "https://updates.push.services.mozilla.com/update/abcdef01234567890abcdefabcdef01234567890abcdef"
  }
}

POST /v1/account/device

Authenticated with session token or oauth

New Device

curl -v \
-X POST \
-H "Host: api-accounts.dev.lcip.org" \
-H "Content-Type: application/json" \
-H 'Authorization: Hawk id="d4c5b1e3f5791ef83896c27519979b93a45e6d0da34c7509c5632ac35b28b48d", ts="1373391043", nonce="ohQjqb", hash="vBODPWhDhiRWM4tmI9qp+np+3aoqEFzdGuGk0h7bh9w=", mac="LAnpP3P2PXelC6hUoUaHP72nCqY5Iibaa3eeiGBqIIU="' \
https://api-accounts.dev.lcip.org/v1/account/device \
-d '{
  "name": "My Phone",
  "type": "mobile",
  "callback": "https://updates.push.services.mozilla.com/update/abcdef01234567890abcdefabcdef01234567890abcdef"
}'

Update Device

curl -v \
-X POST \
-H "Host: api-accounts.dev.lcip.org" \
-H "Content-Type: application/json" \
-H 'Authorization: Hawk id="d4c5b1e3f5791ef83896c27519979b93a45e6d0da34c7509c5632ac35b28b48d", ts="1373391043", nonce="ohQjqb", hash="vBODPWhDhiRWM4tmI9qp+np+3aoqEFzdGuGk0h7bh9w=", mac="LAnpP3P2PXelC6hUoUaHP72nCqY5Iibaa3eeiGBqIIU="' \
https://api-accounts.dev.lcip.org/v1/account/device \
-d '{
  "id": "3d7d",
  "name": "My Old Phone"
}'

Response

{
  "id": "3d7d",
  "name": "My Phone",
  "type": "mobile",
  "callback": "https://updates.push.services.mozilla.com/update/abcdef01234567890abcdefabcdef01234567890abcdef"
}

POST /v1/account/device/destroy

Authenticated with session token or oauth

Request

curl -v \
-X POST \
-H "Host: api-accounts.dev.lcip.org" \
-H "Content-Type: application/json" \
-H 'Authorization: Hawk id="d4c5b1e3f5791ef83896c27519979b93a45e6d0da34c7509c5632ac35b28b48d", ts="1373391043", nonce="ohQjqb", hash="vBODPWhDhiRWM4tmI9qp+np+3aoqEFzdGuGk0h7bh9w=", mac="LAnpP3P2PXelC6hUoUaHP72nCqY5Iibaa3eeiGBqIIU="' \
https://api-accounts.dev.lcip.org/v1/account/device/destroy \
-d '{
  "id": "3d7d"
}'

Response

{}

GET /v1/account/devices

Authenticated with session token or oauth

Request

curl -v \
-X GET \
-H "Host: api-accounts.dev.lcip.org" \
-H "Content-Type: application/json" \
-H 'Authorization: Hawk id="d4c5b1e3f5791ef83896c27519979b93a45e6d0da34c7509c5632ac35b28b48d", ts="1373391043", nonce="ohQjqb", hash="vBODPWhDhiRWM4tmI9qp+np+3aoqEFzdGuGk0h7bh9w=", mac="LAnpP3P2PXelC6hUoUaHP72nCqY5Iibaa3eeiGBqIIU="' \
https://api-accounts.dev.lcip.org/v1/account/devices \

Response

Instead of including the sessionToken field, each device will have a status of "connected" or "disconnected".

[
  {
    "id": "3d7d",
    "name": "My Phone",
    "type": "mobile",
    "status": "connected",
    "callback": "https://updates.push.services.mozilla.com/update/abcdef01234567890abcdefabcdef01234567890abcdef"
  },
  {
    "id": "01ab",
    "name": "My Desktop",
    "type": "desktop",
    "status": "disconnected",
    "callback": "https://updates.push.services.mozilla.com/update/d4c5b1e3f5791ef83896c27519979b93a45e6d0da34c75"
  }  
]

Special Cases

Recovering Device

A recovering device is one that has been registered before but has lost its device id. We may rely on the device "name" field in this case. The recovering device will login as a new device, submitting its name, if the name matches an existing device we will consider them to be the same, otherwise a new device will be created.

Sharing Devices

A shared device may or may not use the same device id for each user (there are tradeoffs for both) but either way it should be difficult for the service to detect if a device is shared and by whom.

Unkown Device

If a login request specifies an unknown device id the login may still succeed, however the device field in the response will be null. This may indicate that the device was explicitly deleted from the device list by the user. A POST to /v1/account/device can attach a new device to the current session.

Questions

  • How big should the device id be?
    • large enough to make collisions unlikely within an individual account
    • small enough that a device id alone can't distinguish between hundreds of accounts
    • A 2-byte id seems to be small enough. Too small?
  • For shared devices is device info more closely bound to the account or device?
    • Example: Alice and Bob share a device. Alice registers the device with the name "Alice's Tablet". Later Bob logs in and changes the name to "Bob's Tablet". When Alice logs in again, which name should she see?
    • The server still has her device name as "Alice's Tablet", but the device may have saved it locally as "Bob's Tablet". Should Alice's record be updated?
    • It can work either way, but we should decide on one or the other.
      • shared device, shared info
      • or shared device, individual info
      • the push callback leads me to lean toward individual info
@rfk
Copy link

rfk commented Sep 29, 2015

Alice and Bob share a device. Alice registers the device with the name "Alice's Tablet". Later Bob
logs in and changes the name to "Bob's Tablet".

It's not really clear how this should behave from device's individual perspective, let alone when you put the server in the mix. For e.g. a desktop firefox instance, ISTM that "Bob logs in" implies that Alice gets logged out, which could clear the locally-stored device name and other FxA-related customizations. For a dedicated Firefox device maybe it's not so simple.

A recovering device is one that has been registered before but has lost its device id.
We may rely on the device "name" field in this case.

I don't think I like this, it seems too easy to have device names that accidentally match, particularly if the device fills in a default based on its hardware platform etc. If we're going to do recovering devices, I think we should let the device provide some sort of fingerprint that it's extremely confident will be unique.

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