Integrate AWS ALB with OIDC IdP

Kapil Raina
7 min readAug 4, 2022

Recently, While working on a ALB fronted ECS Fargate for a client, I came across a less known and less used feature of AWS ALB which enables Out Of Box OIDC integration between AWS ALB and an OIDC provider (any OIDC compliant IDP) or natively with AWS Cognito. I found this feature both handy and architecturally significant. It is handy because like nice OIDC authentication feature in AWS API Gateway, ALB can be made to work in the same way with configuration, leaving all the heavy lifting of OIDC handshake (for example with OIDC /authorize, /token, /userinfo endpoints) to ALB. It is architecturally significant because it executes authentication at the edge without needing to create a custom service for this (like a compute service in a public subnet).

ALB routing rule enables authentication action with OIDC provider or AWS Cognito, both of which need to be configured in the ALB routing rules for a listener.

Setting this integration can be quite tricky, since the documentation, configuration options and general technical help for this pattern is thin. So here I am trying to put together my dealings with this!!

In this article, I would use ALB OIDC IdP integration with a popular OIDC provider — Okta. Okta providers a developer portal that can be used create client apps (of type SPA or Web to emulate implicit or authorization code grant types) and user identities. It implements a OIDC server that provides the OIDC endpoints for developer use. Developer portal also integrates with a default login page that is presented to the resource provider apps for common authentication experience.

High Level Design

The flow in this design is fairly clear from the design. Few important points:

  1. This design assumed that OIDC IdP is outside AWS and thus should be accessible over internet or via a VPC or DX if setup on corporate network. This means that Security Groups for ALB and the NACL for the public subnet where ALB nodes are setup should allow this egress traffic.
  2. If the OIDC IdP is set up within AWS, then this design would change. Mainly the IDP should be able to resolve the ALB public DNS.
  3. There is a variation of design for ALB set up for intranet, which works in the same way as this design, but the traffic is resolved with private Hosted zone on AWS.
  4. For simplicity the ALB target groups have been set with Lambda. This is simplify testing and deployment. But it would just as easily work with EC2 AutoScaling, EC2 ECS, Fargate ECS or any compute service that ALB can integrate with.
  5. The design demonstrates auth_code grant type which is a three way flow and involves exchanging a temporarly auth code on public internet and use backchannel (which is ALB in this case !) to exchange the code for all sort of tokens, in exchange of auth_code with the back channel secured with Client-ID and Client-Secret and TLS.

Set Up Client App on IDP

Okta developer portal provides the ability to create sample applications to test the OAuth/OIDC flows. It also allows creation of users and to assign the users to access the app and define OAuth Scopes. These features would be provided in any OIDC IDP. The main set up for this demo includes:

  1. Create an application of type web that supports OIDC Authentication method. Okta generates a client and secret for this type of app, which is to be set on ALB OIDC settings.

2. Take Note of the client-id and secret for the app.

3. Depending on your account in Okta, you may get a certain type of Authorization server (Custom Auth Server). For developer account, a default authorization server is created. In addition your account is assigned a unique subdomain. These two combined together would mean that the OIDC URLs of interest will be of type:

Issuer : https://dev-31831898.okta.com/oauth2/default

Authorize : https://dev-31831898.okta.com/oauth2/default/v1/authorize

Token : https://dev-31831898.okta.com/oauth2/default/v1/token

UserInfo : https://dev-31831898.okta.com/oauth2/default/v1/userinfo

Keep this info handy (#2 & #3)

4. The app needs a callback URL where Okta sends the auth code. This needs to be the one that is generated with AWS ALB by default (/oauth2/idpresponse).

5. Create users on Okta and Assign them to the access IDP app.

This should be all that is needed on IDP(Okta) front. Next, lets set up AWS Services.

Set Up AWS

  1. Application Code — As Lambda

The backend application is coded via a simple AWS Lambda, that can be reached only for authenticated users. This lambda, simply extracts out the OIDC data (that ALB passes to it for authenticated users) and responds with a very simple HTML

exports.handler = async (event) => {
console.log(JSON.stringify(event));
let b64decbody = Buffer.from(event.body,”base64").toString(“utf-8”)
console.log(b64decbody);// Just to see full event passed by ALB
const response = {

statusCode: 200,
body: “<html><body><h5>”+Buffer.from(event.headers[“x-amzn-oidc-data”],”base64").toString(“utf-8”)+”</h5></body></html>”,
headers: {
“Content-Type”: “text/html”
}
};
return response;
};

2. Target Group for ALB

The target type for this target group is the lambda created.

3. ALB

The ALB set up for the demo is :

  • With a HTTPS listener and hence needs TLS certificates from AWS ACM.
  • Has custom Listener Rules for OIDC integration.
  • Is configured to be internet-facing
  • Is created for the public subnets
  • Has a security group configured for public access on HTTPS and NACL that allows inbound and outbound traffic to ALB

So the ALB setup process is the usual and familiar one.

The main work is on the routing rules, which is based on following requirements:

  • All requests should be on HTTPS(443)
  • Application URLs have a /secure/* pattern to access all features and APIs. All /secure/* requests should be authenticated with OIDC
  • Application has a /login endpoint which should start the login flow.
  • Any other URL should be redirected to /login endpoint

So the routing rules on ALB look as :

The Authenticate action is configured with the Okta app urls seen earlier

This rules initiates authentication for all unauthenticated requests (i.e. the requests that dont have a valid ALB Auth Cookie — AWSELBAuthSessionCookie) on the path /secure/* OR /login. if the requests on these URLs are authenticated (i.e. requests have a valid AWSELBAuthSessionCookie), then the alternate action (#2) is to forward the request to the target group created.

The scope selected in this setting is openid and profile. These scopes are used in the request to the Okta for Access Token. Depending on the scope of Access Token, The OIDC ID token will contain the corresponding info (sub & profile info claims in this case).

The default action rule redirects the request to /login to start the auth process.

Testing

With this setting whenever ALB URL is accessed with any any url other than /secure/* or /login, it will be redirected to login. In all these cases, for the first time, the user would be redirected to Okta login page for authentication.

Post Authentication, Okta would call the ALB redirect URL (set previously in the app i.e /oauth2/idpresponse).

After redirections, the authentication cookie rule passes and the request is passed finally to the Lambda, that extracts the OIDC ID Token Data and displays it. As can be seen, the ID token contains the profile scope claims. User roles can be part of the custom scope set up on IDP and would thus be available in the ID Token for application to execute any entitlements policy decisions (authorization).

References

  1. https://docs.aws.amazon.com/elasticloadbalancing/latest/application/listener-authenticate-users.html
  2. https://docs.aws.amazon.com/elasticloadbalancing/latest/APIReference/API_AuthenticateOidcActionConfig.html
  3. https://oauthdebugger.com/
  4. https://developer.okta.com/docs/reference/api/oidc/

--

--

Kapil Raina

Tech Architect | Unpredictably Curious | Deep Breathing