Accelerating API Security using ForgeRock
Posted August 29, 2021 by Jatinder Singh ‐ 9 min read
Demos the components and configuration needed to implement the much-needed API security using the ForgeRock Platform

Do you know Gartner predicts that by 2022, application programming interface (API) attacks will become the most frequent attack vector, causing data breaches for enterprise web applications? With accelerated Digital Transformation brought by Covid-19 and APIs being the defacto standard to deliver products & services, it is critical to secure all aspects of the API access to defend against lost revenue, fines, and brand and reputation damage.
The API security hardening involves various factors to reduce risk and increase security posture. Since we cannot cover everything through a blog post, the scope of this post is to lay a hands-on foundational understanding of how to secure API access using the OAuth2 federation protocol in ForgeRock AM. Thus, the blog post is a series on API security. This post aims to set the stage with a basic introduction, and the rest of the series will build on the foundation of this post to showcase additional features and configurations.
A quick summary of what we will uncover:
OAuth2 and OIDC Primer
OAuth2 is a standard-based (RFC-6749) federation protocol to allow secure authorization in a standard way across the web, mobile, and desktop applications. Using this protocol, a client can access protected resources on behalf of a user. It is essentially a delegation protocol. So what makes delegation possible? I am glad you asked. OAuth2 effectively implements delegation by introducing a concept called authorization grant. The concept allows a client to obtain the user’s authorization and exchange it for an access token that acts like a pass or a key.
So, why do we need the OAuth2 protocol, and what was the world like before OAuth2 existed for the app developers?
As said earlier, OAuth2 enables delegation. So if we remove delegation, how did the client then access protected resources is the question? The answer pre-2007 is by impersonating the user directly, i.e., the client would ask users for their credentials and authenticate with the access manager to gain unrestricted access to the protected resources. Not only did this involve users blindly trusting a service provider with their credentials, but if there was ever a breach or a known vulnerability, users were directly in the critical path of the security incidents. And not to mention, there was no straightforward way for a user to revoke access once the relationship between a user and the client ended.
With OAuth2, instead of directly sharing the credentials with the client, the client asks for a user’s consent via authorization grant with appropriate scopes. The client then submits this grant to the Authorization Server (AS) to obtain an access token that gives the client controlled and limited access to protected resources. This way of communication is far more secure and allows revocation of access if the relationship between a user and the client ends.
Okay, so OAuth2 makes sense, but why do we need OIDC?
While OAuth2 enables delegation, it does not provide a way for a client to know the identity or user information of who authorized that access and how they authenticated, which might be of great interest to the client. And this is where OIDC plays a key role. It provides an identity layer on top of OAuth2 by providing additional endpoints and tokens through which a client can retrieve information about the user (Identity) and their authentication session.
To keep the scope of the blog post minimal and genuinely understand the value OIDC creates, we will stick to the OAuth2 in this post and leave OIDC for the subsequent posts.
API Security in Action (DEMO)
In our demo, we will protect a fictional API called MaDOC or “My Doctor” that mimics some requirements of an EMR software. The API provides the following features:
- Three different types of users - Admin, Doctor, and Patient;
- Register Patients - Admin can register patients;
- Create Appointments - Admin can create appointments for patients;
- Create Health Records - Doctor can create health records for patients.
NOTE: To make it easier for my audience to follow along, I have included all source code, including a link to MaDOC API, AM Configuration, Postman collection, and IG routes through Sqoop Data’s Github repository. Please see the Github section below for details.
Security Requirements
My goal for the security requirements is to be minimal and instead concentrate on providing a short hands-on lab for deciphering API security using ForgeRock. With that in mind, MaDOC API has the following minimum security requirements:
- MaDOC API must not be altered in any way to enforce security;
- Secure
/users
,/appointments
, and/healthrecords
API endpoints;
Architecture

