Skip to content

Example OIDC and OAuth authentication and authorization with Amazon Cognito IdP, Amazon API Gateway, and AWS Lambda Function

Notifications You must be signed in to change notification settings

rgl/terraform-aws-cognito-example

Repository files navigation

About

Lint

This deploys an example Amazon Cognito IdP, Amazon API Gateway, and AWS Lambda Function.

The following components are used:

components

This will:

Usage (on a Ubuntu Desktop)

Install the dependencies:

Set the AWS Account credentials using SSO:

# set the environment variables to use a specific profile.
# e.g. use the pattern <aws-sso-session-name>-<aws-account-name>-<aws-account-role>-<aws-account-id>
export AWS_PROFILE=example-dev-AdministratorAccess-123456
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_DEFAULT_REGION
# set the account credentials.
# see https://docs.aws.amazon.com/cli/latest/userguide/sso-configure-profile-token.html#sso-configure-profile-token-auto-sso
aws configure sso
# dump the configured profile and sso-session.
cat ~/.aws/config
# show the user, user amazon resource name (arn), and the account id, of the
# profile set in the AWS_PROFILE environment variable.
aws sts get-caller-identity

Or, set the AWS Account credentials using an Access Key:

# set the account credentials.
# NB get these from your aws account iam console.
#    see Managing access keys (console) at
#        https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html#Using_CreateAccessKey
export AWS_ACCESS_KEY_ID='TODO'
export AWS_SECRET_ACCESS_KEY='TODO'
unset AWS_PROFILE
# set the default region.
export AWS_DEFAULT_REGION='eu-west-1'
# show the user, user amazon resource name (arn), and the account id.
aws sts get-caller-identity

Review the inputs.tf file.

Initialize the project:

terraform init -lockfile=readonly

Deploy the example:

terraform apply

Show the terraform state:

terraform state list
terraform show

Get an authorization token using the OAuth 2.0 Client Credentials Grant:

token_url="$(terraform output --raw oidc_token_url)"
jwks_url="$(terraform output --raw oidc_jwks_url)"
userinfo_url="$(terraform output --raw oidc_userinfo_url)"
client_id="$(terraform output --raw example_client_id)"
client_secret="$(terraform output --raw example_client_secret)"
# see https://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html
token_response="$(curl \
  -s \
  -X POST \
  -u "$client_id:$client_secret" \
  -d "grant_type=client_credentials&client_id=$client_id&client_secret=$client_secret" \
  "$token_url")"
jq <<<"$token_response"
# NB In Cognito, this token is a JWT (as defined in the JSON Web Token (JWT)
#    Profile for OAuth 2.0 Access Tokens at
#    https://datatracker.ietf.org/doc/html/rfc9068).
# NB Cognito does not let us validate the a client credentials access token by
#    calling the userinfo url (it requires the oidc scope; but client
#    credentials clients cannot use that scope in Cognito). This means we must
#    validate it using the keys available at the JWKS URL.
token="$(jq -r .access_token <<<"$token_response")"
# NB this will not work. its here just do show that it will not work.
# NB the example service will validate the token using the JWKS, and will
#    populate the response with the authorizationClaims property. see the
#    source code in example/main.go.
curl \
  -s \
  -X GET \
  -H "Authorization: Bearer $token" \
  "$userinfo_url" \
  | jq

Access the example service (hosted by the Go AWS Lambda Function Container), using the token:

example_url="$(terraform output --raw example_url)"
curl \
  -s \
  -X GET \
  -H "Authorization: Bearer $token" \
  "$example_url" \
  | jq

The authorization bearer token will be validated by the example service, and its claims will be returned in the authorizationClaims response property. It will returns something alike:

{
  "authorizationClaims": {
    "auth_time": 1,
    "client_id": "6xxxxxxxxxxxxxxxxxxxxxxxx7",
    "exp": "2024-04-10T07:12:09Z",
    "iat": "2024-04-10T08:12:09Z",
    "iss": "https://cognito-idp.eu-west-1.amazonaws.com/eu-west-1_xxxxxxxxxx",
    "jti": "76543210-aaaa-bbbb-cccc-dddddddddddd",
    "scope": "example/auth",
    "sub": "6xxxxxxxxxxxxxxxxxxxxxxxx7",
    "token_use": "access",
    "version": 2
  }
}

Access the example service endpoint protected by the API Gateway, without, and with a token:

example_url="$(terraform output --raw example_url)"
# without token.
# NB this should fail with an Unauthorized error.
curl \
  -s \
  -X GET \
  "$example_url/jwt-cognito-protected" \
  | jq
# with token.
# NB this should succeed and you should see the claims, validated by the
#    api gateway in the apiGatewayAuthorizationClaims response property.
curl \
  -s \
  -X GET \
  -H "Authorization: Bearer $token" \
  "$example_url/jwt-cognito-protected" \
  | jq

Start the OIDC Authorization Code Grant flow dance, and login as alice:HeyH0Password!:

example_authorization_code_oidc_redirect_url="$(terraform output --raw example_authorization_code_oidc_redirect_url)"
xdg-open "$example_authorization_code_oidc_redirect_url"

At the end of the dance, you should see the user claims, something like:

{
  "iss": "https://cognito-idp.eu-west-1.amazonaws.com/eu-west-1_xxxxxxxxxx",
  "sub": "c5e4ae65-7d41-4652-b9e0-6858b6446186",
  "preferred_username": "alice",
  "email": "alice@example.com",
  "email_verified": true,
  "name": "Alice Doe",
  "given_name": "Alice",
  "family_name": "Doe"
}

Destroy the example:

terraform destroy

List this repository dependencies (and which have newer versions):

GITHUB_COM_TOKEN='YOUR_GITHUB_PERSONAL_TOKEN' ./renovate.sh

Notes

References

About

Example OIDC and OAuth authentication and authorization with Amazon Cognito IdP, Amazon API Gateway, and AWS Lambda Function

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published