Skip to main content

Authorize M2M Requests with Amazon Cognito

Amazon Cognito user pools can issue access tokens to confidential application clients. Background services, scheduled jobs, command-line tools, and automation can use these tokens to call APIs without an end user signing in.

The OAuth 2.0 Client Credentials grant is designed for this type of machine-to-machine (M2M) communication. Apache APISIX can protect the target API by validating each token's signature, issuer, app-client identifier, and custom scope before proxying the request.

The guide shows how to configure a Cognito resource server and M2M app client for APISIX. The app client receives a custom scope and uses its client credentials to request an access token. APISIX validates the token locally with Cognito's JSON Web Key Set (JWKS) and removes authentication data before forwarding the request to the sample upstream.

Prerequisite(s)

caution

Amazon Cognito charges for successful M2M token responses. Review Amazon Cognito pricing and avoid requesting new tokens while an existing token remains valid.

Configure Amazon Cognito

Use a Cognito user pool with a domain, then register a resource server and an M2M app client.

Create or Select a User Pool

Sign in to the AWS Management Console and open Amazon Cognito → User pools.

If a suitable user pool with a domain already exists, select it and continue to the next section. Otherwise, create one:

  1. Select Create user pool.
  2. Select Machine-to-machine application as the application type.
  3. Enter APISIX M2M Client as the application name.
  4. Select Create user directory.

Cognito creates the user pool, a confidential app client, a user pool domain, and a generated default resource server and scope. The following steps replace the generated scope with one that clearly identifies the protected API.

Create a Resource Server

In the user pool, create the protected API and its custom scope:

  1. Select Applications → Resource servers → Create resource server.
  2. Enter APISIX Protected API as the resource server name.
  3. Enter https://apisix.example.com as the resource server identifier.
  4. Select Add custom scope.
  5. Enter read as the scope name and Read protected messages as the description.
  6. Select Create resource server.

The identifier is a logical name for the API and does not need to resolve to a network endpoint. Cognito combines the identifier and scope name as https://apisix.example.com/read in access tokens.

Configure the M2M App Client

If the user pool was created for another application, add an M2M app client:

  1. Select Applications → App clients → Create app client.
  2. Select Machine-to-machine application.
  3. Enter APISIX M2M Client as the application name.
  4. Keep Automatically generate a new client secret selected.
  5. Select Create app client.

Authorize the app client to request the protected API scope:

  1. Open APISIX M2M Client and select Login pages → Edit.
  2. Under Custom scopes, remove the generated default scope if it is present.
  3. Select https://apisix.example.com/read.
  4. Select Save changes.

Authorize the Cognito M2M app client with the protected API scope

The app client should show Client credentials grant as its OAuth grant type and https://apisix.example.com/read as its custom scope. Client Credentials cannot be combined with Authorization Code or Implicit grants in the same Cognito app client.

The managed login status is Unavailable because an M2M app client does not use interactive login pages. The OAuth token endpoint remains available through the user pool domain.

Save the OAuth Configuration

On the user pool Overview page, record the User pool ID. Under Branding → Domain, record the Cognito domain. Open the M2M app client and record its Client ID and Client secret.

Save the values to environment variables, replacing the examples:

export COGNITO_REGION=ap-southeast-2
export COGNITO_USER_POOL_ID=ap-southeast-2_example
export COGNITO_DOMAIN=your-prefix.auth.ap-southeast-2.amazoncognito.com
export COGNITO_M2M_CLIENT_ID=replace-with-your-client-id
export COGNITO_M2M_CLIENT_SECRET=replace-with-your-client-secret
export COGNITO_M2M_SCOPE=https://apisix.example.com/read
export COGNITO_ISSUER="https://cognito-idp.${COGNITO_REGION}.amazonaws.com/${COGNITO_USER_POOL_ID}"
export COGNITO_DISCOVERY="${COGNITO_ISSUER}/.well-known/openid-configuration"

Keep the client secret confidential. Store production credentials in a secret manager and rotate them according to the organization's credential-rotation policy.

Configure APISIX

Configure a route that accepts Cognito bearer tokens before forwarding requests to httpbin.org, a public HTTP request and response service. The /anything/m2m/* endpoint returns request details for verification.

Choose either the Admin API or ADC to configure the route.

Create a route that validates bearer tokens locally with Cognito's JWKS:

curl "http://127.0.0.1:9180/apisix/admin/routes/cognito-m2m" -X PUT \
--data-binary @- <<EOF
{
"uri": "/anything/m2m/*",
"plugins": {
"openid-connect": {
"client_id": "$COGNITO_M2M_CLIENT_ID",
"discovery": "$COGNITO_DISCOVERY",
"bearer_only": true,
"use_jwks": true,
"claim_validator": {
"audience": {
"claim": "client_id",
"required": true,
"match_with_client_id": true
}
},
"required_scopes": ["$COGNITO_M2M_SCOPE"],
"set_access_token_header": false,
"set_id_token_header": false,
"set_userinfo_header": false
},
"proxy-rewrite": {
"headers": {
"remove": ["Authorization"]
}
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}
EOF

bearer_only and use_jwks: Require a bearer access token and validate its JWT signature locally with the public keys published by Cognito.

claim_validator.audience: Reads Cognito's client_id access-token claim and requires it to match the M2M app client configured as client_id. Cognito Client Credentials tokens do not contain an API audience claim.

required_scopes: Requires the access token to contain the custom scope assigned to the M2M app client.

set_access_token_header, set_id_token_header, and set_userinfo_header: Set to false to prevent APISIX from adding the access token, ID token, and token claims to upstream request headers.

proxy-rewrite.headers.remove: Removes the original bearer token before APISIX proxies the request. Review these header settings if the upstream application must receive the access token or its claims.

Verify M2M Authorization

Request an access token for the protected API from Cognito. The --user option sends the client ID and secret using HTTP Basic authentication:

export COGNITO_ACCESS_TOKEN="$(
curl -sS "https://${COGNITO_DOMAIN}/oauth2/token" \
--user "${COGNITO_M2M_CLIENT_ID}:${COGNITO_M2M_CLIENT_SECRET}" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=client_credentials" \
--data-urlencode "scope=${COGNITO_M2M_SCOPE}" | \
jq -er '.access_token'
)"

Send the access token to the protected route:

curl -i "http://127.0.0.1:9080/anything/m2m/get" \
-H "Authorization: Bearer ${COGNITO_ACCESS_TOKEN}"

An HTTP/1.1 200 OK response verifies that APISIX accepted a Cognito access token for the configured app client with the required custom scope. The response body should contain fields similar to these:

{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Host": "127.0.0.1",
"User-Agent": "curl/8.7.1",
"X-Amzn-Trace-Id": "Root=1-...",
"X-Forwarded-Host": "127.0.0.1:9080"
},
"json": null,
"method": "GET",
"origin": "192.168.155.1, xxx.xxx.xxx.xxx",
"url": "http://127.0.0.1:9080/anything/m2m/get"
}

Header values and the reported origin address vary by client and network environment. The upstream request headers should not include Authorization, X-Access-Token, X-Id-Token, or X-Userinfo because the route prevents them from being proxied.

Send the same request without a token:

curl -i "http://127.0.0.1:9080/anything/m2m/get"

APISIX should return an HTTP/1.1 401 Unauthorized response because the route requires a bearer token.

Next Steps

You have now configured APISIX to authorize M2M requests using access tokens and custom scopes from Amazon Cognito. For browser-based user authentication, see Set Up SSO with Amazon Cognito. See the openid-connect plugin reference for more token-validation and authorization options.