OAuth
Require users to authenticate with an OpenID Connect provider for requests to your API products.
This guide includes steps for two common OIDC identity providers, Keycloak and Okta. For more information about this type of external auth, see OIDC and OAuth. You can adapt these steps for other providers.
You can also use the same OAuth policy for multiple API products. For example, you might want to let users authenticate once in the frontend portal.
Before you begin
Create your APIs, including the Gloo ApiDocs that describe the stitched schema.
Bundle your APIs into API products by using a route table.
Make sure that the external auth service is installed and running. If not, install the external auth service in your single or multicluster environment.
kubectl get pods -A -l app=ext-auth-serviceGet the labels of your routes to use to apply policies to, such as with the example query.
kubectl get rt -n istio-ingress -o=jsonpath='{range .items[*]}[{.metadata.name}, {.spec.http[*].name}, {.spec.http[*].labels}]{"\n"}{end}'Example output:
- The
api-example-com-rtroute table does not have any route-level labels. To apply policies, you can add labels to those routes. - The
petstore-rtroute table has ausagePlans: dev-portallabel on itspets-api,users-api, andstore-apiroutes. - The
tracks-rtroute table has ausagePlans: dev-portallabel on itstracks-apiroute.
[api-example-com-rt, , ] [petstore-rt, pets-api users-api store-api, {"usagePlans":"dev-portal"} {"usagePlans":"dev-portal"} {"usagePlans":"dev-portal"} ] [tracks-rt, tracks-api, {"usagePlans":"dev-portal"}]- The
Basic OAuth with Keycloak
Use Keycloak as the OIDC provider to enforce external auth policies.
Step 1: Install Keycloak
You might want to test how to restrict access to your applications to authenticated users, such as with external auth or JWT policies. You can install Keycloak in your cluster as an OpenID Connect (OIDC) provider.
The following steps install Keycloak in your cluster, and configure two user credentials as follows.
- Username:
user1, password:password, email:user1@example.com - Username:
user2, password:password, email:user2@solo.io
Install and configure Keycloak:
Create a namespace for your Keycloak deployment.
kubectl create namespace keycloakCreate the Keycloak deployment.
kubectl -n keycloak apply -f https://raw.githubusercontent.com/solo-io/gloo-mesh-use-cases/main/policy-demo/oidc/keycloak.yamlWait for the Keycloak rollout to finish.
kubectl -n keycloak rollout status deploy/keycloakSet the Keycloak endpoint details from the load balancer service.
export ENDPOINT_KEYCLOAK=$(kubectl -n keycloak get service keycloak -o jsonpath='{.status.loadBalancer.ingress[0].ip}{.status.loadBalancer.ingress[0].hostname}'):8080 export HOST_KEYCLOAK=$(echo ${ENDPOINT_KEYCLOAK} | cut -d: -f1) export PORT_KEYCLOAK=$(echo ${ENDPOINT_KEYCLOAK} | cut -d: -f2) export KEYCLOAK_URL=http://${ENDPOINT_KEYCLOAK} echo $KEYCLOAK_URLSet the Keycloak admin token. If you see a parsing error, try running the
curlcommand by itself. You might notice that your internet provider or network rules are blocking the requests. If so, you can update your security settings or change the network so that the request can be processed.export KEYCLOAK_TOKEN=$(curl -d "client_id=admin-cli" -d "username=admin" -d "password=admin" -d "grant_type=password" "$KEYCLOAK_URL/realms/master/protocol/openid-connect/token" | jq -r .access_token) echo $KEYCLOAK_TOKENUse the admin token to configure Keycloak with the two users for testing purposes. If you get a
401 Unauthorizederror, run the previous command and try again.# Create initial token to register the client read -r client token <<<$(curl -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" -X POST -H "Content-Type: application/json" -d '{"expiration": 0, "count": 1}' $KEYCLOAK_URL/admin/realms/master/clients-initial-access | jq -r '[.id, .token] | @tsv') export KEYCLOAK_CLIENT=${client} echo $KEYCLOAK_CLIENT # Register the client read -r id secret <<<$(curl -k -X POST -d "{ \"clientId\": \"${KEYCLOAK_CLIENT}\" }" -H "Content-Type:application/json" -H "Authorization: bearer ${token}" ${KEYCLOAK_URL}/realms/master/clients-registrations/default| jq -r '[.id, .secret] | @tsv') export KEYCLOAK_SECRET=${secret} echo $KEYCLOAK_SECRET # Add allowed redirect URIs curl -k -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" -X PUT -H "Content-Type: application/json" -d '{"serviceAccountsEnabled": true, "directAccessGrantsEnabled": true, "authorizationServicesEnabled": true, "redirectUris": ["*"]}' $KEYCLOAK_URL/admin/realms/master/clients/${id} # Add the group attribute in the JWT token returned by Keycloak curl -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" -X POST -H "Content-Type: application/json" -d '{"name": "group", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-attribute-mapper", "config": {"claim.name": "group", "jsonType.label": "String", "user.attribute": "group", "id.token.claim": "true", "access.token.claim": "true"}}' $KEYCLOAK_URL/admin/realms/master/clients/${id}/protocol-mappers/models # Create first user curl -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" -X POST -H "Content-Type: application/json" -d '{"username": "user1", "email": "user1@example.com", "firstName": "Alice", "lastName": "Doe", "enabled": true, "attributes": {"group": "users"}, "credentials": [{"type": "password", "value": "password", "temporary": false}]}' $KEYCLOAK_URL/admin/realms/master/users # Create second user curl -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" -X POST -H "Content-Type: application/json" -d '{"username": "user2", "email": "user2@solo.io", "firstName": "Bob", "lastName": "Doe", "enabled": true, "attributes": {"group": "users"}, "credentials": [{"type": "password", "value": "password", "temporary": false}]}' $KEYCLOAK_URL/admin/realms/master/usersOpen the Keycloak frontend.
open $KEYCLOAK_URLLog in to the admin console, and enter
adminas the username andadminas your password.In the Keycloak admin console, go to Users, and verify that the users that created earlier are displayed. You might need to click on View all users to see them.
In the Keycloak admin console, go to Clients, and verify that you can see a client ID that equals the output of
$KEYCLOAK_CLIENT.
Step 2: Configure Keycloak
Now that you installed Keycloak and set up clients to use, configure Keycloak for certain OAuth use cases. The following instructions assume that you are still logged into the Administration Console from the previous step.
You might integrate OIDC with your apps. In such cases, you might need particular details from the OIDC provider to fully set up your apps. To use Keycloak for OAuth protection of these apps, you need certain settings and information from Keycloak.
- For authorization code OAuth: Confirm that you have the following environmental variables set. If not, refer to Step 1: Install Keycloak section.
echo $KEYCLOAK_CLIENT echo $KEYCLOAK_URL - If you plan to use access token validation: Get a JWKS URI to use for an inline OAuth policy.
- From the sidebar menu options, click Realm Settings.
- From the General tab, scroll down to the Endpoints section and open the OpenID Endpoint Configuration link. In a new tab, your browser opens to a URL similar to
http://$KEYCLOAK_URL:8080/realms/master/.well-known/openid-configuration. - In the OpenID configuration, search for the
token_endpointfield. Save the value as an environment variable, such as the following example.export TOKEN_ENDPOINT=$KEYCLOAK_URL/realms/master/protocol/openid-connect/token - In the OpenID configuration, search for the
jwks_urifield, and copy this value, similar tohttp://$KEYCLOAK_URL:8080/realms/master/protocol/openid-connect/certs.export KEYCLOAK_JWKS_URI=<jwks_uri> - In a new browser tab, open the
jwks_urithat you previously copied. - From the opened
jwks_uri, copy and save the entire value of the keys as an environment variable.export KEYCLOAK_CERT_KEYS={"keys":[{"kid":"_YYA...","kty":"RSA","alg":"RSA-OAEP","use":"enc","n":"r4AXlC9sR..."}]}
Step 3: Create the external auth server
Create resources that you need for external auth, such as a Gloo external auth server and route table with a label that you can use to apply policies.
You can do the following steps in a different order, depending on when you want the policy to take effect. For example, you might want the policy to always take effect as soon as the route is created. To do so, you can create the policy before you add the route to the route table.
- Create a Kubernetes secret with the Keycloak OIDC secret.
kubectl apply -f - <<EOF apiVersion: v1 kind: Secret metadata: name: oauth namespace: gloo-mesh type: extauth.solo.io/oauth stringData: client-secret: ${KEYCLOAK_SECRET} EOF - Create an external auth server to use for your policy. The example uses the default external auth server that you created when you installed Gloo Platform.
kubectl apply -f - <<EOF apiVersion: admin.gloo.solo.io/v2 kind: ExtAuthServer metadata: name: ext-auth-server namespace: gloo-mesh spec: destinationServer: ref: cluster: $CLUSTER_NAME name: ext-auth-service namespace: gloo-mesh port: name: grpc EOF
Step 4: Create an OAuth policy with access token validation
Create an external auth policy that uses access token validation. The following example YAML uses JWT validation and an inline JWKS server to provide the JWT.
- For more access token validation options, see the API reference docs for
AccessTokenValidation. - For more information about JWTs, see the JWT policy guide.
- Is your JWT in a custom header? By default, the JWT token is expected in the
Authorization: Bearer <token>header format. If you use a different header, apply a transformation policy. - For more JWT options such as to fetch the token from a remote JWKS instead of an inline JWKS, see the API reference docs for
JwtValidation.
kubectl apply -f - <<EOF
apiVersion: security.policy.gloo.solo.io/v2
kind: ExtAuthPolicy
metadata:
name: oauth-jwt-validation
namespace: gloo-mesh
spec:
applyToRoutes:
- route:
labels:
oauth: "true"
config:
server:
name: ext-auth-server
namespace: gloo-mesh
cluster: $CLUSTER_NAME
glooAuth:
configs:
- oauth2:
accessTokenValidation:
jwt:
localJwks:
inlineString: >-
$KEYCLOAK_CERT_KEYS
EOF
| Setting | Description |
|---|---|
applyToRoutes | Use labels to configure which routes to apply the policy to. This example label matches the app and route from the example route table that you apply separately. If omitted and you do not have another selector such as applyToDestinations, the policy applies to all routes in the workspace. |
server | The external auth server to use for the policy. |
oauth2 | Configure the OAuth 2.0 protocol details to use to authenticate requests. The example uses Keycloak as the external identity provider and access token with JWT validation. |
inlineString | Replace $KEYCLOAK_CERT_KEYS with the value from your Keycloak OIDC provider at $KEYCLOAK_URL/realms/master/protocol/openid-connect/certs. For steps to retrieve this value, refer to the previous section about installing Keycloak. |
Basic OAuth with Okta
Use Okta as the OIDC provider to enforce external auth policies.
Step 1: Set up an Okta OIDC app
Configure an Okta OIDC app to get the information that you need to create external auth policies to secure your resources.
Open the Okta dashboard. If you don’t have an Okta account that you can use, sign up for an Okta developer account.
From the Applications menu, click Applications > Create New App. Note that you might see a Create App Integration button instead.

