Usage
In this section, we show you how to use the authentication-service
.
Login Flow
The service handle the authorization_code
OAuth2 grant type.
Here is how to login with this flow.
Authorize
API Signature: GET /authorize
The /authorize
endpoint is useful for the login flow with grant type authorization_code
.
The authorization endpoint accepts the following query parameters:
- appId: the
APP_ID
key from the configurations file - providerId: the
PROVIDER_ID
key from the configuration file, under the correspondingAPP_ID
An example curl:
curl --request GET 'https://app.example.com/authorize?appId={{APP_ID}}&providerId={{PROVIDER_ID}}'
When called, the authentication service redirects to the IdP's authUrl endpoint, which in turn is expected to redirect to the redirectUrl specified in the configurations file with the query string parameters code and state.
Example of redirect URL:
https://app.example.com/callback?code={{CODE}}&state={{STATE}}
Your application should then implement the logic to call the POST /oauth/token with authorization_code grant type with the information passed in the above query string, as per the Authorization Code Grant OAuth2 flow.
In case you are using a web app this request can be urlmade directly from the browser; in this case the user will be redirected to an external login page, and then, back to the custom callback URI when the login succeeds.
Redirect url parameter
You can pass a redirect url to the request, by means of the redirect
query string parameter. After a successful login, the /oauth/token
endpoint will set the Location
header to the specified url.
If a request comes with no redirect
query parameter, the Location
header will be set to the redirectUrlOnSuccessfullLogin
app setting parameter, if specified.
In all other cases the Location
header will not be set.
The response status code of a successful request is always 200 OK
, which means that browsers will ignore by default the Location
header. It is responsibility of the client implementation to use the redirect url when needed.
It is strongly recommended to set the allowedRedirectUrlsOnSuccessfulLogin
parameter (available from v2.8.0) on the app configuration, in order to avoid the Open Redirector vulnerability.
If you set this parameter, the service will refuse to accept any redirect url not present in the set. This prevents an attacker client to set malicious redirect URLs. Any request providing a redirect URL not included in the set will result in a 400 Bad Request
, and the token will not be issued.
This parameter may become required in the next releases of the service.
Client generated state
This feature is turned off by default for retrocompatibility reasons, but its usage is strongly encouraged.
To enable this feature, you need to set the config parameter variable authorizeStateRequired
to true
, in each app where you need it.
To achieve a better level of security, the client should take charge of the process of the state generation. This enables protection against CSRF attacks, as documented here.
The client must follow these steps:
Generate a random state string. The randomly generated string should be sufficiently hard to guess, as described here. For instance, an UUID v4 is compliant with this specification.
Save the state on the client application side (for example in local storage), making sure the location where it is saved it is only accessible by the client.
Call the
/authorize
endpoint, passing the state in the query string, like in the following example:curl --request GET 'https://app.example.com/authorize?appId={{APP_ID}}&providerId={{PROVIDER_ID}}&state=<some-hard-to-guess-string>'
Once the redirect URL is called, the client must check that the state in the redirect URL querystring matches the one it generated at the first step. If it doesn't, the client must refuse to proceed in the token request.
If the state validation check succeeds, the client can now obtain a token by calling the
/oauth/token
endpoint, as described in the related section.
Get JWT Token
API Signature: POST /oauth/token
This endpoint is used to obtain the token, that the user will then use to authenticate to the endpoints.
Authorization code grant type
The body of the request, in JSON format, is composed of the following fields:
{
"code": "{{CODE}}",
"state": "{{STATE}}"
}
These fields are usually retrieved from the redirect url query string.
The endpoint will return a JSON object with accessToken, refreshToken and expiresAt.
{
"accessToken": "my-access-token",
"refreshToken": "my-refresh-token",
"expireAt": 1234567890
}
Web site cookie configuration
If you are using a website client, you can set the isWebsiteApp
field to true
in the service configuration; this way, the token API will return a session cookie named sid.
The sid cookie has the following default configuration:
* HttpOnly: `true`
* Secure: `true`
* Path: `/`
* SameSite: `Lax`
You can further customize the cookie response by adding the sidCookieCustomAttributes
to the service configuration, at application level.
In particular, you can change the SameSite
attribute to Strict
, and add a Domain
attribute with a value of your choice.
Here is an example snippet of the configuration:
...
"isWebsiteApp": true,
"sidCookieCustomAttributes": {
"sameSite": "Strict",
"domain": "example.com"
}
...
Refer to this documentation for a thorough explanation of the mentioned cookie attributes.
For security reasons, you cannot change the other attributes of the cookie, and you cannot set the SameSite
attribute to None
.
The JWT Token
The authentication service generates a JWT Token compliant with the RFC 7519 specification.
In particular, the service populates the standard claims:
exp
with the expiration date as unix timestampiat
with the issuance date as unix timestampsub
with theuserId
, which is the Mongo Id of the user CRUD documentjti
with a random generated UUIDv4iss
with theissuer
specified in the current application config
Along with the above mentioned standard claims, the token contains a custom claim named user
, which is an object with the following structure:
{
"properties": {
"userId": {
"type": "string"
},
"groups": {
"type": "array",
"items": { "type": "string" }
},
"email": {
"type": "string"
},
"name": {
"type": "string"
},
"userSettingsURL": {
"type": "string"
},
"providerUserId": {
"type": "string"
},
"metadata": {
"type": "object",
"additionalProperties": true
}
}
}
The data are populated from the user CRUD collection. The providerUserId
and the metadata
fields are included and populated only if configured, as explained here.
Refresh token
API Signature: POST /refreshtoken
The endpoint POST /refreshtoken
allows to issue a new token, by using the refreshToken
received in the Token request.
The endpoint needs to be called with a POST, like in the following example:
curl --location --request POST 'https://app.example.com/refreshtoken' \
--header 'Content-Type: application/json' \
--data-raw '{
"refreshToken": {{refreshToken}}
}'
Under the hood, the authentication service will call the provider refresh token endpoint, and then it will refresh its token.
The response is the same as the /oauth/token
:
{
"accessToken": "my-access-token",
"refreshToken": "my-refresh-token",
"expireAt": 1234567890
}
Skip provider refresh token
If in your provider you set the skipProviderRefreshToken
option, the authentication service will validate the provider refresh token by calling the provider userinfo before issuing the refresh token request.
The refresh token on the provider will only be issued if the validation fails.
User Info
API Signature: GET /userinfo
The /userinfo
endpoint returns the contents of the user
claim of the JWT Token.
See the related section for more information on how the JWT Token is built and how you can personalize it to achieve a customized userinfo response.
You can contact the endpoint in this way:
curl --location --request GET 'https://app.example.com/userinfo' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <access_token>'
Custom claims
If you need to have additional custom claims in the user
claim of the token and, as a consequence, in the userinfo response, you have to include the customTokenClaims
object in the app configuration.
The object contains the following fields:
includeProviderUserId
, if set totrue
theproviderUserId
will be included in theuser
claim, and populated with the user id of the provider. The default behavior is not to include the field.metadataFieldsToInclude
, an array of strings. If it contains at least one element, ametadata
object is added to theuser
claim and populated with the specified fields taken from the correspondingmetadata
object of the CRUD user collection.
As an example, consider a user collection like the following:
{
"_id": "some-mongo-id",
"name": "John Doe",
"groups": ["someGroup"],
"username": "someUsername",
"email": "johndoe@example.com",
"providerId": "someProviderId",
"providerUserId": "some-id",
"metadata": {
"firstName": "John",
"surname": "Doe",
"address": {
"city": "Milan",
"country": "Italy"
}
}
}
The authentication service can be configured as follows:
{
"apps": {
"APP_ID": {
"providers": {
"PROVIDER_ID": {
...
}
},
...
"customTokenClaims": {
"includeProviderUserId": true,
"metadataFieldsToInclude": [
"surname", "address"
]
}
}
}
}
The configuration snippet above will result in the following JWT token claims:
{
"user": {
"userId": "some-mongo-id",
"groups": [
"someGroup"
],
"email": "johndoe@example.com",
"name": "John Doe",
"providerUserId": "some-id",
"metadata": {
"surname": "Doe",
"address": {
"city": "Milan"
}
}
},
"exp": 123456789,
"jti": "some-jti",
"iat": 123456789,
"iss": "some-iss",
"sub": "some-mongo-id"
}
Integration with the Authorization Service
The /userinfo
endpoint can be used by the Authorization Service to determine whether the requested resource can be accessed by the current user, based on the contents of the groups
array.
You need to set the following variables in the configuration with these values:
- USERINFO_URL=
http://authentication-service/userinfo
(NB: change the hostname if it has been named differently) - CUSTOM_USER_ID_KEY=
userId
- HEADERS_TO_PROXY=
x-request-id,request-id,cookie,authorization,client-type,host,x-forwarded-host
Custom properties to proxy
If you have configured some custom claims in the authentication service, you can also add them to the authorization service Custom properties to proxy.
Just add the name of the fields as a comma-separated list to the following authorization service environment variable:
USER_PROPERTIES_TO_PROXY=metadata,email,name,providerUserId
This way, a JSON string will be set to an header with name miauserproperties
, populated with the metadata field of the userinfo response.
Referring to the example above, the header will have the following contents:
{
"email": "johndoe@example.com",
"name": "John Doe",
"providerUserId": "some-id",
"metadata": {
"surname": "Doe",
"address": {
"city": "Milan"
}
}
}
Token Info
API Signature: GET /tokeninfo
This endpoint returns potentially confidential information, that a user may not be supposed to have; therefore, depending on your specific use case, you probably don't want to expose it publicly.
This endpoint has the same response of the userinfo, with in addition a provider
field, populated with the following fields:
- accessToken: the access token obtained from the IdP.
- id: the provider id of the currently used configuration
- baseUrl: the OAuth2 base url of the provider, as set in the configuration
- userId: the provider id of the current user
An example of response:
{
"userId": "63cacfa530a2a89a7f057dce",
"groups": [
"testgroup"
],
"email": "some@email.com",
"name": "John Doe",
"provider": {
"accessToken": "provider-token",
"id": "provider-id",
"baseUrl": "provider base url",
"userId": "provider-user-id"
}
}
You can use the /tokeninfo
endpoint as USERINFO_URL
in the authorization service. This is useful if you want the information included in the provider
key to be added to the miauserproperties
header.
Revoke sessions
This API is intended to be used by a privileged user (e.g. an administrator or a customer service operator) or application. Make sure to expose it with a proper authorization config.
API Signature: DELETE /sessions
This is an admin feature that revokes all sessions of the specified user. This is useful when the user is being blocked or deleted on the provider side.
You can contact the endpoint this way:
curl --location --request DELETE 'https://app.example.com/sessions/:userId' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <access_token>'
The endpoint in case of success will reply with status code 200 and the number of deleted elements. An example of response:
{
"count": 2
}
Cleanup all users queue
This API is intended to be used by a privileged user (e.g. an administrator or a customer service operator) or application (e.g. a cronjob). Make sure to expose it with a proper authorization config.
API Signature: DELETE-/expired-sessions
This API is helpful to clean up all the expired sessions of all the users.
This is especially useful when the STORED_ACCESS_TOKEN_NUMBER environment variable is a big number, in order to keep stored data in Redis to the minimum.
It's recommended to setup a CronJob that periodically invokes this API; the period can be tuned with respect to the queue length and to other parameters that depend on your use case, such as the number of logins per unit of time.
The operation is deliberately non-atomic, as it triggers a potentially long and CPU-intensive task that could block Redis.
In case of success, the endpoint will return a 204 No Content response.
Cleanup a specific users queue
The endpoint can receive also as a query params a specific userID. In this case, only the session of the specified user are cleaned up.
Here is an example:
curl --location --request DELETE 'https://app.example.com/expired-sessions/:userId`' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <access_token>'
Webhooks
When a new User is activated on the ID Provider, the UserInfo collection can be synchronized through this webhook. (Currently only Okta provider is supported)
Pay careful attention when configuring the webhook as the endpoint must be properly protected as it may allow anyone to create new user records.
The webhook is made of 2 endpoints:
- GET
/webhook/apps/:appId/providers/:providerId/user
used to validate the hook by the OID Provider - POST
/webhook/apps/:appId/providers/:providerId/user
used to handle the user activation
Validate the Webhook (Okta)
In order to validate the webhook, Okta sends a GET request on the webhook url with a specific header x-okta-verification-challenge
and expects to receive in response the same value of the header.
Security Configuration
In order to secure these endpoints from unwanted requests (that will end up in users being added to the CRUD collection), you need to secure the Endpoint (for instance using an API Key). Remember to keep the API Key secure and to share it only with who is going to consume the webhook!