Set Up SSO with Okta
OpenID Connect (OIDC) is a simple identity layer on top of the OAuth 2.0 protocol. It allows clients to verify the identity of end users based on the authentication performed by the identity provider, as well as to obtain basic profile information about end users in an interoperable and REST-like manner.
Okta offers a comprehensive suite of identity and access management tools designed to support secure and scalable user authentication, enabling businesses to streamline access control across a variety of applications and devices.
The guide will show you how to integrate APISIX with Okta using authorization code grant and client credentials grant, using the openid-connect
plugin.
Prerequisite(s)
- Install Docker.
- Install cURL to send requests to the services for validation.
- Follow the Getting Started tutorial to start a new APISIX instance in Docker.
- Have an Okta Workflow Identity Cloud account.
Configure Okta
In this section, you will create an app integration and configure the needed details.
Create App Integration
In Okta, find the Applications dropdown from the left menu, select Applications, and click Create App Integration:
Select OIDC - OpenID Connect as the sign-in method and Web Application as the application type:
Click Next. Continue to fill out the details:
- enter an app integration name
- select Authorization Code and Client Credentials for the grant type
- add
http://localhost:9080/anything/callback
to the sign-in redirect URIs
For controlled access, you may skip group assignment for now, or adjust the access accordingly:
Click Save.
Locate Client ID, Secret, and Domain
Once the app is created, find the client ID and secret under the app's General tab:
Locate the Okta domain by clicking your username in the upper-right corner:
Save the client ID, client secret, your Okta domain name, and your authorization server to environment variables for configurations in APISIX:
# replace with your values
export OKTA_CLIENT_ID=0oakxd44nbRLmD6JF5d7
export OKTA_CLIENT_SECRET=h4B_dqp_QnIIzZiDG9MSM_tr4QJJkuEj8CF8iw9jGaBak7m60cArKdsHPf
export OKTA_DOMAIN=dev-02223846.okta.com
export OKTA_AUTH_SERVER=default
Assign User to App
Complete steps in this section only if you are implementing the authorization code flow.
Navigate back to the Applications main interface and click Assign Users to App:
Select the app as well as users to assign, and click Next:
Finish by confirming the assignments:
See configure for authorization code grant for the next step.
Create Custom Scopes
Complete steps in this section only if you are implementing the client credentials flow.
The client credentials flow does not have a user context, so you cannot request openid
scopes. Instead, create a custom scope.
Navigate back to the Applications main interface and click Assign Users to App:
In the left menu, find the Security dropdown from the left menu, select API, and click into your authorization server:
Under the Scopes tab, add a new scope:
Fill out the scope name as well as the display phrase, and finish creating the scope:
Under the Access Policies tab, add a new policy:
Fill out the policy name, description, as well as the client to assign to, and finish creating the policy:
Once created, add a rule for the policy:
Configure the rule details. Make sure the client credentials grant is checked and finish creating the rule:
See configure for client credentials grant for the next step.
Configure APISIX
In this section, you will create a route with OIDC that forwards client requests to httpbin.org, a public HTTP request and response service.
The route /anything/{anything}
of httpbin.org
returns anything passed in request data in JSON type, such as methods, arguments, and headers.
Configure for Authorization Code Grant
The authorization code grant is used by web and mobile applications. The flow starts by authorization server displaying a login page in browser where users could key in their credentials. During the process, a short-lived authorization code is exchanged for an access token, which APISIX stores in browser session cookies and will be sent with every request visiting the upstream resource server.
Create the route and enable the plugin openid-connect
as such:
- Admin API
- ADC
curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '
{
"id": "auth-with-oidc",
"uri":"/anything/*",
"plugins": {
"openid-connect": {
"client_id": "'"$OKTA_CLIENT_ID"'",
"client_secret": "'"$OKTA_CLIENT_SECRET"'",
"discovery": "https://'"$OKTA_DOMAIN"'/.well-known/openid-configuration",
"scope": "openid profile",
"redirect_uri": "http://localhost:9080/anything/callback"
}
},
"upstream":{
"type":"roundrobin",
"nodes":{
"httpbin.org:80":1
}
}
}'
❶ client_id
: Okta client ID.
❷ client_secret
: Okta client secret.
❸ discovery
: URI to Okta discovery document.
❹ redirect_uri
: URI to redirect to after authentication with Okta.
services:
- name: httpbin Service
routes:
- uris:
- /anything/*
name: auth-with-oidc
plugins:
openid-connect:
client_id: '0oakxd44nbRLmD6JF5d7'
client_secret: 'h4B_dqp_QnIIzZiDG9MSM_tr4QJJkuEj8CF8iw9jGaBak7m60cArKdsHPf'
discovery: 'https://dev-xxxxxxxx.okta.com/.well-known/openid-configuration'
scope: openid profile
redirect_uri: 'http://localhost:9080/anything/callback'
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
❶ client_id
: Okta client ID.
❷ client_secret
: Okta client secret.
❸ discovery
: URI to Okta discovery document.
❹ redirect_uri
: URI to redirect to after authentication with Okta.
Synchronize the configuration to APISIX:
adc sync -f adc.yaml
Verify
Navigate to http://localhost:9080/anything/test
in a browser. You should be redirected to the Okta's log-in page:
Log in with your credentials. If successful, the request will be forwarded to httpbin.org
and you should see a response similar to the following:
{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "text/html..."
...
},
"json": null,
"method": "GET",
"origin": "127.0.0.1, 59.71.xxx.xxx",
"url": "http://127.0.0.1/anything/test"
}
Configure for Client Credentials Grant
The client credentials grant involves an machine-to-machine (M2M) application exchanging credentials with services where there is no user involved.
Create the route and enable the plugin openid-connect
as such:
- Admin API
- ADC
curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '
{
"id": "auth-with-oidc",
"uri":"/anything/*",
"plugins": {
"openid-connect": {
"client_id": "'"$OKTA_CLIENT_ID"'",
"client_secret": "'"$OKTA_CLIENT_SECRET"'",
"discovery": "https://'"$OKTA_DOMAIN"'/oauth2/'"$OKTA_AUTH_SERVER"'/.well-known/openid-configuration",
"scope": "openid profile apisix",
"redirect_uri": "http://localhost:9080/anything/callback",
"use_jwks": true
}
},
"upstream":{
"type":"roundrobin",
"nodes":{
"httpbin.org:80":1
}
}
}'
❶ client_id
: Okta client ID.
❷ client_secret
: Okta client secret.
❸ discovery
: URI to Okta authorization server discovery document.
❹ redirect_uri
: URI to redirect to after authentication with Okta.
❺ use_jwks
: Use the JWKS endpoint to verify the token.
services:
- name: httpbin Service
routes:
- uris:
- /anything/*
name: auth-with-oidc
plugins:
openid-connect:
client_id: '0oakxd44nbRLmD6JF5d7'
client_secret: 'h4B_dqp_QnIIzZiDG9MSM_tr4QJJkuEj8CF8iw9jGaBak7m60cArKdsHPf'
discovery: 'https://dev-xxxxxxxx-admin.okta.com/oauth2/default/.well-known/openid-configuration'
scope: openid profile apisix
redirect_uri: 'http://localhost:9080/anything/callback'
use_jwks: true
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
❶ client_id
: Okta client ID.
❷ client_secret
: Okta client secret.
❸ discovery
: URI to Okta authorization server discovery document.
❹ redirect_uri
: URI to redirect to after authentication with Okta.
❺ use_jwks
: Use the JWKS endpoint to verify the token.
Synchronize the configuration to APISIX:
adc sync -f adc.yaml
Obtain Access Token
Before proceeding, first prepare a base64-encoded string of the client ID and client secret, in the format of clientid:clientsecret
, which will be included in the Authorization
header next for basic authentication.
Obtain an access token for the application:
curl "https://dev-02213846-admin.okta.com/oauth2/default/v1/token" -X POST \
-H 'accept: application/json' \
-H 'authorization: Basic MG9ha3hkNDRuYlJMbUQ2SkY1ZDc6cTRCX2RxcF9RbklJefwwJTVNNX3RyNFFKSmttdUVqOEhOM2xzQ0Y4aXc5UEdhQmFmN202MGNBcktkc0hQZg==' \
-H 'cache-control: no-cache' \
-H 'content-type: application/x-www-form-urlencoded' \
--data 'grant_type=client_credentials&scope=apisix'
❶ Replace with your base64-encoded string containing the client ID and client secret.
❷ Adjust the scope accordingly based on your configuration.
You should see a response similar to the following:
{
"token_type":"Bearer",
"expires_in":3600,
"access_token":"eyJraWQiOiJTYko2dGFzUmwtOGVZaFBtQmY4TERXcjYtczJwNG9vRXNZSVZzSGNUUHFNIiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULm1ya1VTd1lyV1VmbHpQcnZoTEV6OXNXMTVSejBvUWtRVjMzSTkzUDVGLWsiLCJpc3MiOiJodHRwczovL2Rldi0wMjIxMzg0Ni5va9vYXV0aDIvZGVmYXVsdCIsImF1ZCI6ImFwaTovL2RlZmF1bHQiLCJpYXQiOjE3MzA5Njc3MTgsImV4Dk3MTMxOCwiY2lkIjoiMG9ha3hkNDRuYlJMbUQ2SkY1ZDciLCJzY3AiOlsiYXBpc2l4Il0sInN1YiI6IjBvYWt4ZDQ0bmJSTG1ENkpGNWQ3In0.PYndg75QRIr4N4kfLXCmlNX3IdzsF-qYYQuYZQPS0QqT21G6AT5eM1idgiEhn6shRKhWkpXLEQTL0mZn6sbgU1S00R5TYHrDzR-tKIhTnoNeJ3aAjduqadoQnQLJk20LQ58NRWb33fN0i9f51NLRs5zQPZbWDNNl1g6s4XE1wirlU_Ol60Gx-cFgbMyrfs4eJW2-q5AS6mtjPz1blS9sOL4KLbWkU0-HkWc-QAv7Dx05Dmat8lT-NvKbIbFq21DlV5BKCL44k55oPrhZSAfkP0494ab8TEsOhCgo8dT0GGYe9219B3rBMDG2aL0jh1OdeZ8coD2haPeA",
"scope":"apisix"
}
Save the access token to an environment variable:
# replace with your access token
export ACCESS_TOKEN="eyJraWQiOiJTYko2dGFzUmwtOGVZaFBtQmY4TERXcjYtczJwNG9vRXNZSVZzSGNUUHFNIiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULm1ya1VTd1lyV1VmbHpQcnZoTEV6OXNXMTVSejBvUWtRVjMzSTkzUDVGLWsiLCJpc3MiOiJodHRwczovL2Rldi0wMjIxMzg0Ni5va9vYXV0aDIvZGVmYXVsdCIsImF1ZCI6ImFwaTovL2RlZmF1bHQiLCJpYXQiOjE3MzA5Njc3MTgsImV4Dk3MTMxOCwiY2lkIjoiMG9ha3hkNDRuYlJMbUQ2SkY1ZDciLCJzY3AiOlsiYXBpc2l4Il0sInN1YiI6IjBvYWt4ZDQ0bmJSTG1ENkpGNWQ3In0.PYndg75QRIr4N4kfLXCmlNX3IdzsF-qYYQuYZQPS0QqT21G6AT5eM1idgiEhn6shRKhWkpXLEQTL0mZn6sbgU1S00R5TYHrDzR-tKIhTnoNeJ3aAjduqadoQnQLJk20LQ58NRWb33fN0i9f51NLRs5zQPZbWDNNl1g6s4XE1wirlU_Ol60Gx-cFgbMyrfs4eJW2-q5AS6mtjPz1blS9sOL4KLbWkU0-HkWc-QAv7Dx05Dmat8lT-NvKbIbFq21DlV5BKCL44k55oPrhZSAfkP0494ab8TEsOhCgo8dT0GGYe9219B3rBMDG2aL0jh1OdeZ8coD2haPeA"
Verify
Send a request to the route with the valid access token:
curl -i "http://127.0.0.1:9080/anything/test" -H "Authorization: Bearer $ACCESS_TOKEN"
You should receive an HTTP/1.1 200 OK
response.
Send a request to the route with an invalid access token:
curl -i "http://127.0.0.1:9080/anything/test" -H "Authorization: Bearer invalid_token"
You should receive an HTTP/1.1 401 Unauthorized
response.
Next Steps
APISIX supports the integration with many other OIDC identity providers, such as Keycloak, Authgear, Microsoft Entra ID (Azure AD), and Google.
In addition, APISIX also supports built-in authentication approaches such as key authentication, basic authentication, and JWT.