Figure: Okta application dashboard 
Figure: Okta application dashboard Select OIDC - OpenID Connect as the sign-in method for your app and Single-Page Application as your application type. Then, click Next.
Enter a name for your app and optionally upload a logo.
For Grant type, check both Authorization Code and Refresh Token.
For Sign-in redirect URIs, enter the location from which you want to allow users to log in. The URL is composed of hostname that you set up for your Portal resources and the
/callbackpath. For example, for the developer portal frontend app, you might enter:https://developer.example.comto let a user log in from the secured home page.- React starter app:
https://developer.example.com/apisto let a user log in from the APIs page. - Backstage frontend plug-in:
https://developer.example.com/gloo-platform-portalto let a user log in from the Gloo Portal Backstage plug-in page. https://developer.example.com/callbackfor the callback path.
For the Sign-out redirect URIs, enter the location to redirect the user after logging out, such as the following examples:
- React starter app:
https://developer.example.com/logout. - Backstage frontend plug-in:
https://developer.example.com/gloo-platform-portal/logout
- React starter app:
From the Assignments section, select Allow everyone in your organization to access. This way, you do not need to assign a user or group to this app. Instead, you can use your Okta developer account credentials to test the Okta authentication flow.
Click Save to save your changes. You are redirected to the Okta app details page.
From the General tab on the Okta app details page, note the Client ID.

