Apigee Edge, the API management platform, already has a well-defined OAuth2 policy that supports various OAuth2 grant types. In addition to that, Apigee also supports integration with an external OAuth system and relying on the Authentication flow of the external system to provide security for the APIs proxied via Edge. There are 2 ways to execute:
In this post, we will explore the second option. This is because we may have users stored in an external OAuth system like Auth0 and it delegates access tokens on successful authentication of the users. Since on Apigee, we also use access tokens to secure our APIs, it will be beneficial if we can use the same access tokens that were generated by auth0 and verified by Apigee. This reduces the work for Apigee to generate its own tokens and also ensures that the token is generated for a legitimate user.
Apigee Edge is an API management platform that helps you expose your backend services (APIs) to your client applications in a secure and well-defined manner. Apigee creates an API proxy that acts as a facade for the client applications which want to access your APIs. Apigee lets your backend services focus only on the core business logic and frees them from the concerns of access control, traffic rate-limiting, data mediation, and so on.
Auth0 is an identity-as-a-service platform that provides identity-related services like Authentication (AuthN) and Authorization (AuthZ). It is a developer-friendly identity management solution, providing an easy path for developers to integrate their applications by providing SDKs for a wide range of programming languages and frameworks. It also provides integration with various popular social identity providers like Facebook, Google, Linked In, etc, and also provides integration with different enterprise identity management solutions like Azure Active Directory, LDAP, etc.
Flow:
Step 1: Setup an Auth0 application
Auth0 application is a way to integrate different types of apps with Auth0. It provides us an app ID that is required when integrating a particular app with Auth0 or when requesting an access token for a user. We can think of an Auth0 app as a link that connects our apps with Auth0 and through it we can use different services offered by Auth0. To create the application we need to do the following steps:
Step 2: Setup an Auth0 API
Auth0 API lets us define the API URL of our backend application that we want to secure with Auth0 access tokens and in our case, we want to secure an Apigee API proxy, so we will mention its URL. When connecting with Auth0 from an app, we can specify an audience parameter with API proxy URL as its value. This tells Auth0 that this app wants to have an access token specific to this particular API or API proxy. Later on, when Apigee validates this access token, it will check for the audience parameter.
Step 3: Create a user in Auth0
To authenticate a user, we need to create a user in Auth0 which we can later use when authenticating with Auth0. Note that the user needs to be present in the Auth0 database or if it is present in an external database, then it should be imported to Auth0 database first.
We can create users directly in the users section in the Auth0 dashboard. To create a user, we need to mention the user email, password, and connection type. The connection type in our case is “Username-Password-Authentication” which means we can use username and password when authenticating with Auth0. It also means that the user will be created in the default Auth0 database.
Step 4: Add policies to API proxy to validate Auth0 access tokens
Our API proxy should be able to validate/verify the access token that is present in the API request. In order to do so, we can use Apigee’s VerifyJWT policy to verify the access token from the Auth0 as they are JWT based access tokens. When configuring the Verify JWT policy, we need to mention the following key parameters:
The final policy configuration should look like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<VerifyJWT async="false" continueOnError="false" enabled="true" name="VJ-VerifyAuth0AccessToken">
<DisplayName>VJ-VerifyAuth0AccessToken</DisplayName>
<Algorithm>RS256</Algorithm>
<Type>Signed</Type>
<PublicKey>
<JWKS uri="https://{auth0_domain}/.well-known/jwks.json"/>
</PublicKey>
<Issuer>https://{auth0_domain}/</Issuer>
<Audience>https://{apigee_domain}/test-api</Audience>
</VerifyJWT>
Now that we have set up all the things that we require, it’s time to test our implementation. To test our setup, we will use Postman, the API testing tool. It can make HTTP requests to our API proxy and we can also attach an access token with the request.
Generate the access token
To authenticate a user with Auth0 and to get an access token from Auth0, we will use the Auth0 token endpoint with the OAuthV2 password grant type. In the API request to Auth0 token endpoint, we need to mention the following information:
Request (CURL)
curl --location --request POST 'https://{apigee_domain}/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=mCKhEC7x5a0E323xxxxxxxxxxxx' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'scope=openid email profile' \
--data-urlencode 'audience=https://{apigee_domain}/test-api' \
--data-urlencode '[email protected]' \
--data-urlencode 'password=test@123'
Response
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR..........",
"id_token": "eyJhbGciOiJSUzI1NiI...........",
"scope": "openid email profile",
"expires_in": 86400,
"token_type": "Bearer"
}
API request with an invalid access token:
When we make an API request to our API proxy endpoint without an access token, it should respond with a 401 unauthorized error. In the below image, our API proxy responded with an error response because our Verify JWT policy was not able to find the access token. Hence rejected the API request:
Request:
curl --location --request GET 'https://{apigee_domain}/test-api' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cC.......'
Response:
{
"fault": {
"faultstring": "Invalid token: policy(VJ-VerifyAuth0AccessToken)",
"detail": {
"errorcode": "steps.jwt.InvalidToken"
}
}
}
API request with an access token:
Now, we will make an API request to our Apigee endpoint with an access token. The access token should be added as an “Authorization” request header as:
Once we add the access token with the request, we can click on send, and in the response, we should be able to get a 200 success response from the API proxy with the response data. At this moment, we can conclude that our JWT policy has successfully validated the access token.
Request:
curl --location --request GET 'https://mohdilyas-eval-test.apigee.net/test-api' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImNmUjJ0TmVsalNFSFAySHVzWjhQMiJ9.eyJpc3MiOiJodHRwczovL2lseWFzLWF1d........'
Response:
Hello, Guest!
Conclusion
When developing the solution, testing forms an equally important aspect as it validates the quality of execution. A similar approach can be used with other identity providers like Amazon Cognito, OKTA, etc. It could be possible that our users are stored or managed by a different identity provider than Auth0. As long as that provider is capable of providing JWT access tokens, this solution can be used there as well. Besides verifying the token, Apigee can decode it as well, which means we can have access to all the claims of the token as “flow variables” and those can be used to implement other use cases within the API proxy.
This approach is suitable for scenarios where we are delegating our authentication and authorization to an external identity management provider. We can integrate this external system with our applications and those apps can use APIGEE to get the data from the backend data services. In case, we are not dependent on an external identity provider, then we can still use APIGEE for generating access tokens and then use them to get the data from the API proxies.