Securing APIs using OpenID Connect in API Gateway

Supported Versions: 10.1 & 10.2

For securing APIs using OpenID Connect in API Gateway for versions 10.3 and above please refer https://tech.forums.softwareag.com/t/securing-apis-using-openid-connect-in-api-gateway-10-3/237456

webMethods API Gateway tutorial

Introduction

OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol. It allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server. In API Gateway, APIs can be enforced with OpenID authentication and the authorization can be done based on the claims present in the ID token.

Prerequisite

The prerequisite is to create an application in the OpenID provider in our example we need to create an application in google.

Things that the OpenID provider(Google) needs from API Gateway Administrators,

  • The callback URL pointing to the host where APIGateway is running. The callback URL should be in this format "http(s)://<hostname>:<port>/rest/pub/apigateway/openid/openIDCallback"      
    Example1 :  http://samlplehost:5555/rest/pub/apigateway/openid/openIDCallback
    Example2 :  https://samlplehost:7799/rest/pub/apigateway/openid/openIDCallback

Things that OpenID provider(Google) gives to API Gateway Administrators

  • Clientid
  • Client Secret

Use-case 1

In this use-case we are going to configure Google OpendID provider and enforce a API with OpenID authentication. In this use-case the OpenID authentication is done using the authorization code flow.
Step 1 :  Login to APIGateway with a user belonging to API-Gateway-Administrators group
Step 2 : Go to OpenID provider page in the Administrator screen.

Step 3 : Click Add OpenID provider.

Step 4 : Fill in the provider details. If the OpenID provider has Discovery URL then you can just copy the Discovery URL and click Discover all the respective provider configurations will be filled automatically. For google the discovery URL is "https://accounts.google.com/.well-known/openid-configuration". If the discovery URL is not provided then you can fill in the provider details manually.
 

 
Field Description
Name Name of an OpenID provider.
API Gateway supports OpenID Connect (OIDC) identity tokens provided a standards-compliant OpenID provider. For example, Google, Salesforce.
OpenID Connect discovery URL The discovery endpoint of OpenID Connect.
Note:  
When the discovery URL is specified, some of the fields are automatically populated with data provided in the URL.
Token issuer endpoint The endpoint URL of an OpenID token issuer used by API Gateway.
Token endpoint The endpoint URL of OpenID Provider's token (id_token) through which the client application exchanges the authorization code, client ID, and client secret, obtain ID tokens and refresh tokens.
Authorization endpoint The endpoint URL through which API Gateway authenticates and grants authorization to client applications.
JSON Web Key endpoint The endpoint URL of JSON Web Key Signature (JWKS) through which API Gateway verifies and validates the signature of ID tokens.
Truststore alias API Gateway uses the certificates in this truststore alias to verify and validate the signature of the ID token.
Userinfo endpoint Optional. The endpoint URL through which API Gateway retrieves the end user identity information to include as claims in the ID tokens.
Include userinfo claims Optional. Select the check box if you want to include the end user identity information as claims in the ID tokens.
Note:  
Some of the OpenID Providers do not include the complete end user information in the ID tokens. In such cases, you could use this Include userinfo claims check box to include the complete user information. API Gateway generates a secondary ID token that includes the required user information as claims. The token also includes the application ID, and primary ID token, if client has specified the application ID in the GET OpenID token request to API Gateway. As a pre-requisite, you must have the JWT settings configured in API Gateway.
Scope The specific set of end user identity information to include as claims in the ID tokens.
Response type The authorization grant type to obtain authorization codes, ID tokens, and refresh tokens.
Supported values:
code
id_token
token id_token
id_token token
The value code indicates the authorization grant type is code, and an authorization code is returned in the response to obtain ID tokens and refresh tokens.
The values id_token, token id_token, id_token token and indicate the grant type is implicit, and the ID tokens are directly sent to the client.
Token endpoint authentication method The client authentication method that API Gateway will use to communicate with the OpenID provider to fetch the ID token in exchange of an authorization code.
Supported values:
client_secret_basic
client_secret_post
Display Optional. The display type of user interface for end user authentication and consent flow.
Supported values:
none
page
popup
touch
wap
Prompt Optional. The display type of user interface prompt for end user reauthentication and consent flow.
Supported values:
none
login
consent
select_account
Client id An identifier that is unique to the client application.
Client secret The password or phrase for the client application to use to authorize communication with the end user.
Redirect hostname The host name of the redirect endpoint of API Gateway that is configured in the OpenID Provider.
Redirect port The port number on which the redirect endpoint is listening for requests.
Max age Optional. The duration of time (in seconds) in which the end user must have been authenticated. If this time has expired, the OpenID provider must re-authenticate the end user.
Locale Optional. The display language of the user interface for end user authentication and consent flow.