Figure: Okta General tab 
Figure: Okta General tab Store the Client ID as an environment variable.
export CLIENT_ID=<client-id>
Step 2: Configure other Okta account details
Configure other Okta account details, such as the claims that you want to include in the access token.
From the navigation menu, click Security > API.
Click the Authorization Server that you want to use, such as
default.From the Settings tab, click the Metadata URI. In a new tab, your browser opens to a URL similar to
https://dev-1234567.okta.com/oauth2/default/.well-known/oauth-authorization-server.From the metadata URI, search for and save the endpoints that you need as environment variables.
The
token_endpointis where to get the OAuth token.export TOKEN_ENDPOINT=https://dev-1234567.okta.com/oauth2/default/v1/tokenThe
authorization_endpointis where to get the PKCE authorization code.export AUTH_ENDPOINT=https://dev-1234567.okta.com/oauth2/default/v1/authorizeThe
end_session_endpointis where to end the session.export LOGOUT_ENDPOINT=https://dev-1234567.okta.com/oauth2/default/v1/logout
Get the JSON Web Key Set (JWKS) that you use later for an inline access token external auth policy.
From the metadata URI, find the
jwks_uriendpoint. In a new tab, open this endpoint, such ashttps://dev-1234567.okta.com/oauth2/default/v1/keys.export OKTA_JWKS_URI=<jwks_uri>Copy and save the entire value of these keys as an environment variable.
export CERT_KEYS={"keys":[{"kty":"RSA","alg":"RS256","kid":"sKv...","use":"sig","e":"AQAB","n":"kdhR..."}]}
Return to the authorization server page in Okta.
From the Claims tab, click Add Claim to add any claims that you want the access token to include. Later, you can use these claims in your portal groups. For example, the portal group might require a member of
organization: solo.ioto access certain API products. For more information about claims, see JWT structure. For example, you might configure the following claims:- email: Return the user’s email as configured in the user’s profile.
- organization: Return the user’s organization as configured in the user’s profile.
- group: Include any group (match regex
*) for theopenidscope.