Please see below for the description of each component:
- MaDOC - Provides a fictional EMR like API for patient management;
- Identity Gateway (IG) - Acts as a Resource Server (RS) and integrates MaDOC API into the ForgeRock Platform without modifying the application itself or container where it runs;
- Access Manager (AM) - Acts as an Authorization Server (AS), provides access management capability, and handles both AuthN and AuthZ requirements;
- User - Acts as a Resource Owner (RO) and provides authorization to Client to access protected resources provided by MaDOC API.
- Postman (Client) - While it’s not shown in the above diagram, we’ll use Postman as a Client to access protected resources held at MaDOC API for our demo.
Configure OAuth2 Services & Clients
As the first steps, we have to set up the OAuth2 services and clients within AM to be aware of all the parties participating in the OAuth2 dance. Our Postman collection provided as part of this blog post has two directories - Set-Up
and Demo
. In this part of the demo, we’ll go ahead and run all four steps provided in the Set-Up
directory.
Step 1: Service Account Login
Request
curl --location --request POST 'https://identity1.sqoopdata.local:17143/openam/json/realms/root/authenticate' \
--header 'Accept-API-Version: resource=2.1' \
--header 'X-OpenAM-Username: amadmin' \
--header 'X-OpenAM-Password: changeit' \
--header 'Cookie: amlbcookie=01; iPlanetDirectoryPro=EnGrBQWK7dPPmSuYS_HPAwBfUQI.*AAJTSQACMDIAAlNLABw2S0dPY0ZUOUpQK0YyVHcvT1A5QjJSS05sRHM9AAR0eXBlAANDVFMAAlMxAAIwMQ..*'
Response
{
"tokenId": "EnGrBQWK7dPPmSuYS_HPAwBfUQI.*AAJTSQACMDIAAlNLABw2S0dPY0ZUOUpQK0YyVHcvT1A5QjJSS05sRHM9AAR0eXBlAANDVFMAAlMxAAIwMQ..*",
"successUrl": "/openam/console",
"realm": "/"
}
Step2: Create OAuth 2.0 Service Provider
Request
curl --location --request POST 'https://identity1.sqoopdata.local:17143/openam/json/realms/emr/realm-config/services/oauth-oidc?_action=create' \
--header 'iplanetDirectoryPro: {{adminSSOToken}}' \
--header 'Content-Type: application/json' \
--header 'Accept-API-Version: resource=1.0' \
--header 'Cookie: amlbcookie=01; iPlanetDirectoryPro=w_-iGc_ok2uNBKK6Zj0XoCQXsrw.*AAJTSQACMDIAAlNLABxLWGVXUFhiejhkSUM3Tndjb2FrZGhXUk5OODg9AAR0eXBlAANDVFMAAlMxAAIwMQ..*' \
--data-raw '{
"advancedOAuth2Config": {
"responseTypeClasses": [
"code|org.forgerock.oauth2.core.AuthorizationCodeResponseTypeHandler",
"device_code|org.forgerock.oauth2.core.TokenResponseTypeHandler",
"token|org.forgerock.oauth2.core.TokenResponseTypeHandler",
"id_token|org.forgerock.openidconnect.IdTokenResponseTypeHandler"
],
"grantTypes": [
"authorization_code",
"password"
]
},
"coreOAuth2Config": {
"statelessTokensEnabled": true
}
}'
Response
Note: Response truncated to maintain some sanity :) Please download our Postman collection for complete details.
{
"_id": "",
"_rev": "-516372400",
"advancedOAuth2Config": {
"responseTypeClasses": [
"code|org.forgerock.oauth2.core.AuthorizationCodeResponseTypeHandler",
"device_code|org.forgerock.oauth2.core.TokenResponseTypeHandler",
"token|org.forgerock.oauth2.core.TokenResponseTypeHandler",
"id_token|org.forgerock.openidconnect.IdTokenResponseTypeHandler"
],
"grantTypes": [
"authorization_code",
"password"
],
...
...
}
Step3: Create Client Application (CA)
Request
Note: Request truncated to maintain some sanity :) Please download our Postman collection for complete details.
curl --location --request PUT 'https://identity1.sqoopdata.local:17143/openam/json/realms/emr/realm-config/agents/OAuth2Client/client-application' \
--header 'Content-Type: application/json' \
--header 'X-Requested-With: ForgeRock Collection' \
--header 'Cookie: amlbcookie=01; iPlanetDirectoryPro=EnGrBQWK7dPPmSuYS_HPAwBfUQI.*AAJTSQACMDIAAlNLABw2S0dPY0ZUOUpQK0YyVHcvT1A5QjJSS05sRHM9AAR0eXBlAANDVFMAAlMxAAIwMQ..*' \
--data-raw '{
"coreOAuth2ClientConfig": {
"userpassword": "password",
"scopes": {
"inherited": false,
"value": [
"users",
"appointments",
"healthrecords"
]
},
"redirectionUris": {
"inherited": false,
"value": ["https://ig1.sqoopdata.local:17193/oauth2"]
},
...
...
}'
Response
Note: Response truncated. Please download our Postman collection for complete details.
{
"_id": "client-application",
"_rev": "1376747112",
"coreOAuth2ClientConfig": {
"userpassword": null,
"loopbackInterfaceRedirection": {
"inherited": false,
"value": false
},
"defaultScopes": {
"inherited": false,
"value": []
},
"refreshTokenLifetime": {
"inherited": false,
"value": 0
},
"scopes": {
"inherited": false,
"value": [
"users",
"appointments",
"healthrecords"
]
},
...
...
}
Step4: Create Resource Server (RS)
Request
Note: Request truncated. Please download our Postman collection for complete details.
curl --location --request PUT 'https://identity1.sqoopdata.local:17143/openam/json/realms/emr/realm-config/agents/OAuth2Client/resource-server' \
--header 'Content-Type: application/json' \
--header 'X-Requested-With: ForgeRock Collection' \
--header 'Cookie: amlbcookie=01; iPlanetDirectoryPro=EnGrBQWK7dPPmSuYS_HPAwBfUQI.*AAJTSQACMDIAAlNLABw2S0dPY0ZUOUpQK0YyVHcvT1A5QjJSS05sRHM9AAR0eXBlAANDVFMAAlMxAAIwMQ..*' \
--data-raw '{
"coreOAuth2ClientConfig": {
"userpassword": "password",
"scopes": {
"inherited": false,
"value": [
"am-introspect-all-tokens"
]
},
...
...
}'
Response
Note: Response truncated. Please download our Postman collection for complete details.
{
"_id": "resource-server",
"_rev": "-1497047801",
"coreOAuth2ClientConfig": {
"userpassword": null,
"loopbackInterfaceRedirection": {
"inherited": false,
"value": false
},
"defaultScopes": {
"inherited": false,
"value": []
},
...
...
}
Identity Gateway Routes
Out of the box, the MaDOC API is not OAuth2 compliant; it does not know how to do the OAuth2 dance with the Authorization Server (AS). To bridge this gap, we will stand up IG to proxy MaDOC API calls. IG will act as a Resource Server and do the needed OAuth2 dance with the AS.
As part of this step, go ahead and copy our IG configuration provided in the ig-config
directory of the source code to your IG instance.
Let’s Test
With all the needed OAuth2 infrastructure set up, we are all set to test if IG can protect our endpoints or not.
Since IG is proxying the MaDOC API, let’s see what would happen if we call the /users
endpoint without an access token.
Request
curl --location --request GET 'https://ig1.sqoopdata.local:17193/users' \
--header 'Content-Type: application/json'
Response
We get a 401 Unauthorized
error.
HTTP/1.1 401
WWW-Authenticate: Bearer realm="OpenIG"
Content-Length: 0
Date: Fri, 27 Aug 2021 16:48:31 GMT
To access the protected endpoints, we need to obtain an access token. So let’s run through Steps 5 and 7 provided in the Demo
directory of the Postman collection. The outcome of these steps essentially obtains the user session, authorization code, and access token.
Assuming you have obtained the access token, let’s re-run the previous request, passing the authorization bearer token in the header.
Request
curl --location --request GET 'https://ig1.sqoopdata.local:17193/users' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer TIf9O_BtX2SG7Zz00akb-_G-OHA'
Response
Viola! We get a successful response back from the MaDOC API. Let’s try to understand what really happened in this request:
- IG extracted the access token sent in the authorization header;
- IG validated the access token against AS;
- IG validated the access token has the scopes required by the filter configuration, i.e. “users” in this case;
- After validating the request, IG forwarded the request to MaDOC API.
[
{
"id": 1,
"username": "alice",
"firstName": "Alice",
"lastName": "Roy",
"created": "2021-08-26T18:48:09.844768Z",
"utype": 2
},
{
"id": 2,
"username": "bob",
"firstName": "Bob",
"lastName": "Nomandy",
"created": "2021-08-27T16:42:45.515611Z",
"utype": 1
},
{
"id": 3,
"username": "mark",
"firstName": "Mark",
"lastName": "Manning",
"created": "2021-08-27T16:44:00.538517Z",
"utype": 3
}
]
Github Source Code
The companion source code for this blog post includes the following:
You can find the source code at our repository hosted at Github here.
You can find the source code for the MaDOC API at our Github here.
Summary
There you have it; our MaDOC API is now successfully protected with the OAuth2 protocol using the ForgeRock Platform. The remarkable thing is the API itself does not know the security protecting its endpoints, nor was it altered in any way to deliver the needed security posture.
If you enjoyed reading this post, stay tuned for the upcoming posts in the series.
References
OAuth2 RFC6749 https://datatracker.ietf.org/doc/html/rfc6749
OIDC 1.0 https://openid.net/specs/openid-connect-core-1_0.html
ForgeRock OAuth2 Guide https://backstage.forgerock.com/docs/am/7.1/oauth2-guide/