The design of account registration API using JWT
> JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties.
These are the well-known use cases of JWT in WebAPI.
- OIDC ID Token
- OAuth Access Token
- JWT Authentication
I recently designed and implemented an account registration flow using JWT in my project.
This post will show you how to achieve a flexible account registration flow with a minimal data store.
## Account registration flow requirements
This post introduces the general account registration flow for native apps.
- User agrees to Terms of Service, Privacy Policy
- User enters an email address or SMS number and enters a confirmation digit string received
- User enters password
- User enters nickname
- User enters birthdate
- User confirms the input items and completes account registration
This post also considers ID federation with external Identity Providers.
- User agrees to Terms of Service, Privacy Policy
- User selects Identity Provider and gets user information by OIDC
- User enters nickname(Information from Identity Provider is preset)
- User enters birthdate(Information from Identity Provider is preset)
- User confirms the input items and completes account registration
When mBaaS appeared several years ago, they provided developers with an API to first create a plain user object and then set attributes on it.But the combination of those APIs for a this registration flow is not intuitive. For a more flexible but more intuitive implementation, I design stateful, transactional APIs using JWT.
## Stateful implementation using JWT named “Transactional Registration Flow”
The sequence diagram is shown.
The details of the request / response are not described here. Each time a user interaction occurs, the native app also interacts with the backend server.
- Use JWT-formatted Registration Token to represent transactions
- Backend server validates each parameter
- Validated registration data is included in the Payload of Registration Token
- Backend server manages only Registration Token identifier
### Registration Token
Registration Token is a string in JWT format.(The example below uses JSON Web Signature.)
The JWT header includes “kid”, “alg”, “typ”claims.
{"alg": "HS256", // Only backend server verifies signature"kid": "samplehs256","typ": "registration" // for Registration Token}
The initialized JWT payload contains “iat”, “iss”, “jti”, “aud” claims.
{"iat": 1582005693,"iss": "http://server.example.com", // Identifier of Backend Server"jti": "abcdefghijklmn", // Identifier of Registration Token"aud": "this_is_native_app_identifier" // Identifier of Native App like OAuth 2.0 Client ID}
The backend server verifies these metadata and signatures each time it receives a request. Then, for each numbered operation in the sequence, the backend server adds data to the JWT payload.
In “1. User agrees to Terms of Service, Privacy Policy”
{"iat": 1582005693,"iss": "http://server.example.com","jti": "abcdefghijklmn","aud": "this_is_native_app_identifier","agreed_tos": "v2020_02_01","agreed_privacy_policy": "v2020_01_01"}
The backend server regenerates the signature when updating the payload.
By repeating this, the backend server finally adds all the data required for registration to Payload.
{"iat": 1582005693,"iss": "http://server.example.com","jti": "abcdefghijklmn","agreed_tos": "v2020_02_01","agreed_privacy_policy": "v2020_01_01","email": "tokyo_taro@example.com","email_verified": true,"password": "(Encrypted Password)","nickname": "Taro","birthdate": "1990-12-01"}
Some claims will be validated again in the final registration process.
### Using Identity Fedetation
When using ID federation with an external Identity Provider, part of the sequence changes.
When using OpenID Connect, obtain the values to be used as “idp”, “idp_user_id”, and “email” from the ID token’s payload.
Set “email_verified” to true if the IdP is verifying the email.
If there is a value for “nickname” or “birthdate”, preset it in the user interaction form and ask for user input.
{"iat": 1582005693,"iss": "http://server.example.com","jti": "abcdefghijklmn","agreed_tos": "v2020_02_01","agreed_privacy_policy": "v2020_01_01","idp": "google""idp_user_id": "1234567890","email": "tokyo_taro@example.com","email_verified": true,"nickname": "TokyoTaro","birthdate": null}
The registration token payload contains IdP’s user ID.
However, there is a back-end server signature that makes it difficult for an attacker to tamper with it.
{"iat": 1582005693,"iss": "http://server.example.com","jti": "abcdefghijklmn","agreed_tos": "v2020_02_01","agreed_privacy_policy": "v2020_01_01","idp": "google""idp_user_id": "1234567890","email": "tokyo_taro@example.com","email_verified": true,"nickname": "Taro","birthdate": 1990-12-01}
In this example, the final Payload does not include the password.
The backend server determines the required parameters according to its own policy.
### Datastore required by backend server
Backend servers need to manage only “identifiers” because the Registration Token holds the data needed for registration.(If you want to handle more sensitive information, it is better to store it on the backend server.)
There are three minimum functions required for the data store.
- create : Create an identifier when generating a registration token and include it in “jti” of Payload
- read : When validating the Registration Token, the backend server uses the Payload “jti” value as a key to check if it is valid.
- delete : When the registration process is completed, delete the value with “jti” as the key
Not only Memcached and Redis but also various data stores such as RDB / NoSQL have this function.
Datastore capacity is kept low because only identifiers are managed.
Also, compared to stateless APIs and APIs that hold temporary registration data in the data store, flows using JWT can be handled only by modifying the individual APIs and the logic of registration completion processing when changing the registration flow therefore, the impact on the data store can be reduced.
### Other use cases
The combination of such a simple data store and JWT can be applied not only to registration but also to various functions.
I will continue to show you how to use JWT effectively.
This is an English translation of the idea presented at the Digital Identity Community event in Japan.