Step 5: Fill in the following details manually

  • Client id (It will be provided by the OpenID provider in our case Google while creating the application)
  • Client secret (It will be provided by the OpenID provider in our case Google while creating the application)
  • Redirect hostname : htttp(s)://<hostname>
    1. Example1 : http://samlplehost
    2. Example2 : https://samlplehost
  • Redirect Port : <port>
    • 5555
    • 7799

The Redirect hostname and redirect port should be the one that was provided to the OpenID provider (Google) while creating the application.
Exampleguration". If the discovery URL is not provided then you can fill in the provider details manually.

Examples:

Callback Url (Specified in Google) Redirect hostname (Specified in API Gateway)          Redirect port (Specified in API Gateway)
http://samlplehost:5555/rest/pub/apigateway/openid/openIDCallback http://samplehost 5555
https://samlplehost:7799/rest/pub/apigateway/openid/openIDCallback https://samlplehost 7799


Step 6: Click Add and then set OpenID provider we have created as the default one.


 

Step 7: Create an API and enforce OpenID authentication as shown below

Step 8: Now let us see how a client application can request for an openid token.

  1. The client has to use the following URL format to fetch the ID token "http(s)://<host>:<port>/rest/pub/apigateway/openid/getOpenIDToken".             Example http://mcarunchi01:5555/rest/pub/apigateway/openid/getOpenIDToken
     
  2. By default API Gateway mandates HTTPS port to fetch the ID token from the provider, However this can be relaxed by setting the "pg_OpenID_isHTTPS" in Administrator → Extended Settings.
     

     
  3. If you try to fetch the OpenID token with http port without changing this property you will end up getting this error.

After properly configuring the "pg_OpenID_isHTTPS" for the requirement , you can copy the URL and paste in the browser. The browser will be redirected to the OpenID provider login page in our page it is Google.

After filling the proper credentials you will get an ID token.

This id_token can be de-crypted using a online tool like https://jwt.io/. This ID Token contains the claims of the user that logged in and also the signature of the OpenID provider. After decrypting the id_token which we received the payload will look something like below.

ID_Token Payload

{
  "azp": "****************",
  "aud": "****************",
  "sub": "***************",
  "email": "carundev@gmail.com",
  "email_verified": true,
  "at_hash": "CQ9KyDT_R45wO5TyFUIkWQ",
  "nonce": "12308389-aab9-4a4f-8a97-d4079781dc91",
  "iss": "https://accounts.google.com",
  "iat": 1509089298,
  "exp": 1509092898,
  "name": "Arun Dev",
  "picture": "https://******************************",
  "given_name": "Arun",
  "family_name": "Dev",
  "locale": "en"
}

Note: I  have masked some of the payload data with *

Step 9: Now invoke the API with the id_token we received in the Authorization header

Request  

GET http://MCARUNCHI01:5555/gateway/Petstore/1.0.0/pet/1 HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpsdsdsdsdsdsdxcxcxc***************
Host: MCARUNCHI01:5555
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

Note: I  have masked some of the request id_token in the authorization header with *

Response 
 
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, DELETE, PUT
Access-Control-Allow-Headers: Content-Type, api_key, Authorization
Content-Type: application/json
Content-Length: 138
 
{
    "id": 1,
    "category": {
        "id": 0,
        "name": "string"
    },
    "name": "doggie",
    "photoUrls": ["string"],
    "tags": [{
            "id": 0,
            "name": "string"
        }
    ],
    "status": "available"
}have masked some of the request id_token in the authorization header with *

Use-case 2

In this use-case we are going to configure Google OpendID provider and enforce a API with OpenID authentication and authorize the client application based on the claims present in the id_token.In this use-case the OpenID authentication is done using the authorization code flow.

Step 1: Follow the steps 1 to 7 from use-case 1

Step 2: Create an application with the claims you want to authorize the application as shown below ,

The API Providers can decide on choosing the claims that can be used to authorize the application. For our demo we are using the "name" claim present in the id_token

Step 3: Enforce "Identify & Authorize Application" policy action and also select "OpenID Connect" in the "Identification Type" as shown below

Step 4: Below are the two ways of fetching the id_token

 Step 4a: Follow the steps 8 to 9 in Use case 1

 Step 4b: The id_token can be fetched for an particular application by using the following format,

 "http(s)://<host>:<port>/rest/pub/apigateway/openid/getOpenIDToken?app_id=<application_id>"

  Example:  http://mcarunchi01:5555/rest/pub/apigateway/openid/getOpenIDToken?app_id=31ef7eab-cebf-44a1-81bb-b1f0b034293c

