Imagine you want to have user authentication protecting a bunch of web applications that do not implement any authentication methods themselves.

We found ourselves in a situation where such a problem was solved by adding a reverse-proxy layer, in which Apache performed OpenID Connect authentication to Okta before sending the request to the actual application.

Apache Solution

For various reasons we were unhappy with this situation and set out to find a better way to solve this problem. We explored solutions using CloudFront, API Gateway, and the Application Load Balancer.

Amazon CloudFront Solution

Initially, we investigated using Lambda@Edge in CloudFront to perform the authentication. Our main objection to this approach is that the application needs to be publicly available so that CloudFront can use it as an origin. In normal conditions, we would be quite comfortable with this solution, but given that the authentication takes place in CloudFront and not in the application itself we desire better protection of the vulnerable endpoints.

Amazon API Gateway Solution

With API Gateway we implemented a Lambda authorizer to perform authentication and by employing VPC Link we did not have to expose the naked application to the Internet. Our main objection to this approach is that it breaks WebSockets.

Application Load Balancer Solution

The ALB provides an OpenID Connect Action that can perform OpenID Connect authentication in the Load Balancer, allowing us to make the ALB publicly accessible! This greatly reduces the complexity of our solution.

ALB Solution

Configuring Okta for OIDC Authentication

We assume you would like to authenticate againts Okta, any other OpenID Connect identity provider is supported as well. If you prefer to authenticate against Cognito User Pools, there is an action supporting them as well.

In Okta you need to setup an authorization server and an application.

Okta Authorization Server

You will need to have set up an authorization server. Please find the Issuer URI for your authorization server, e.g., https://example.okta-emea.com/oauth/default, see this screenshot.

Okta Application

You also need to setup an application, see Okta documentation. In the Application configuration in Okta, make sure to set the “Login redirect URI” to /oauth2/idresponse on your application URL, e.g., https://example.com/oauth2/idresponse.

Please find the application’s Client ID and Client Secret, see screenshot.

Configuring the ALB for Okta Authentication

We prefer infrastructure as code, so here we show a CloudFormation snippet for defining an ALB Listener with OpenID Connect authentication.

We do not want to put secrets in the code, therfore store the application’s client secret as a parameter in the parameter store. Unfortunately CloudFormation does not support SecureString parameters (as of writing), so you will have to store it as an unencrypted String parameter with the name okta_client_secret.

Snippet

This is, in our opinion, still more secure than putting the secret plain text in the template.

In the following snippet, please make a mental substitution for the following variables:

  • $ISSUER_URI: the Issuer URI, e.g., https://example.okta-emea.com/oauth/default
  • $OKTA_CLIENT_ID, the application’s Client ID
  Listener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      Certificates:
        - CertificateArn: !Ref Certificate
      DefaultActions:
        - Order: 1
          Type: authenticate-oidc
          AuthenticateOidcConfig:
            AuthorizationEndpoint: $ISSUSER_URI/v1/authorize
            ClientId: $OKTA_CLIENT_ID
            ClientSecret: "{{ resolve:ssm:okta_client_secret:1 }}"
            Issuer: $ISSUSER_URI
            Scope: openid email
            TokenEndpoint: $ISSUSER_URI/v1/token
            UserInfoEndpoint: $ISSUSER_URI/v1/userinfo
            SessionTimeout: 300
        - Order: 2
          Type: forward
          TargetGroupArn: !Ref TargetGroup
      LoadBalancerArn: !Ref LoadBalancer
      Port: 443
      Protocol: HTTPS