Authentication
Unless specified explicitly per project, all applications that need to use an authentication scheme, must use DIT's Central Authentication Service (CAS) for that purpose.
URLs
Use https://auth.dev.krd in dev, staging, and pre-prod environment.
Use https://account.id.krd in production environment.
OpenID Connect
The CAS is an OpenID Connect-compatible service. It strictly follows the specifications of OpenID Connect to perform authentication.
Discovery
It provides a discovery endpoint for dynamic information discovery at the endpoint /.well-known/openid-configuration
OAuth 2.0
The CAS is an OAuth 2.0-compatible service. It strictly follows the specifications of OAuth 2.0 to perform authorization.
Flows
Among the different authorization flows supported by our CAS, we only permit the followings to be used:
- Authorization Code with PKCE
- Implicit
- Client Credentials
Authentication Strategy
When using the CAS for authentication, the method through which authentication is ensured is the validation of access tokens that is received in a Authorization Bearer header.
Access tokens from the CAS are JWTs.
When validating the access token, it is important to check:
issclaim: the Issuer, which corresponds to the URLs in this section.expclaim: the expiration of the token. Only valid tokens must be allowed.audclaim: the audience. This corresponds to the name of the your application as a protected resource in the CAS.alg: the algorithm, which must beRS256.kid: which is the key with which the token is signed. For this, you will need to use the JWKS endpoint (refer to Discovery in this section) to verify the public key.
Note: We highly recommend using existing packages and libraries that take care of the implementations mentioned in this guide instead of making raw HTTP requests using an HTTP client. The existing libraries are battle-tested and take care of many abstraction that you may skip.
Authentication Flows
Client Credentials Flow
DIT's authentication scheme follows OpenID and OAuth2 standards. In your software requires machine-to-machine (M2M) communication, authentication can be achieved through the "client_credentials" flow.
To access the desired APIs within the DIT ecosystem, backend services must obtain an access token from the Central Authentication Service (CAS) by providing a Client ID, Client Secret, and specific scopes to which the given client can access. The CAS is OpenID-compliant and provides a discovery URL, which can be used by OpenID Clients to easily discover the specific configurations of DIT's CAS.
To obtain an M2M token, send a POST request from POSTMAN to DIT's CAS:
POST https://auth.dev.krd/connect/tokenSet the Header to: Content-Type: application/x-www-form-urlencoded
Set the Request Body to:
{
"grant_type": "client_credentials",
"scope": "YOUR SCOPES GO HERE (SPACE SEPARATE THEM IF MULTIPLE)",
"client_id": "YOUR CLIENT ID",
"client_secret": "YOUR CLIENT SECRET"
}Upon successful handling of the request, the response payload will contain an access token, expiry time, token type, and requested scopes:
{
"access_token":"A LONG ACCESS TOKEN HERE",
"expires_in":3600,
"token_type":"Bearer",
"scope":"YOUR REQUESTED SCOPES, SPACE SEPARATED"
}You can use the access token from the response above to connect to the specific API you need.
Authorization Code + PKCE Flow
When using this flow, we prohibit access tokens to be stored on the client-side due to security concerns. All access tokens, refresh tokens, and ID tokens must be concealed and be kept from the browser or any other client that is prone to exposure.
For that, we follow the Backend for Frontends (BFF) Pattern to safely store access tokens. Take a look at our Backend for Frontends for an implementation reference.
We highly recommend OAuth2 Proxy as the go-to solution for the Backend for Frontends implementation. It's ready for use.
If you are about to use this flow, speak to the Head of Digital Development and DevOps to set the Backend for Frontends according to your needs. Otherwise, it is considered to be an application bug.
OAuth 2.0 with Authorization Code Grant and Proof Key for Code Exchange (PKCE) is a more secure way to authenticate users in web and mobile applications, mitigating the risks associated with the traditional Authorization Code flow. Below are the technical details and steps on how this flow works. This flow shall be used whenever there is a user login involded:
Receive a Client ID and Client Secret from DIT for your web client.
- For this to happen, you must provide the callback URL that we need to register so that the user is redirected to it upon successful login.
- The aforementioned callback url will be validated against the
redirect_uriin the request (See point 4 and 5 below)
- The aforementioned callback url will be validated against the
- For this to happen, you must provide the callback URL that we need to register so that the user is redirected to it upon successful login.
The client generates a random string called the
code_verifier. This should be a high-entropy cryptographic random string.The
code_verifieris hashed using SHA-256 to create thecode_challenge. Thecode_challengeis then base64-url-encoded.Authorization Request:
The client redirects the user to the authorization server’s authorization endpoint.
The request includes the following parameters:
response_type=codeclient_idredirect_uriscopecode_challengecode_challenge_method=S256
Example:
bashhttps://authorization-server.com/auth? response_type=code& client_id=YOUR_CLIENT_ID& redirect_uri=YOUR_REDIRECT_URI& scope=YOUR_SCOPES& code_challenge=YOUR_CODE_CHALLENGE& code_challenge_method=S256& state=UNIQUE_IDENTIFIER_OF_CHALLENGENote: It is crucial to store this challenge in a temporary persistent store / database and associate with a unique key. The unique, then, must be sent as a
stateto the authorization server (CAS). Thestatewill later be sent back to your client in step number 5, which can be used to track down thecode_verifier
The user logs in and grants permissions to the client application.
Upon successful authentication and authorization, the authorization server redirects the user back to the client’s specified
redirect_uriwith an authorization code.Example redirect:
bashhttps://your-app.com/callback?code=AUTHORIZATION_CODE&state=UNIQUE_IDENTIFIER_OF_CHALLENGEUse the value of the
stateto find thecode_verifierthat is needed in the next step (6. Token Request).
Token Request:
The client makes a POST request to the authorization server’s token endpoint to exchange the authorization code for an access token.
The request includes:
grant_type=authorization_codeclient_idclient_secret(if applicable)redirect_uricode(the authorization code received in the previous step)code_verifier(the original code verifier used to create the code challenge)
Example of payload sent to the token endpoint:
json{ "grant_type": "authorization_code", "client_id": "YOUR_CLIENT_ID", "client_secret": "YOUR_CLIENT_SECRET", //optional "redirect_uri": "YOUR_REDIRECT_URI", "code": "AUTHORIZATION_CODE", "code_verifier": "YOUR_CODE_VERIFIER" }
Token Response:
- The authorization server validates the
code_verifierby hashing it and comparing it to thecode_challengesent in the authorization request. - If valid, the authorization server returns an access token (and optionally a refresh token) to the client.
- The authorization server validates the
Summary
The PKCE extension provides an additional layer of security by ensuring that the authorization code can only be exchanged for an access token by the client that initiated the authorization request. This is achieved through the code_verifier and code_challenge, preventing authorization code interception attacks and enhancing the security of the OAuth 2.0 Authorization Code flow, especially for public clients like mobile and single-page applications.
Flow Diagram
+--------+ +---------------+
| |--(A) Authorization Request ------>| Resource |
| | + code_challenge | Owner |
| | +---------------+
| |
| |<-(B) Authorization Code ---------<|
| | |
| Client | |
| |--(C) Authorization Code --------->|
| | + code_verifier | Authorization |
| | | Server |
| |<-(D) Access Token ---------------<|
+--------+ +---------------+In this diagram:
- (A) The client sends an authorization request to the authorization server with a
code_challenge. - (B) The authorization server sends an authorization code back to the client.
- (C) The client exchanges the authorization code for an access token, including the
code_verifier. - (D) The authorization server validates the
code_verifierand, if valid, issues an access token.
User On-boarding
Most applications are generally designed to deal with end-users. This means that beyond authentication strategies, applications must store user information to some extent. Since all users are centrally registered in the CAS, applications that use the CAS will delegate registration and authentication to it. Accordingly, applications will need a way to make sure that only specified users can access the given applications. Apparently, not all users registered in the CAS can access all applications that utilize the CAS.
CAS API
For that, applications may request to search in the CAS in order to on-board users to their system using the CAS API.
URLs
Use https://auth.dev.krd in dev, staging, and pre-prod environment.
An internal service URL will be provided for production environment.
Authentication Flow
To authenticate your application to use the CAS API, use a client credentials flow with a Client ID and a Client Secret. Request a development Client ID & Secret from the Head of DevOps.
Required Scopes
To use the CAS API for user on-boarding, cas-admin.users.read scope is required.
Endpoints
Once you have a valid access token, you can send the token as an Authorization Bearer header in your requests to the CAS API.
List Users for Enrollment
To search for users that can be enrolled in your application, use the following endpoint:
Endpoint: GET /api/v1/users/to-enroll
Query Parameters:
Search(required, string): Search term to find users. This searches across user names, emails, and other user identifiers.
Example Request:
GET https://auth.dev.krd/api/v1/users/to-enroll?Search=john.doe@example.com
Authorization: Bearer YOUR_ACCESS_TOKENExample Response:
{
"data": [
{
"id": "user-id-123",
"userName": "john.doe",
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-20T14:45:00Z",
"lastPasswordChangedAt": "2024-01-15T10:30:00Z",
"lockoutEnd": null,
"isDeleted": false,
"isBlocked": false
}
]
}Response Fields:
id(string): Unique identifier for the useruserName(string): Username of the useremail(string): Email address of the userfirstName(string, nullable): First name of the userlastName(string, nullable): Last name of the usercreatedAt(date-time): When the user account was createdupdatedAt(date-time, nullable): When the user account was last updatedlastPasswordChangedAt(date-time, nullable): When the user's password was last changedlockoutEnd(date-time, nullable): When the user's account lockout ends (if locked)isDeleted(boolean): Whether the user account is deletedisBlocked(boolean): Whether the user account is blocked
Get User by ID for Enrollment
To retrieve detailed information about a specific user by their ID, use the following endpoint:
Endpoint: GET /api/v1/users/to-enroll/{id}
Path Parameters:
id(required, string): The unique identifier of the user
Example Request:
GET https://auth.dev.krd/api/v1/users/to-enroll/user-id-123
Authorization: Bearer YOUR_ACCESS_TOKENExample Response:
{
"data": {
"id": "user-id-123",
"userName": "john.doe",
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-20T14:45:00Z",
"lastPasswordChangedAt": "2024-01-15T10:30:00Z",
"lockoutEnd": null,
"isDeleted": false,
"isBlocked": false
}
}The response structure is the same as the list endpoint, but returns a single user object instead of an array.