If we want to get the id_token for a particular application then we need to configure JWT in API Gateway.  The reason behind this is that whenever a client request for a id_token providing an application id , API Gateway generates a secondary JWT token embedding the primary token fetched from the OpenID provider.

The application id can be fetched from APIGateway by clicking on the created application and the id will be available in the URL as shown below,


If the JWT configuration is not configured and a request for id_token is made with app_id then  you will end up with the error shown below,

Once the JWT configuration is done you can copy the URL and paste in the browser. The browser will be redirected to the OpenID provider login page in our page it is Google.

After filling the proper credentials you will get an ID token.

This id_token can be de-crypted using a online tool like https://jwt.io/. 

Secondary ID_Token Payload  
{
  "sub": "38980105421*************************",
  "aud": [
    "31ef7eab-cebf-44a1-81bb-b1f0b034293c",
    ""
  ],
  "nbf": 1509099329,
  "iss": "SAGTokenIssuer",
  "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjJjZjkwYTVlNjRjNzFk***********",
  "exp": 1509102929,
  "iat": 1509099329,
  "app_id": "31ef7eab-cebf-44a1-81bb-b1f0b034293c"
}

The above id_token is the secondary JWT token generated by API Gateway. In the above JWT you can see a claim called "id_token" this will contain the primary id_token that is generated from the OpenID provider.
The secondary id_token is signed by APIGateway while the primary id_token will be signed by the OpenID provider. APIGateway will verify both the tokens is valid or not

Primary ID_Token Payload

{
  "azp": "****************",
  "aud": "****************",
  "sub": "***************",
  "email": "carundev@gmail.com",
  "email_verified": true,
  "at_hash": "CQ9KyDT_R45wO5TyFUIkWQ",
  "nonce": "12308389-aab9-4a4f-8a97-d4079781dc91",
  "iss": "https://accounts.google.com",
  "iat": 1509089298,
  "exp": 1509092898,
  "name": "Arun Dev",
  "picture": "https://******************************",
  "given_name": "Arun",
  "family_name": "Dev",
  "locale": "en"
}

Note: I  have masked some of the payload data with *

Step 5: Now invoke the API with the id_token we received in the Authorization header

Request

GET http://MCARUNCHI01:5555/gateway/Petstore/1.0.0/pet/1 HTTP/1.1
Authorization: Bearer eeyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIzODk4MDEwNTQyMTAtNHBhOWJkZHExaWt***************
Host: MCARUNCHI01:5555
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

Note: I  have masked some of the request id_token in the authorization header with *

Response

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, DELETE, PUT
Access-Control-Allow-Headers: Content-Type, api_key, Authorization
Content-Type: application/json
Content-Length: 138
 
{
    "id": 1,
    "category": {
        "id": 0,
        "name": "string"
    },
    "name": "doggie",
    "photoUrls": ["string"],
    "tags": [{
            "id": 0,
            "name": "string"
        }
    ],
    "status": "available"
}

If id_token is requested for a user who is not configured in the application you end up with the following error,

Response

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer Realm = 'Integration Server',error = 'invalid_token', error_description = 'The provided token is invalid or expired.',Bearer Realm = 'Integration Server',error = 'invalid_token', error_description = 'The provided token is invalid or expired.'
Connection: Close
Accept: application/json
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
Host: MCARUNCHI01:5555
Content-Type: application/json
Content-Length: 354
 
{"Exception":"API Gateway encountered an error. Error Message:  Unable to identify the application for the request. Request Details: Service - Petstore, Operation - /pet/{petId}, Invocation Time:3:59:14 PM, Date:27 Oct, 2017,  Client IP - 10.60.37.23, User - 389801054210-4pa9bddq1ikmbs8t349e6qdbm4niefgu.apps.googleusercontent.com and Application:null"}

Use-case 3

In this use-case we will see how OpenID authentication is done using the implicit code flow.

Step 1 : Follow the steps 1 to 5 from use-case 1

Step 2 : In the OpenID provider configuration page change the Response Type to id_token as shown below.

This will work only if the OpenID providers support implicit code flow.

Step 3 : Follow the steps 1 to 5 from use-case 1

Step 4 : Now let us see how a client application can request for an OpenID token .

The client has to use the following URL format to fetch the ID token "http(s)://<host>:<port>/rest/pub/apigateway/openid/getOpenIDToken".                                                                    
Example http://mcarunchi01:5555/rest/pub/apigateway/openid/getOpenIDToken

After filling the proper credentials you will get an ID token in the URL and not in the response body as shown below,



Step 5 : Now invoke the API with the id_token we received in the Authorization header

Useful Links

http://openid.net/connect/
https://connect2id.com/products/nimbus-oauth-openid-connect-sdk/openid-connect-providers

image.png