Table of Contents
This document is a tutorial to describe how to integrate an Authlete backed authorization server (AS) with external identity providers (IdPs) so that you can build API authorization infrastructure with highly secure industry standards like Financial-grade API (FAPI) while offloading user authentication and management functions to such IdPs.
This integration would be beneficial for service providers that have already built a repository of user credentials and information using IDaaS (Identity as a Service), and been required to implement FAPI-based security provisions defined by ecosystems such as UK Open Banking, Australian Consumer Data Right and Open Banking Brasil.
In this tutorial, you can see an integration example using Okta as an OpenID Connect (OIDC) IdP and java-oauth-server as an AS backed with Authlete.
These accounts would be needed before you begin this tutorial:
You also need to have an environment where you can install and run java-oauth-server.
How an AS authenticates end-users is intentionally not defined in the specifications of OAuth 2.0 (RFC 6749) and OIDC (OpenID Connect Core 1.0). Therefore, AS implementations may choose to delegate end-user authentication to an external OIDC IdP, and obtain information about the user from the IdP. The diagram below illustrates the flow of identity federation between an AS and an external OIDC IdP.
The following is explanation about the flow in the diagram in words.
302 Found
(or whatever triggers redirection) with the Location
header to the web browser in order to make the web browser send the authentication request to the authorization endpoint of the OP.302 Found
(or whatever triggers redirection) with the Location
header to the web browser in order to make the web browser access the redirection URI of the AS (not of the Client) with the authorization code. From a viewpoint of the OP, the AS is a client application and the redirection URI is one among those the AS has registered to the OP in advance.302 Found
(or whatever triggers redirection) with the Location
header to the web browser in order to make the web browser access the redirection URI of the Client (not of the AS) with the authorization code. This assumes that the Client has registered at least one redirection URI to the AS in advance.The next sections will introduce how to configure Okta as an IdP and integrate it with an Authlete-backed AS (java-oauth-server) to enable the flow shown above.
You need to create a new “App Integration” in the server which is hosted on Okta so that an AS can act as a client application of the OIDC IdP.
The table below shows points for App Integration settings.
Item | Value |
---|---|
Sign-in method | OIDC - OpenID Connect |
Application Type | Web Application |
Sign-in redirect URIs | http://localhost:8080/api/federation/callback/okta |
Controlled access | Allow everyone in your organization to access |
You may have noticed that the value of “Sign-in redirect URIs” implies that this tutorial will later instruct you to run an AS on your local machine at the port number 8080 and that the AS provides /api/federation/callback/okta
API.
Images below are screenshots of App Integration settings.
After you create the App Integration, Okta issues a pair of Client ID and Client secret. The pair will be used in the next section.
In this tutorial, we use java-oauth-server as an AS. It is an open-source sample implementation of AS that uses Authlete as backend.
First, download java-oauth-server and edit authlete.properties
. You need to change the values of service.api_key
and service.api_secret
in the file at least.
$ git clone https://github.com/authlete/java-oauth-server
$ cd java-oauth-server
$ vi authlete.properties
Second, open federations.json
with your text editor. This file describes configurations of identity federations. The initial content looks like below. Replace YOUR_COMPANY
, YOUR_CLIENT_ID
and YOUR_CLIENT_SECRET
with actual values which have been assigned to you from Okta. See Appendix/federations.json for details about federations.json
.
{
"federations": [
{
"id": "okta",
"server": {
"name": "Okta-hosted IdP",
"issuer": "https://YOUR_COMPANY.okta.com"
},
"client": {
"clientId": "YOUR_CLIENT_ID",
"clientSecret": "YOUR_CLIENT_SECRET",
"redirectUri": "http://localhost:8080/api/federation/callback/okta",
"idTokenSignedResponseAlg": "RS256"
}
}
]
}
The above are all the necessary configuration. Start java-oauth-server on your local machine.
$ docker-compose up
# or mvn jetty:run
If you confirm logs like below, java-oauth-server is ready to work.
app_1 | [INFO] Started ServerConnector@7c5ae5c3{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
app_1 | [INFO] Started @41702ms
app_1 | [INFO] Started Jetty Server
In the diagram shown in the “Identity federation between AS and IdP” section, the client uses the OIDC authorization code flow (response_type=code
). But here, to quickly confirm that identity federation with Okta works, we use the OIDC implicit flow which issues an ID token only (response_type=id_token
).
The URL below is an authorization request. Replace CLIENT_ID
with a client ID issued from Authlete (not from Okta) and SERVICE_API_KEY
with a service API key issued from Authlete, and then copy and paste the URL to the address bar of your web browser.
http://localhost:8080/api/authorization?client_id=CLIENT_ID&response_type=id_token&scope=address+email+openid+phone+profile&redirect_uri=https://api.authlete.com/api/mock/redirection/SERVICE_API_KEY&nonce=mynonce&state=mystate&prompt=login
You’ll see an authorization page like below. Click the link “Okta-hosted IdP” which is located over the “Authorize” button.
The web browser will display the authorization page like below which was generated by Okta. Input your login ID and password which you use to login the server hosted on Okta.
The web browser will re-display the original authorization page generated by java-oauth-server. This time, java-oauth-server regards that you have logged in. Press the “Authorize” button.
java-oauth-server will issue an ID token and redirect the web browser to the redirection endpoint. The mock implementation of the redirection endpoint hosted on an Authlete server (/api/mock/redirection/SERVICE_API_KEY
) displays parameters it receives.
When a request includes an unencrypted ID token, the redirection endpoint decodes the payload part of the ID token and shows it like below.
The table below indicates data sources of claims in the issued ID token. All the user attributes originate from Okta.
Claim | Data Source |
---|---|
email |
Okta |
email_verified |
Okta |
family_name |
Okta |
given_name |
Okta |
locale |
Okta |
name |
Okta |
phone_number_verified |
Okta |
preferred_username |
Okta |
updated_at |
Okta |
zoneinfo |
Okta |
iss |
Authlete / Service configuration, issuer |
sub |
java-oauth-server / subject parameter passed to Authlete’s /api/auth/authorization/issue API |
aud |
Authlete / Client configuration, clientId (unmodifiable) |
exp |
Authlete / expiration date of the ID token |
iat |
Authlete / issue date of the ID token |
auth_time |
java-oauth-server / authTime parameter passed to Authlete’s /api/auth/authorization/issue API |
nonce |
Client / the value of the nonce request parameter |
s_hash |
Authlete / computed based on the state request parameter |
You may wonder where the value of the sub
claim ("00ucabhbjLVphPrXF696@okta"
in the example) originates from. It is just a result of concatenating the value of the sub
claim returned from Okta with the string "@okta"
. The concatenation is conducted in createUserEntity()
method in FederationEndpoint.java. There is no big meaning there and it is up to you how to determine the value of the sub
claim.
In this tutorial, we were able to confirm an integration pattern between an Authlete backed authorization server and an external identity provider, and a real walk-through using java-oauth-server and Okta.
You can learn implementation-level details about identity federation by looking into Federation.java and FederationEndpoint.java of java-oauth-server.
After receiving user information from the userinfo endpoint of an OIDC IdP, the current implementation of java-oauth-server registers a user record into its on-memory user database. However, commercial implementations may choose other approaches.
For example, an implementation may avoid copying user data from an OIDC IdP into its database and instead just remember the access token issued from the IdP so that the implementation can retrieve up-to-date user information from the userinfo endpoint of the IdP at any time it wants to.
To implement such behavior, the AS may prepare a database to remember access tokens issued from IdPs. Another possible approach is to use Authlete’s Extra Properties feature with which an AS can associate arbitrary key-value pairs with an access token and make Authlete remember IdP-issued access tokens. (cf. “How to add extra properties to an access token”)
federations.json
is a configuration file that describes configurations of identity federations. java-oauth-server loads the file at the first time it receives an authorization request. Identity federations listed in the file will appear in the authorization page of java-oauth-server.
The default location of the file is "federations.json"
. The environment variable FEDERATIONS_FILE
and the system property federations.file
can be used to specify a different location of the file.
The content of federations.json
must be a JSON object which has "federations"
as a top-level property like below.
{
"federations": [
"(configurations of identity federations)"
]
}
Each entry in the "federations"
array represents a configuration of identity federation and must be a JSON object which has "id"
, "server"
and "client"
as top-level properties.
{
"federations": [
{
"id": "(unique identifier among the configurations)",
"server": {
"(server configuration)"
},
"client": {
"(client configuration)"
}
}
]
}
The value of "id"
is used as federationId
in the following API paths of java-oauth-server.
/api/federation/initiation/federationId
/api/federation/callback/federationId
"server"
describes the configuration of the target OpenID Provider. Its value is a JSON object which has properties shown in the table below.
Property | Description |
---|---|
name |
The display name of the OpenID Provider. This is used in the authorization page. |
issuer |
The issuer identifier of the OpenID Provider. The value must match the value of "issuer" in the discovery document of the OpenID Provider. The OpenID Provider must expose its discovery document at {issuer}/.well-known/openid-configuration so that identity federation of java-oauth-server can work. |
"client"
describes the configuration of the Relying Party, which is always java-oauth-server. Note that java-oauth-server is a client application from a viewpoint of external OpenID Providers. The value of "client"
is a JSON object which has properties shown in the table below.
Property | Description |
---|---|
clientId |
Client ID issued by the OpenID Provider. |
clientSecret |
Client secret issued by the OpenID Provider. If this property is set, token requests sent to the token endpoint of the OpenID Provider will include an Authorization header for client authentication. This behavior assumes that the token endpoint supports client_secret_basic as a method of client authentication. |
redirectUri |
A redirect URI that you have registered into the OpenID Provider. |
idTokenSignedResponseAlg |
The algorithm the OpenID Provider uses when it signs ID tokens. If this property is omitted, "RS256" is used as the default value. |