I’m attempting to set a simple “Hello Word” example where a user can access a lambda endpoint only after authenticating with Cognito via social login.
I have all the code which deploys succesfully and I can get through the login flow and get a valid cognito token, however when I invoke my endpoint I always get
{
"message": "Forbidden"
}
So I’ve missed a step somewhere but not sure where.
cognito-cdk-stack.ts
export class CognitoCdkStack extends cdk.Stack {
public readonly userPool: cognito.UserPool;
public readonly userPoolClient: cognito.UserPoolClient;
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Create Cognito User Pool
this.userPool = new cognito.UserPool(this, 'UserPool', {
userPoolName: 'my-app-user-pool',
selfSignUpEnabled: true,
signInAliases: { email: true },
autoVerify: { email: true },
standardAttributes: {
givenName: { required: true, mutable: true },
familyName: { required: true, mutable: true },
},
passwordPolicy: {
minLength: 8,
requireLowercase: true,
requireUppercase: true,
requireDigits: true,
requireSymbols: true,
},
});
const userPoolGoogleProvider = new cognito.UserPoolIdentityProviderGoogle(this, 'GoogleProvider', {
userPool: this.userPool,
clientId: '...',
clientSecretValue: cdk.SecretValue.unsafePlainText('...'),
scopes: ['profile', 'email'],
attributeMapping: {
email: cognito.ProviderAttribute.GOOGLE_EMAIL,
givenName: cognito.ProviderAttribute.GOOGLE_GIVEN_NAME,
familyName: cognito.ProviderAttribute.GOOGLE_FAMILY_NAME,
},
});
this.userPool.registerIdentityProvider(userPoolGoogleProvider);
this.userPoolClient = new cognito.UserPoolClient(this, 'UserPoolClient', {
userPool: this.userPool,
generateSecret: false,
oAuth: {
callbackUrls: ['http://localhost:3000/callback'],
logoutUrls: ['http://localhost:3000'],
flows: { implicitCodeGrant: true },
scopes: [cognito.OAuthScope.OPENID, cognito.OAuthScope.EMAIL, cognito.OAuthScope.PROFILE],
},
supportedIdentityProviders: [cognito.UserPoolClientIdentityProvider.GOOGLE],
});
this.userPoolClient.node.addDependency(userPoolGoogleProvider)
new cdk.CfnOutput(this, 'UserPoolId', { value: this.userPool.userPoolId, exportName: 'UserPoolId' });
new cdk.CfnOutput(this, 'UserPoolClientId', { value: this.userPoolClient.userPoolClientId, exportName: 'UserPoolClientId' });
}
}
lambda-cdk-stack.ts
export class LambdaCdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const userPoolId = cdk.Fn.importValue('UserPoolId');
const helloFunction = new PythonFunction(this, 'Hello', {
functionName: 'hello',
entry: 'lambda',
runtime: lambda.Runtime.PYTHON_3_12,
index: 'index.py',
handler: 'handler',
timeout: cdk.Duration.seconds(900),
memorySize: 2048,
});
// Create API Gateway
const api = new apigateway.RestApi(this, 'HelloApi', {
restApiName: 'Hello World'
});
const importedUserPool = cognito.UserPool.fromUserPoolId(this, 'ImportedUserPool', userPoolId);
const authorizer = new apigateway.CognitoUserPoolsAuthorizer(this, 'CognitoAuthorizer', {
cognitoUserPools: [importedUserPool],
});
// Create a protected resource
const protectedResource = api.root.addResource('protected');
protectedResource.addMethod('GET', new apigateway.LambdaIntegration(helloFunction), {
authorizer: authorizer,
authorizationType: apigateway.AuthorizationType.COGNITO,
});
// Add cors support
api.root.addCorsPreflight({
allowOrigins: ['*'],
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowHeaders: ['Content-Type', 'Authorization', 'X-Amz-Date', 'X-Api-Key', 'X-Amz-Security-Token', 'X-Amz-User-Agent'],
allowCredentials: true,
});
// Output the API URL
new cdk.CfnOutput(this, 'ApiUrl', {value: api.url});
}
}
To authenticate via Postman I call
https://<myPoolName>.us-west-2.amazoncognito.com/oauth2/authorize?response_type=code&client_id=<myClientId>&redirect_uri=http://localhost:3000/callback&scope=openid+profile+email
And this returns me a code which looks like 17080341-c72b-4ad5-843c-8gb7bb740442
(example not real) then using the code I exchange it for a cognito token via
curl --location 'https://<myPoolDomain>.auth.us-west-2.amazoncognito.com/oauth2/token'
--header 'Content-Type: application/x-www-form-urlencoded'
--header 'Cookie: XSRF-TOKEN=<...>'
--data-urlencode 'grant_type=authorization_code'
--data-urlencode 'client_id=<...>'
--data-urlencode 'code=17080341-c72b-4ad5-843c-8gb7bb740442'
--data-urlencode 'redirect_uri=http://localhost:3000/callback'
And that works which gets me back
{
"id_token": "ey...",
"access_token": "ey...",
"refresh_token": "ey...",
"expires_in": 3600,
"token_type": "Bearer"
}
Finally I take the access_token and set that as Bearer token in the request to the lambda before getting Forbidden
.
Where did I go wrong here?