Sometimes there is a need to create the API for 3rd party clients to integrate with your product. And then the question appears: what auth method to use. Oauth 2 is the most widely adopted standard for API auth. Oauth 2 is secure, tokens are short-lived and revocable, granular access is possible. In this blogpost we will show you how to create Oauth 2 authorization with AWS Cognito for your API Gateway using AWS CDK. Using Cognito this way eliminate a need for additional auth Lambda with custom code.
Prerequisites
- You are familiar with AWS API gateway
- You are familiar with infrastructure as code
- You are familiar with AWS CDK and how to use it
Solution details
- Use AWS Cognito Userpool as an authorizer
- Create the machine-to-machine app client resource, attach needed scopes. When it’s created, you get client ID and secret credentials that you will have to share with 3rd party client
- Create the domain for Cognito Userpool, that would look like a separate endpoint with different domain that your API gateway domain. This domain can be a generated one by AWS and look like: {YOUR_CUSTOM_PREFIX}.auth.{AWS_REGION}.amazoncognito.com or a custom one (in this case domain certificate in North Virginia region is required)
- Create Cognito authorizer in your API Gateway and set Cognito Userpool as a source for it
How client application will interact with the API

Notes
- In this example we create only one default custom scope. Of course you can create multiple custom scopes and attach them to different app clients and API Gateway endpoints according to your needs
- We create only one proxy integration endpoint for API gateway for simplicity of the example
Out of scope of this example
- API Gateway setup (other than auth related minimum for this example)
- Deployment of your CDK defined infrastructure
AWS CDK code to create Oauth 2 with API Gateway infrastructure
// Cognito user pool
const userPool = new UserPool(this, 'UserPool')
// Custom resource server and scope
const customScope = new ResourceServerScope({
scopeName: 'default',
scopeDescription: 'Custom default scope'
})
const customResourceServer = userPool.addResourceServer('ResourceServer', {
identifier: 'default',
userPoolResourceServerName: 'Custom default resource server',
scopes: [customScope]
})
// Cognito app clients
userPool.addClient('ThirdPartyAppClient', {
userPoolClientName: 'ThirdPartyClientName',
generateSecret: true,
authFlows: {
userSrp: false,
userPassword: false,
adminUserPassword: false,
user: false,
custom: false
},
oAuth: {
flows: {
clientCredentials: true
},
scopes: [OAuthScope.resourceServer(customResourceServer, customScope)]
},
refreshTokenValidity: Duration.days(5)
})
// Create cognito generated domain
userPool.addDomain('UserPoolDomain', {
cognitoDomain: {
domainPrefix: 'YOUR_CUSTOM_PREFIX' // Replace it with your domain prefix
}
})
// Rest Api
const restApi = new RestApi(this, 'NewRestApi', {
endpointTypes: [EndpointType.REGIONAL]
})
// Cognito authorizer
const authorizer = new CognitoUserPoolsAuthorizer(this, 'Authorizer', {
cognitoUserPools: [userPool]
})
// Api proxy integration
const proxyLambda = new lambda.Function(this, 'ProxyLambda', {
runtime: lambda.Runtime.NODEJS_22_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('app/dist/src'),
timeout: Duration.seconds(10)
})
const proxyIntegration = new LambdaIntegration(proxyLambda, {
proxy: true,
allowTestInvoke: true
})
const proxyResource = restApi.root.addResource('{proxy+}')
proxyResource.addMethod('ANY', proxyIntegration, {
authorizer,
authorizationScopes: ['default/default']
})
(Optional) AWS Code to create custom Cognito domain
// Create custom cognito domain
const hostedZoneId = 'YOUR_HOSTED_ZONE_ID' // Replace it with your hosted zone ID
const hostedZoneName = 'YOUR_HOSTED_ZONE_NAME' // Replace it with your hosted zone name
const hostedZone = HostedZone.fromHostedZoneAttributes(this, 'HostedZone', {
hostedZoneId: hostedZoneId,
zoneName: hostedZoneName
})
const cognitoCustomDomainName = `YOUR_SUBDOMAIN.${hostedZoneName}` // Replace it with your desired subdomain if needed
const cognitoCertificate = Certificate.fromCertificateArn(
this,
'CognitoCertificate',
'YOUR_CERTIFICATE_ARN' // Replace with your certificate ARN, certificate must be in the us-east-1 (North Virginia) region
)
const customDomain = userPool.addDomain('UserPoolCustomDomain', {
customDomain: {
domainName: cognitoCustomDomainName,
certificate: cognitoCertificate
}
})
new ARecord(this, 'UserPoolCustomDomainAliasRecord', {
zone: hostedZone,
recordName: cognitoCustomDomainName,
target: RecordTarget.fromAlias(new UserPoolDomainTarget(customDomain))
})
Testing of auth endpoint after deployment
You can simply test it with CURL command:
curl -X POST {YOUR_COGNITO_DOMAIN}/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=CLIENT_ID&client_secret=CLIENT_SECRET"
Alternatively use tools like Postman.
Conclusion
That’s it! ;) You have just seen how easy Oath 2 auth for API Gateway can be created.