This guide describes how to integrate Tokotalk Single Sign-On into your application. Tokotalk Single Sign-On service conforms OpenID Connect specification and operates as OAuth 2.0 authorization server. External documents explaning OAuth 2.0 can be used as supplementary material but this document is prior to others.
- Authenticating the user with Tokotalk Single Sign-On
The diagram shows API flow for the user to login to your application using Tokotalk SSO.
- A user visits your application and needs authentication (a new user or login session is expired)
- Your application initiates authentication by redirecting the user to Tokotalk SSO page.
- The user logs in to SSO and is redirected back to your application with
authorization_code
. - Your application exchanges
authorization_code
toID Token
which contains user's identity information. - Your application validates
ID Token
and obtains user's identity information from it. - Your application creates session to complete login process or creates a new account for the user
sequenceDiagram
participant user as User-Agent
participant app as Application
participant auth as Tokotalk SSO
user->>app: User visits
app->>app: User needs to login first
rect rgb(235, 245, 255)
note over app: STEP 1: Initiate authorization
app-->>user: Redirect to authorize endpoint
user->>auth: GET /auth
end
auth-->>user: Display login page
user->>auth: Login to SSO
auth-->>user: Redirect with authorization code. (302 Found)
rect rgb(235, 245, 255)
note over app: STEP 2: Exchange code for token
user->>app: GET {redirect_uri}?code={authorization_code}
app->>auth: Exchange authorization_code for tokens<br>POST /oauth/token
auth-->>app: {id_token, access_token, ...}
end
rect rgb(235, 245, 255)
note over app: STEP 3: Obtain identity of the user
opt Requires cache
app->>auth: GET /.well-known/jwks.json
auth-->>app: public keys for token verification (JWKS)
end
app->>app: Verify id_token
app->>app: Application sign in/up process using user identity contained in ID token<br>Create session for application by your own method
app-->>user: Login success
end
(OAuth 2.0 is originally designed as authorization framework and OpenID Connect is built on top of OAuth 2.0. We will use it for Authentication
)
When the user visits your application and needs to login, the first step to do is requesting authentication by redirecting the user to /auth
endpoint. As a result, the user is redirected back to the location where you specified with redirect_uri
parameter with authorization_code
.
Create a unique session token which holds state between your application and the user to protect your request from cross-site request forgery (CSRF). A random generated string or hash from your internal state is a good option for it. Store the state token in your backend service to check it later. Pass this state token as state
parameter of /auth
request in the next step.
You can omit this step for your first implementation, but it is recommended to use state
parameter for security.
Redirect the user to /auth
endpoint with below parameters.
If the user is not logged in to SSO, the login page is presented. After logged in, the user is redirected to redirect_uri
with authorization_code
.
If the user has already logged in to SSO, the user is redirected back without seeing any screen. (Consent
screen of conventional OAuth flow could be implemented for later use case, but it doesn't exist for now)
GET /auth?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope=openid+profile&state={STATE}
Host: auth.eks.codebrick.io
Parameters
Name | Description | Required |
---|---|---|
response_type | Use code . (authorization grant flow) |
Required |
client_id | Your application's client ID. | Required |
redirect_uri | Your application's callback URI where the authorization response is sent back. | Required |
scope | Use openid profile . (space delimited strings of authorization scope) |
Required |
state | anti-foregery state token. This is optional, but strongly recommended for security. | Optional |
This is an example of authentication request. Copy and paste it into your browser, then you'll see the login page.
https://auth.eks.codebrick.io/auth?
response_type=code&
client_id=client.ZTM3YWVlZDViYWViZDE5MG&
redirect_uri=https://your-app.example.com/auth_callback&
scope=openid+profile&
state=NWE1OWY5NzJhODNjMjQ3Nz
If STEP 1 was successful, the user is redirected to the location where you specified as redirect_uri
paramemter in STEP 1-2 with query parameters authorization_code
and state
.
https://your-app.example.com/auth_callback?code=OTUzZDg4Mjg3MWZkMzkxMj&state=NWE1OWY5NzJhODNjMjQ3Nz
authorization_code
is an one-time used code to be exchanged for tokens. The next step is to exchange authorization_code
(or briefly code
) to tokens.
You should confirm state
query parameter by comparing it with the value which you stored in your backend in STEP 1-1.
The next step is exchanging authorization_code
to access_token
, refresh_token
and id_token
.
Your application server exchanges them by sending POST request to token endpoint.
Remember that code
is supposed to be used one-time only. Repeated exchange request with same code
will fail.
The exchange request must contain CLIENT_ID
and CLIENT_SECRET
in Basic auth header.
(Authorization: Basic <credentials>
, where credentials is base64 encoding of 'CLIENT_ID' + ':' + 'CLIENT_SECRET')
POST /oauth/token
Host: auth.eks.codebrick.io
Content-Type: application/x-www-form-urlencoded
Authorization: Basic {Base64(CLIENT_ID:CLIENT_SECRET)}
grant_type=authorization_code&code={CODE}&redirect_uri={REDIRECT_URI}
Parameters
Name | Description |
---|---|
grant_type | authorization_code |
code | The authorization code that was returned to redirect_uri as query parameter code . |
redirect_uri | redirect_uri of initial request (STEP-1). This is used only for confirmation. Token exchange request won't call it |
A successful response contains following fields as JSON object.
Response
Name | Description |
---|---|
id_token | A JWT that contains user's identity information. |
access_token | A token can be used to access othe resource server. |
expires_in | The remaining life time of access token. |
refresh_token | A token to renew access_token. |
scope | The scope of access_token. |
token_type | Bearer |
An ID token
which is retrieved in previous step is a JWT(JSON Web Token). A JWT is simply base64 encoded JSON object signed by cryptographical algorithms. You should validate JWT to confirm that it is signed by a valid issuer. ID Token
contains user's identity information and your application completes login process using that information.
Tokotalk SSO service provides public keys to validate JWT as JWKS(JSON Web Keys) format on public well-known folder. Public keys can be changed periodically as security practice but it won't be frequent, so you can cache them to avoid repeated round trip of requests. Do not repeat it for every request. You can manually cache them or use libraries implements in-memory caches for JWKS.
GET /.well-known/jwks.json
Host: auth.eks.codebrick.io
It is critical to validate JWT before using it. By validation, you can confirm it was issued by a valid issuer, which is Tokotalk SSO in this case. Validation is a process of comparing crytographic signature and supported by mutliple libraries for the most of programming languages. jwt.io/libraries
This is an example of parsed ID token payload.
{
"iss":"https://accounts.eks.codebrick.io",
"sub":"83169e34-d306-462b-85d2-7f165935e893",
"aud":"client-1",
"exp":"1653314314",
"iat":"1653314014",
"email":"[email protected]",
"name":"name of user"
}
Some fields must be verified. (Libraries usually provide arguments to check them)
Field | Description | Need to verify |
---|---|---|
iss | Issuer of token. This must be https://accounts.eks.codebrick.io |
YES |
aud | Audience of token. This must be your CLIENT ID . |
YES |
sub | Subject. This is the user's SSO ID. | |
exp | Expiry time. It must be after current date. | YES |
iat | Issued time of token. | |
Email address of the user. | ||
name | Name of the user. |
Proceed to sign in/up process using user information obtained from ID token
. This part is up to your application.
To logout the user from SSO, redirect the user to /signout
endpoint.
GET /signout?redirect_uri=https://www.example.com
HTTP/1.1 302 Found
Location: https://www.example.com
Parameters
Name | Description |
---|---|
redirect_uri | The user is redirected back to this location after signing out from SSO |
example-app is a simple node.js application to demonstrate how to integrate SSO into your application. Take a look for quick understanding.
If your application has existing user accounts which were created before SSO is integrated, you can create SSO accounts for them through following API. It will create a new account and respond its SSO ID.
For authorization, the API must contain CLIENT_ID
and CLIENT_SECRET
in Basic auth header as we do 2-2 exchanging code for token.
POST /v1/api/users
Host: auth.eks.codebrick.io
Authorization: Basic {Base64(CLIENT_ID:CLIENT_SECRET)}
Content-Type: application/json
{
"email": "[email protected]",
"password": "password"
"name": "John Doe",
"phone": "+62 21 12345678"
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"id": "ad73776d-004b-42b7-a725-5aa5fb0ecd74",
"new": true
}
Parameters
Name | Type | Description |
---|---|---|
string |
Email address of user. | |
password | string |
Initial password for the account. Password must contain 8 or more characters. |
name | string |
Name of user. |
phone | string |
Phone number. |
A successful response contains following fields as JSON object.
Response
Name | Type | Description |
---|---|---|
id | string |
SSO ID of the user. It is same as sub field of ID Token. |
new | boolean |
Indicates whether an account is created. |
When the user want to change password, redirect the user to /password
endpoint.
The user will be redirect back to redirect_uri
after changing password.
GET /password?redirect_uri=https://www.example.com
HTTP/1.1 302 Found
Location: https://www.example.com
Parameters
Name | Description |
---|---|
redirect_uri | The user is redirected back to this location after changing password |