Figure: Okta default auth server Claims tab 
Figure: Okta default auth server Claims tab From the Token Preview tab, verify that the tokens return the information that you expect, such as the same
kidas$CERT_KEYSvalue that you previously saved and the claims that you configured.- In OAuth/OIDC client, enter the name of your app.
- In Grant type, select Authorization Code.
- In User, enter your username or the name of the user that you want to log in to the frontend developer portal.
- In Scopes, enter
openid. - Click Preview Token, then flip between the
id_tokenandtokenpreviews. - If you do not see the claim information that you expect, click your profile > My settings and review your personal information. For example, you might not have an
organizationorgroupset. You can edit your profile to include this information, then preview the token again.

Figure: Okta Token Preview tab 
Figure: Okta Token Preview tab
Step 3: Create the OAuth policy
Set up external auth resources with Okta as the OIDC provider.
Create an external auth server that enforces your extauth policy.
kubectl apply -f - <<EOF apiVersion: admin.gloo.solo.io/v2 kind: ExtAuthServer metadata: name: ext-auth-server namespace: gloo-mesh spec: destinationServer: ref: cluster: $CLUSTER_NAME name: ext-auth-service namespace: gloo-mesh port: name: grpc EOFCreate an external auth policy for the
oauth: "true"routes to enforce authentication via an access token from the Okta OIDC provider that you set up. Make sure to replace$CERT_KEYSwith the entire{"keys":[{"kid":"_YYA...","kty":"RSA","alg":"RSA-OAEP","use":"enc","n":"r4AXlC9sR..."}]}value that you previously retrieved.kubectl apply -f -<<EOF apiVersion: security.policy.gloo.solo.io/v2 kind: ExtAuthPolicy metadata: name: oauth-okta namespace: gloo-mesh spec: applyToRoutes: - route: labels: oauth: "true" config: server: name: ext-auth-server namespace: gloo-mesh cluster: $CLUSTER_NAME glooAuth: configs: - oauth2: accessTokenValidation: jwt: localJwks: inlineString: >- $CERT_KEYS EOFReview the following table to understand this configuration. For more information, see the API docs.
Setting Description spec.applyToRoutes Select the routes that you want to apply this policy to. In this example, you want to require external authentication for all routes with the oauth: "true"label.spec.config.server The external auth server to use for the policy. glooAuth.configs.oauth2.accessTokenValidation.jwt Set up the policy to enforce authentication via an access token that conforms to the JWT specification. glooAuth.configs.oauth2.accessTokenValidation.jwt.localJwks Use the local JWKS that you provide inline to validate the JWT access token. glooAuth.configs.oauth2.accessTokenValidation.jwt.localJwks.inlineString Provide the JWKS keys that you retrieved from your OIDC provider inline. Verify that the external auth policy is applied successfully.
Review the status of the external auth policy and make sure that it shows
ACCEPTED.kubectl get extauthpolicy oauth-okta -n gloo-mesh -o yamlGet the authconfig resource that was created for your policy and make sure that it shows
ACCEPTED.kubectl get authconfig -n gloo-mesh -o yamlIf you see a
REJECTEDerror similar toinvalid character 'k' looking for beginning of object key string, try copying the$CERT_KEYSvalue manually again.
Verify OAuth
Verify that requests to your API products now require external authentication.
Get the external address of your ingress gateway. The steps vary depending on the type of load balancer that backs the ingress gateway.
export INGRESS_GW_ADDRESS=$(kubectl get svc -n istio-ingress istio-ingressgateway -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo $INGRESS_GW_ADDRESSNote: Depending on your environment, you might see
<pending>instead of an external IP address. For example, if you are testing locally in kind or minikube, or if you have insufficient permissions in your cloud platform, you can instead port-forward the service port of the ingress gateway:kubectl -n istio-ingress port-forward deploy/istio-ingressgateway-main 8081Send a
curlrequest to one of your APIs.curl -v --resolve api.example.com:80:${INGRESS_GW_ADDRESS} http://api.example.com/trackapi/tracksReview the output of the previous step. The request does not succeed because you need to authenticate via OIDC.
Next steps
- If you haven’t already, set rate limits for your API products.
- Configure the developer portal.