OAuth2 Identity Service
accesso provides an OIDC and OAuth2.1-compliant service capable of authenticating users via stored passwords or through social or federated identity with other upstream OIDC providers. Upon successful authentication, the endpoint returns a JSON Web Token (JWT) "access_token", which may then be used for making calls to accesso's APIs.
Standard Conventions
Our implementation of authentication is based on IETF standards and includes support for the following RFCs:
- RFC 6749 - The OAuth 2.0 Authorization Framework
- RFC 6750 - The OAuth 2.0 Authorization Framework: Bearer Token Usage
- RFC 7519 - JSON Web Token (JWT)
- RFC 7523 - JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants
- RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients
- OpenID Connect Core 1.0
- OpenID Connect Discovery 1.0
Our efforts for standard compliance are intended to ensure that client applications can reuse standard libraries for authentication flows. We provide full support for the OIDC Discovery protocol so that clients can acquire most of their configuration details directly from our service. E.g. see https://api.us.te2.io/.well-known/openid-configuration
We have also designed our services to enable capabilities like federated and social authentication in a way that is fully encapsulated in our service configuration so that client applications can use the standard authentication flow regardless of whether social authentication is enabled or not. It’s possible to change to an alternative upstream authenticator at a later time without affecting clients using these APIs.
accesso’s access_tokens are in the form of JSON Web Tokens, or JWT.
Learn more about JWTs and list libraries for working with JWTs. refresh_tokens
are obscure byte sequences and are not valid JWTs.
OAuth2 / OIDC Authentication APIs
accesso’s Authentication API is an OAuth2 and OIDC-compliant set of endpoints which enables an authorized user to acquire an access_token
needed for accesso’s APIs.
The Authorization Service supports the following authentication use cases:
- Sign-in: Allowing a user to sign in and acquire access_token via the standard ‘authorization_code’ grant type of OAuth2.1. The login page is hosted by accesso, and can be fully customized to match the look and feel of your application.
- Federated Sign-in: Allowing a user to sign and acquire an access_token via the use of an upstream OIDC-compliant system. This federated approach allows an external system you may already be using to authenticate the end user, but issues a standard access_token good for calls to accesso’s apis.
- Anonymous Users: Allowing a user on a personal device to acquire an access token so that they can then interact with the system as an anonymous user. Their anonymous identity can be retained over time by remembering the refresh_token on that device. The user identity representes “the anonymous person using that specific device” to interact with the system.
- Token Exchange: Allowing an idtoken which has already been acquired from an upstream federated provider to be exchanged for an access_token.
- Client Authentication: Allowing an external system to authenticate as a client, based on its own credentials, for system integration use cases.
The Authorization Service supports the following OAuth2.1 grant types (and extensions):
authorization_code
- for sign-in and social and federated sign-in use cases.urn:accesso:oauth2:grant-type:anonymous
- for anonymous user use cases.urn:ietf:params:oauth:grant-type:jwt-bearer
- for token exchange use cases.client_credentials
withclient_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
- for system integration use cases.refresh_token
- for refreshing an access_token previously granted.
On success, the OAuth2 token endpoint will return a json object which will contain an access_token in JSON Web Token (JWT) format. A refresh_token will also be returned, and (if requested) an idtoken
can also be returned per the OIDC standard. All requests to the token endpoint are URL encoded (application/x-www-form-urlencoded) per the OAuth2 standard.
Regardless of the type of authentication, a successful authentication results in an HTTP response status of 200 and the following response payload:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"access_token": "eyJhbGciOiJSUz...",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJSUzI1Ni...",
"expires_in": 86399,
"scope": "authenticated openid services:all",
"account_id": "user@yourcompany.com",
"user_type": "Guests",
"venueId": null,
"organization": "your-company",
"amr": [
{
"authority": "credential"
}
],
"iss": "https://www.theexperienceengine.com/",
"phone_number": null,
"source": "UNKNOWN",
"iat": 1576075405,
"email": "user@yourcompany.com",
"uuid": "404589da-7a39-4e9c-8697-ce05fb57449e",
"jti": "ec2488b7-f364-437c-a640-204670b9c44d"
}
Our response is based on the OAuth2.1 standard but includes additional fields in the JSON object response.
Notable fields above include:
- access_token: access_token as a JWT. Provided to accesso endpoints via the
Authorization
header. - refresh_token: Refresh token. Used to acquire a new access token once the issued one expires.
- uuid: ID of the user in the accesso ecosystem.
- iat: UTC timestamp, in seconds, at which the access and refresh tokens were issued.
- expires_in: Number of seconds before the access token expires. The access token is no longer valid at time
iat + expires_in
.
Authentication Scenarios
End-user grant_types
Sign-in via OAuth2 login screen
For end users, the standard grant_type=authorization_code
flow is supported, conforming to the OAuth2.1 specification. The authorization service serves a login screen and (optional) consent screen which can be branded via configuration to use any html/css desired. In summary, this process works as follows:
- The client application pops a webview or otherwise redirects the user’s browser to the authorization service’s login screen, passing the following parameters:
response_type=code
client_id=your-client-id
redirect_uri=your-redirect-uri
scope=openid services:all
state=your-state
- The user enters their credentials and submits the form.
- The authorization service validates the credentials and redirects the user back to the redirect_uri, passing the following parameters:
code=your-code
state=your-state
- The client application exchanges the code for an access_token by calling the token endpoint, passing the following parameters:
grant_type=authorization_code
code=your-code
redirect_uri=your-redirect-uri
client_id=your-client-id
client_secret=your-client-secret
The login screen served can also be configured to present social authentication options based on any desired OIDC-compliant social providers such as Google, Facebook, etc. Regardless of whether the user signs in using credentials managed by our service or via an upstream social provider, the service will return an authorization code which can then be exchanged for an access_token.
A standard OAuth2 display of the login screen is done via the https://api.{region}.te2.io/oauth2/authorize
endpoint. For example:
1
2
3
4
5
6
7
GET /oauth2/authorize?response_type=code\
&client_id={your client id}\
&scope={scopes desired}\
&state={any optional state you want n the redirect URL}\
&redirect_uri=https://{yourapplication.com/yourpath}\
&nonce={optional nonce}\
HTTP/1.1
The initial display of the login screen requires the client_id be passed as a query parameter, but the client_secret does not need to be passed. The client_secret is only required during step 4 of OAuth2 interaction when the client exchanges the authorization code for an access_token. That can be done by a server-side component of the client application thus ensuring the secret is never exposed in a guest’s browser. This approach is recommended, as it keeps the client’s credentials secure.
Once an authorization code is acquired, the OAuth2 /token endpoint can be called to exchange the code for an access_token. For example:
1
2
3
4
5
6
curl --location --request POST 'https://api.{region}.te2.io/oauth2/token' \
--header 'accept: application/json' \
--header 'content-type: application/x-www-form-urlencoded' \
--header 'Authorization: Basic {client_id:client_secret}' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'code={authorization_code received via redirect}' \
Our service also supports the PKCE standard and recommend its use as a way to further ensure that the authorization code cannot be intercepted and used by a malicious actor.
Federated Sign-in via external OIDC-compliant service
Federated authorization is also supported in which our service delegates to an upstream OIDC-compliant service.
This also works using the standard grant_type=authorization_code
flow of OAuth2, with the login screen and authentication
being handled by the upstream service. Once that is complete, our service issues
an authorization_code based on that successful upstream authentication, and the rest of the normal OAuth2 flow applies.
Thus federated (or social) login scenarios proceed exactly as with the standard authorization_code
OAuth2 grant just described. Ultimately, a standard accesso
access_token is issued, but the user’s credentials are confirmed by the upstream service.
One other difference with this scenario is that the access_token that is acquired from the upstream service is embedded into the acccesso access_token as a JWT claim. This allows the application to access the upstream token if it is needed for any reason. For example, to call an API in external services which only accepts access_tokens from that upstream authorization service.
Federated Sign-in via token exchange
If the federated authentication flow is appropriate, but the user has already signed into the upstream service,
then it would not be ideal to send them back through that flow to get an accesso access_token. As an alternative to doing so,
a grant_type of grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
is supported which allows a client
to exchange an idtoken
acquired from the upstream service for an accesso access_token.
An example of the api call to acquire a token via this mechanism:
1
2
3
4
5
6
7
curl --location --request POST 'https://api.{region}.te2.io/oauth2/token' \
--header 'accept: application/json' \
--header 'content-type: application/x-www-form-urlencoded' \
--header 'Authorization: Basic {client_id:client_secret}' \
--data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer' \
--data-urlencode 'assertion={your id_token}' \
--data-urlencode 'scope={desired scopes}'
Parameters used above include:
- region: Your region. This is
us
orap
. If you are unsure which region you are in, contact your assigned Client Engagement Manager. - client_id / client_secret: The credentials which identifies a particular application or program using the accesso APIs. This information should be provided to you.
Required properties
- grant_type - Must be set to “urn:ietf:params:oauth:grant-type:jwt-bearer”
- assertion - The base64 encoded JWT idtoken acquired from the upstream service
Optional properties
- scope - Optional. If no scope is requested, the resource server will use the default scope configured for the client_id along with the ‘openid’ scope.
Required headers
- Authorization - Basic authorization with the client_id / client_secret provided to the customer
- Content-Type - application/x-www-form-urlencoded or application/json are supported
Anonymous Sign-in
For end users who do not wish to sign-up / sign-in, but who are working with a personal device (e.g. mobile or confirmed personal computer) an extension of the OAuth2.1 specification is supported: grant-type=urn:accesso:oauth2:grant-type:anonymous
. This allows a client-facing application
to request an access_token representing the unidentified guest user of the system.
When an anonymous token is requested, a new user is inserted into the database.
The ID of the user is then returned along with a valid access token and valid refresh token.
These can be stored on the device to “remember”
that specific anonymous user so that over time,
the anonymous user can continue to interact with the system as the same individual,
and their activity will continue to be associated with that specific user profile.
An example anonymous sign-in request:
1
2
3
4
5
6
curl --location --request POST 'https://api.us.te2.io/oauth2/token' \
--header 'accept: application/json' \
--header 'content-type: application/x-www-form-urlencoded' \
--header 'Authorization: Basic {client_id:client_secret}' \
--data-urlencode 'grant_type=urn:accesso:oauth2:grant-type:anonymous'
--data-urlencode 'scope={desired scopes and may include openid}'
Parameters used above include:
- region: Your region. This is
us
orap
. If you are unsure which region you are in, contact your assigned Client Engagement Manager. - client_id / client_secret: The credentials which identifies a particular application or program using the accesso APIs. This information should be provided to you.
Required properties
- grant_type - Must be set to “urn:accesso:oauth2:grant-type:anonymous”
Optional properties
- scope - Can be set to anonymous or omitted altogether. If no scope is requested, the resource server will use the default scope configured for the client_id along with the ‘openid’ scope.
Required headers
- Authorization - Basic authorization with the client_id / client_secret provided to the customer
- Content-Type - application/x-www-form-urlencoded or application/json are supported
Refresh
Access tokens issued by accesso expire after 24 hours. These tokens can be reissued, or refreshed, at any time by using the refresh token which was issued alongside the access token. Refreshing will issue both a new access token as well as a new refresh token.
1
2
3
4
5
6
curl --location --request POST 'https://api.{region}.te2.io/v4/tokens' \
--header 'accept: application/json' \
--header 'content-type: application/x-www-form-urlencoded' \
--header 'Authorization: Basic {apiKey}' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token={credential}'
Parameters used above include:
- region: Your region. This is
us
orap
. If you are unsure which region you are in, contact your assigned Client Engagement Manager. - apiKey: The API key which identifies a particular application or program using the accesso APIs. This information should be provided to you.
- credential: Refresh token.
Required properties
- grant_type - Must be set to “refresh_token”
- refresh_token - The previously issued refresh token. The accesso token response issues a refresh token in JWT format. The refresh token itself contains the scopes granted.
Required headers
- Authorization - Basic authorization with the user being the customer
- Content-Type - application/x-www-form-urlencoded or application/json are supported
System Integration grant_types
For system-to-system integration use cases, the standard grant_type=client_credentials
flow is supported, conforming to the OAuth2.1 specification specification.
The access_tokens which are issued for client_credentials are not associated with any specific end users of the system, and can (with the right scopes) give the associated application access to any data in the system. To help ensure that the client_credentials used in this way are harder to compromise, accesso has standardized on the use of JWT tokens for client authentication, as explained in https://datatracker.ietf.org/doc/html/rfc7523#section-2.2. This ensures that the client ‘secret’ can be a full RS256 private key which is never transmitted over the network. It is instead used to sign a JWT which is sent as the credential for the token request.
An example of the api call to acquire a token via this mechanism:
1
2
3
4
5
6
7
8
curl --location --request POST 'https://api.{region}.te2.io/oauth2/token' \
--header 'accept: application/json' \
--header 'content-type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'scope={desired scopes as space delimited list}' \
--data-urlencode 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
--data-urlencode 'client_assertion={Ecoded JWT Token}' \
--data-urlencode 'client_id=happyland'
For more details on the construction of the JWT token, and this form of credential in general, see OAuth2 Service tokens guide.
Our implementation can also perform client_credentials grants based on credentials passed via basic auth headers, but this is not recommended for production use. Use of that model for production systems will be evaluated and approved on a case-by-case basis. Approval is typically based restricting the maximum scope of access which such credentials would provide if they were compromised, and the presence of other security measures which could prevent their misuse.
Best practices
Do:
- reuse anonymous tokens when possible. For example, if you acquired an anonymous refresh token prior to a user logging in, use that anonymous refresh token to fetch a new access token when the user logs out.
Don’t:
-
fetch an anonymous token unless absolutely necessary.
-
wait until an access token expires to refresh. Before calling an accesso endpoint, check the expiration of the access token. If the token is within one minute of expiration, refresh the token.