AWS API gateway Cognito - How to fetch from react app - amazon-web-services

I try to add Cognito auth to an react app which calls an API gateway, too. I made it to have auth in the react app with:
export default withAuthenticator(App);
But now I in addition want to make the API gateway secure with the same login.
Currently I get this error:
Access to fetch at '.../' from origin 'http://localhost:3000' has been
blocked by CORS policy: Request header field
access-control-allow-origin is not allowed by
Access-Control-Allow-Headers in preflight response.
Tested different things I found online, but actually I haven't found what exactly I need to send as Authorization (Is token.sessionToken the right thing?) nor what the header needs in addition. The API call looks like this currently.
callAPI = async (url) => {
let token = await Amplify.Auth.currentCredentials();
console.log(token.sessionToken)
const apiurl = 'https...?url=' + url
const response = await fetch(apiurl, {
method: 'GET',
mode: 'cors',
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH',
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, Authorization',
Authorization: token.sessionToken
},
})
const data = await response.json();
Serverless of the API gateway + corresponding lambda:
resources:
...
Resources:
...
CognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: ${self:service}_user_pool
MfaConfiguration: OFF
UsernameAttributes:
- email
Policies:
PasswordPolicy:
MinimumLength: 8
RequireLowercase: False
RequireNumbers: False
RequireSymbols: False
RequireUppercase: False
CognitoUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
ClientName: ${self:service}_client
UserPoolId:
Ref: CognitoUserPool
ApiGatewayAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
Name: CognitoUserPool
Type: COGNITO_USER_POOLS
IdentitySource: method.request.header.Authorization
RestApiId:
Ref: ApiGatewayRestApi
ProviderARNs:
- Fn::GetAtt:
- CognitoUserPool
- Arn
CognitoIdentityPool:
Type: AWS::Cognito::IdentityPool
Properties:
IdentityPoolName: ${self:service}_identity_pool
AllowUnauthenticatedIdentities: false
CognitoIdentityProviders:
- ClientId:
Ref: CognitoUserPoolClient
ProviderName:
Fn::GetAtt: [CognitoUserPool, ProviderName]
CognitoIdentityPoolRoles:
Type: AWS::Cognito::IdentityPoolRoleAttachment
Properties:
IdentityPoolId:
Ref: CognitoIdentityPool
Roles:
authenticated:
Fn::GetAtt: [CognitoAuthRole, Arn]
unauthenticated:
Fn::GetAtt: [CognitoUnauthRole, Arn]
CognitoAuthRole:
Type: AWS::IAM::Role
Properties:
RoleName: appAuthRole
Path: /
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Federated: "cognito-identity.amazonaws.com"
Action:
- "sts:AssumeRoleWithWebIdentity"
Condition:
StringEquals:
"cognito-identity.amazonaws.com:aud":
Ref: CognitoIdentityPool
"ForAnyValue:StringLike":
"cognito-identity.amazonaws.com:amr": authenticated
Policies:
- PolicyName: "CognitoAuthorizedPolicy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "mobileanalytics:PutEvents"
- "cognito-sync:*"
- "cognito-identity:*"
Resource: "*"
- Effect: "Allow"
Action:
- "execute-api:Invoke"
Resource: "*"
CognitoUnauthRole:
Type: AWS::IAM::Role
Properties:
RoleName: appUnauthRole
Path: /
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Federated: "cognito-identity.amazonaws.com"
Action:
- "sts:AssumeRoleWithWebIdentity"
Condition:
StringEquals:
"cognito-identity.amazonaws.com:aud":
Ref: CognitoIdentityPool
"ForAnyValue:StringLike":
"cognito-identity.amazonaws.com:amr": unauthenticated
Policies:
- PolicyName: "CognitoUnauthorizedPolicy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "mobileanalytics:PutEvents"
- "cognito-sync:*"
- "cognito-identity:*"
Resource: "*"
...
functions:
ampstoryscreenshotsStep1:
handler: src/index.ampstoryscreenshotsStep1
events:
- http:
path: '{proxy+}'
method: get
cors: true
integration: lambda
authorizer:
type: COGNITO_USER_POOLS
authorizerId:
Ref: ApiGatewayAuthorizer
Would also be interesting to learn how to test this with postman?

Ok after checking docs + some test this is my solution. This creates cognito user pool, identity pool and the auth for the api gateway
Serverless:
resources:
Resources:
...
CognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: ${self:service}_user_pool
MfaConfiguration: OFF
Policies:
PasswordPolicy:
MinimumLength: 8
RequireLowercase: False
RequireNumbers: False
RequireSymbols: False
RequireUppercase: False
AutoVerifiedAttributes:
- email
CognitoUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
ClientName: ${self:service}_client
UserPoolId:
Ref: CognitoUserPool
CognitoIdentityPool:
Type: AWS::Cognito::IdentityPool
Properties:
IdentityPoolName: ${self:service}_identity_pool
AllowUnauthenticatedIdentities: false
CognitoIdentityProviders:
- ClientId:
Ref: CognitoUserPoolClient
ProviderName:
Fn::GetAtt: [CognitoUserPool, ProviderName]
CognitoIdentityPoolRoles:
Type: AWS::Cognito::IdentityPoolRoleAttachment
Properties:
IdentityPoolId:
Ref: CognitoIdentityPool
Roles:
authenticated:
Fn::GetAtt: [CognitoAuthRole, Arn]
unauthenticated:
Fn::GetAtt: [CognitoUnauthRole, Arn]
CognitoAuthRole:
Type: AWS::IAM::Role
Properties:
RoleName: appAuthRole
Path: /
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Federated: "cognito-identity.amazonaws.com"
Action:
- "sts:AssumeRoleWithWebIdentity"
Condition:
StringEquals:
"cognito-identity.amazonaws.com:aud":
Ref: CognitoIdentityPool
"ForAnyValue:StringLike":
"cognito-identity.amazonaws.com:amr": authenticated
Policies:
- PolicyName: "CognitoAuthorizedPolicy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "mobileanalytics:PutEvents"
- "cognito-sync:*"
- "cognito-identity:*"
Resource: "*"
- Effect: "Allow"
Action:
- "execute-api:Invoke"
Resource: "*"
CognitoUnauthRole:
Type: AWS::IAM::Role
Properties:
RoleName: appUnauthRole
Path: /
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Federated: "cognito-identity.amazonaws.com"
Action:
- "sts:AssumeRoleWithWebIdentity"
Condition:
StringEquals:
"cognito-identity.amazonaws.com:aud":
Ref: CognitoIdentityPool
"ForAnyValue:StringLike":
"cognito-identity.amazonaws.com:amr": unauthenticated
Policies:
- PolicyName: "CognitoUnauthorizedPolicy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "mobileanalytics:PutEvents"
- "cognito-sync:*"
- "cognito-identity:*"
Resource: "*"
GatewayResponseDefault4XX:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
gatewayresponse.header.Access-Control-Allow-Methods: "'GET,PUT,OPTIONS'"
ResponseType: DEFAULT_4XX
RestApiId:
Ref: 'ApiGatewayRestApi'
GatewayResponseDefault5XX:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
gatewayresponse.header.Access-Control-Allow-Methods: "'GET,PUT,OPTIONS'"
ResponseType: DEFAULT_5XX
RestApiId:
Ref: 'ApiGatewayRestApi'
ApiGatewayAuthorizer:
DependsOn:
- ApiGatewayRestApi
Type: AWS::ApiGateway::Authorizer
Properties:
Name: cognito-authorizer
IdentitySource: method.request.header.Authorization
RestApiId:
Ref: ApiGatewayRestApi
Type: COGNITO_USER_POOLS
ProviderARNs:
- Fn::GetAtt: [CognitoUserPool, Arn]
functions:
ampstoryscreenshotsStep1:
handler: src/index.ampstoryscreenshotsStep1
events:
- http:
path: '{proxy+}'
method: get
cors: true
authorizer:
type: COGNITO_USER_POOLS
authorizerId:
Ref: ApiGatewayAuthorizer
The order here is important.
I query like this:
callAPI = async (url) => {
const user = await Amplify.Auth.currentAuthenticatedUser();
const token = user.signInUserSession.idToken.jwtToken;
const apiurl = config.apiGateway.URL + '/takescreenshots/?url=' + url
console.log(apiurl)
const response = await fetch(apiurl, {
method: 'GET',
headers: {
Authorization: token
},
})
const data = await response.json();
which just works if your signed in.

Related

Getting Cloudwatch to send CreateLogGroup messages to EventBridge

I want CloudWatch to send CreateLogGroup messages to EventBridge.
I understand this is possible, but also that Cloudwatch doesn't seem to send these messages by default. It seems you have to configure CloudTrail to get it to forward the message. But I can't find a CloudTrail configuration which works - in general deployment fails with:
AWS::CloudTrail::Trail - "Invalid request provided: Incorrect S3 bucket policy is detected for bucket"
AWSTemplateFormatVersion: '2010-09-09'
Outputs:
HelloFunction:
Value:
Ref: HelloFunction
WatcherFunction:
Value:
Ref: WatcherFunction
WatcherTrailBucket:
Value:
Ref: WatcherTrailBucket
Parameters:
MemorySizeDefault:
Default: '512'
Type: String
RuntimeVersion:
Default: '3.8'
Type: String
TimeoutDefault:
Default: '5'
Type: String
Resources:
HelloFunction:
Properties:
Code:
ZipFile: |
def handler(event, context):
print (event)
Handler: index.handler
MemorySize:
Ref: MemorySizeDefault
Role:
Fn::GetAtt:
- HelloFunctionRole
- Arn
Runtime:
Fn::Sub: python${RuntimeVersion}
Timeout:
Ref: TimeoutDefault
Type: AWS::Lambda::Function
HelloFunctionRole:
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Version: '2012-10-17'
Policies:
- PolicyDocument:
Statement:
- Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Effect: Allow
Resource: '*'
Version: '2012-10-17'
PolicyName:
Fn::Sub: hello-function-role-policy-${AWS::StackName}
Type: AWS::IAM::Role
WatcherFunction:
Properties:
Code:
ZipFile: |
def handler(event, context):
print (event)
Handler: index.handler
MemorySize:
Ref: MemorySizeDefault
Role:
Fn::GetAtt:
- WatcherFunctionRole
- Arn
Runtime:
Fn::Sub: python${RuntimeVersion}
Timeout:
Ref: TimeoutDefault
Type: AWS::Lambda::Function
WatcherFunctionRole:
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Version: '2012-10-17'
Policies:
- PolicyDocument:
Statement:
- Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Effect: Allow
Resource: '*'
Version: '2012-10-17'
PolicyName:
Fn::Sub: watcher-function-role-policy-${AWS::StackName}
Type: AWS::IAM::Role
WatcherEventRule:
Type: AWS::Events::Rule
Properties:
EventPattern:
source:
- aws.logs
detail-type:
- "AWS API Call via CloudTrail"
detail:
eventName:
- CreateLogGroup
Targets:
- Id:
Fn::Sub: watcher-event-rule-${AWS::StackName}
Arn:
Fn::GetAtt:
- WatcherFunction
- Arn
State: ENABLED
WatcherEventRulePermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
Principal: events.amazonaws.com
FunctionName:
Ref: WatcherFunction
SourceArn:
Fn::GetAtt:
- WatcherEventRule
- Arn
WatcherTrailBucket:
Type: AWS::S3::Bucket
WatcherTrailBucketPolicy:
DependsOn:
- WatcherTrailBucket
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: WatcherTrailBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: cloudtrail.amazonaws.com
Action: s3:GetBucketAcl
Resource: "*"
Condition: {}
- Effect: Allow
Principal:
Service: cloudtrail.amazonaws.com
Action: s3:PutObject
Resource: "*"
Condition:
StringEquals:
s3:x-amz-acl: bucket-owner-full-control
WatcherTrail:
Type: AWS::CloudTrail::Trail
Properties:
EventSelectors:
- ReadWriteType: All
IsLogging: true
S3BucketName:
Ref: WatcherTrailBucket
IsLogging: true
S3KeyPrefix: logs/
Your WatcherTrail runs before WatcherTrailBucketPolicy, so that's why it fails (CloudFormation does not deploy resource in the order of their definition in the template). Add explicit DependsOn dependency on the bucket policy. Also your WatcherTrailBucketPolicy is incorrect, and trial needs a name. So it should be:
WatcherTrailBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: WatcherTrailBucket
PolicyDocument: !Sub |
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSCloudTrailAclCheck20150319",
"Effect": "Allow",
"Principal": {"Service": "cloudtrail.amazonaws.com"},
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::${WatcherTrailBucket}",
"Condition": {
"StringEquals": {
"aws:SourceArn": "arn:aws:cloudtrail:${AWS::Region}:${AWS::AccountId}:trail/MyTrial"
}
}
},
{
"Sid": "AWSCloudTrailWrite20150319",
"Effect": "Allow",
"Principal": {"Service": "cloudtrail.amazonaws.com"},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::${WatcherTrailBucket}/logs/AWSLogs/${AWS::AccountId}/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control",
"aws:SourceArn": "arn:aws:cloudtrail:${AWS::Region}:${AWS::AccountId}:trail/MyTrial"
}
}
}
]
}
WatcherTrail:
Type: AWS::CloudTrail::Trail
DependsOn: WatcherTrailBucketPolicy
Properties:
TrailName: MyTrial
EventSelectors:
- ReadWriteType: All
IsLogging: true
S3BucketName:
Ref: WatcherTrailBucket
IsLogging: true
S3KeyPrefix: logs/

Lambda not assuming role in AWS serverless framework from Cognito User Pool. What am I doing wrong?

I'm trying to create a admin API using api gateway, lambda and dynamoDB. I want only the logged in users in certain group be able to access lambda.
I see that the lambda would not assume role of the cognito user and access the dynamoDB. API only works if I attach the dynamoDB policy to the LambdaRole.
Is there a way for this to work dynamically based on user groups. Do I need to add this assumeRole logic in the lambda handler itself ? Please help
This is the ERROR I get when I git my API from postman with Authorization tokens
{
"message": "User: arn:aws:sts::09723XXXXX357:assumed-role/tango-admin-lambda-role/tango-admin-service-dev-getActiveOrders is not authorized to perform: dynamodb:Query on resource: arn:aws:dynamodb:us-east-1:09723XXXXX357:table/dev-tango-service-tango-order-db",
"code": "AccessDeniedException",
"time": "2022-05-23T18:42:39.241Z",
"requestId": "DDK7AS6SBO8D0U3KXXXXXXXX",
"statusCode": 400,
"retryable": false,
"retryDelay": 46.2351059972586
}
Here is my serverless.yml file
service: tango-admin-service
provider:
name: aws
runtime: nodejs14.x
region: us-east-1
environment:
DYNAMODB_TABLE: ${opt:stage, self:provider.stage}-tango-service-tango-order-db
# functions
functions:
hello:
handler: src/handlers/hello.handler
events:
- http:
path: hello
method: get
getActiveOrders:
handler: src/handlers/getActiveOrders.handler
role: TangoAdminLambdaRole # How can this role be assume from Congito ? It only works if I directly pass the Role with DynamoDB Policy
events: # The events that trigger this function
- http:
path: get-active-orders
method: post
cors: true
authorizer:
type: COGNITO_USER_POOLS
authorizerId:
Ref: TangoAdminApiAuthorizer
# Serverless plugins
plugins:
- serverless-plugin-typescript
- serverless-offline
resources:
Resources:
TangoAdminCognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: TangoAdmin
TangoAdminCognitoUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
ClientName: TangoAdminWebApp
GenerateSecret: false
UserPoolId:
Ref: "TangoAdminCognitoUserPool"
TangoAdminGroup:
Type: AWS::Cognito::UserPoolGroup
Properties:
GroupName: "TangoAdmin"
Description: "Admin group for Tango"
Precedence: 0
RoleArn: !GetAtt CognitoAdminIAMRole.Arn
UserPoolId:
Ref: "TangoAdminCognitoUserPool"
CognitoAdminIAMRole:
Type: AWS::IAM::Role
Properties:
RoleName: "tango-admin-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Federated:
- "cognito-identity.amazonaws.com"
Action:
- "sts:AssumeRoleWithWebIdentity"
Policies:
- PolicyName: "tango-admin-group-policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "dynamodb:*"
Resource:
- "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}"
TangoAdminApiAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
Name: TangoAdmin
RestApiId:
Ref: ApiGatewayRestApi
Type: COGNITO_USER_POOLS
ProviderARNs:
- Fn::GetAtt: [ TangoAdminCognitoUserPool, Arn ]
IdentitySource: method.request.header.Authorization
TangoAdminLambdaRole:
Type: AWS::IAM::Role
Properties:
RoleName: "tango-admin-lambda-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action:
- "sts:AssumeRole"
Outputs:
TangoAdminCognitoUserPoolId:
Description: "Tango Admin Cognito User Pool ID"
Value:
Ref: "TangoAdminCognitoUserPool"
TangoAdminCognitoUserPoolClientId:
Description: "Tango Admin Cognito User Pool Client ID"
Value:
Ref: "TangoAdminCognitoUserPoolClient"

AccessDeniedException when testing GET method in AWS API Gateway with HttpMethod POST

I want to implement a GET method in AWS API Gateway that returns the messages from a AWS SQS. When I test it I get an exception:
<AccessDeniedException>
<Message>Unable to determine service/operation name to be authorized</Message>
</AccessDeniedException>
I have all the stack defined in a Serverless yml file:
functions:
listExportJob:
handler: src/listExportJob.handler
role: listExportJobIAM
environment:
processingqueueUrl: https://xxxxx/processing-exports-queue-eu-local
events:
- processingsqs:
arn: arn:aws:sqs:xxxxx:processing-exports-queue-eu-local
events:
- sqs:ChangeMessageVisibility
- sqs:ChangeMessageVisibilityBatch
- sqs:GetQueueAttributes
- sqs:ReceiveMessage
resources:
Resources:
processingSQSQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: processing-exports-queue-eu-local
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: ApiGateway
listExportAPIResource:
Type: "AWS::ApiGateway::Resource"
Properties:
ParentId:
Fn::GetAtt:
- "ApiGatewayRestApi"
- "RootResourceId"
PathPart: "listExport"
RestApiId:
Ref: ApiGatewayRestApi
listExportAPIMethod:
Type: AWS::ApiGateway::Method
DependsOn: processingSQSQueue
Properties:
RestApiId:
Ref: ApiGatewayRestApi
ResourceId:
Ref: listExportAPIResource
HttpMethod: "GET"
MethodResponses:
- StatusCode: "200"
ResponseParameters:
"method.response.header.Access-Control-Allow-Origin": true
AuthorizationType: "NONE"
Integration:
Type: AWS
Credentials:
Fn::GetAtt: [ "APIGatewaySQSIAM", "Arn" ]
IntegrationHttpMethod: POST
IntegrationResponses:
- StatusCode: "200"
ResponseParameters:
"method.response.header.Access-Control-Allow-Origin": "'*'"
ResponseTemplates:
"application/json": ""
Uri: arn:aws:apigateway:xxxxx/processing-exports-queue-eu-local
APIGatewaySQSIAM:
Type: AWS::IAM::Role
Properties:
Path: /app/all/
RoleName: APIGSQSRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- apigateway.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: APIGATEWAYIAMAll
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Resource: "*"
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- Effect: Allow
Resource:
- "*"
Action:
- "sqs:SendMessage"
listExportJobIAM:
Type: AWS::IAM::Role
Properties:
RoleName: listExportJobRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: listExportJobIAMAll
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
- logs:CreateLogGroup
Resource: '*'
- Effect: Allow
Action:
- sqs:ChangeMessageVisibility
- sqs:ChangeMessageVisibilityBatch
- sqs:GetQueueAttributes
- sqs:ReceiveMessage
Resource: arn:aws:sqs:xxxxx:processing-exports-queue-eu-local
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource: '*'
I have defined the GET method with IntegrationHttpMethod POST and I can't see what's wrong with my implementation.
AWS API Gateway has another method that posts a message directly to AWS SQS and works properly.
Finally it works using Lambda-Proxy integration adding a http event to the function:
events:
- http:
path: listExports
method: get
cors: true

CloudFormation template for Cognito gets NotAuthorizedException error

I am having trouble creating a stack with my CloudFormation template. When it reaches the IdentityPoolRoleAttachment I get the error:
Access to Role 'CognitoIdentityUnauthenticated' is forbidden. (Service: AmazonCognitoIdentity; Status Code: 400; Error Code: NotAuthorizedException; Request ID: 0866b3a1-15ab-11e9-82dd-1b30dc4a6a4d)
I've matched everything to what was created through the console and cannot figure out the problem. Can anyone see what I might be missing?
AWSTemplateFormatVersion: 2010-09-09
Description: AWS Script to create Cognito User and Identity pools
Resources:
CognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: cognito-userpool
Schema:
- Name: email
AttributeDataType: String
Mutable: false
Required: true
Policies:
PasswordPolicy:
MinimumLength: 8
RequireLowercase: true
RequireNumbers: true
RequireUppercase: true
CognitoUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
ClientName: cognito-app-client
ExplicitAuthFlows:
- ADMIN_NO_SRP_AUTH
GenerateSecret: false
ReadAttributes:
- email
RefreshTokenValidity: 30
UserPoolId: !Ref CognitoUserPool
CognitoIdentityPool:
Type: AWS::Cognito::IdentityPool
Properties:
IdentityPoolName: cognito_identity_pool
AllowUnauthenticatedIdentities: true
CognitoIdentityProviders:
- ClientId:
Ref: CognitoUserPoolClient
ProviderName:
Fn::GetAtt:
- CognitoUserPool
- ProviderName
CognitoIdentityAuthenticatedRole:
Type: AWS::IAM::Role
Properties:
RoleName: CognitoIdentityAuthenticated
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Federated: cognito-identity.amazonaws.com
Action: sts:AssumeRoleWithWebIdentity
Condition:
StringEquals:
"cognito-identity.amazonaws.com:aud":
Ref: CognitoIdentityPool
"ForAnyValue:StringLike":
"cognito-identity.amazonaws.com:amr": authenticated
CognitoIdentityUnauthenticatedRole:
Type: AWS::IAM::Role
Properties:
RoleName: CognitoIdentityUnauthenticated
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Federated: cognito-identity.amazonaws.com
Action: sts:AssumeRoleWithWebIdentity
Condition:
StringEquals:
"cognito-identity.amazonaws.com:aud":
Ref: CognitoIdentityPool
"ForAnyValue:StringLike":
"cognito-identity.amazonaws.com:amr": unauthenticated
# Create policy to allow authenticated user role to invode API lambda function
CognitoAuthInvokeLambdaPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: CognitoAuthInvokeLambdaPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- mobileanalytics:PutEvents
- cognito-sync:*
- cognito-identity:*
Resource:
- "*"
- Effect: Allow
Action:
- execute-api:Invoke
Resource:
- "arn:aws:execute-api:us-east-1:*:xxxxxxxxxx/*"
- Effect: Allow
Action:
- "s3:*"
Resource:
- "*"
Roles:
- Ref: CognitoIdentityAuthenticatedRole
# Create policy to allow unauthenticated user role (currently only has access to Cognito - add additional resources if needed)
CognitoUnauthPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: CognitoUnauthPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- mobileanalytics:PutEvents
- cognito-sync:*
- cognito-identity:*
Resource:
- "*"
Roles:
- Ref: CognitoIdentityUnauthenticatedRole
# Assigns the role to the Identity Pool
CognitoIdentityPoolRoleMapping:
Type: "AWS::Cognito::IdentityPoolRoleAttachment"
Properties:
IdentityPoolId: !Ref CognitoIdentityPool
Roles:
authenticated: !Ref CognitoIdentityAuthenticatedRole
unauthenticated: !Ref CognitoIdentityUnauthenticatedRole
#Output IDs and ARNs
Outputs:
CognitoUserPoolID:
Value:
Ref: CognitoUserPool
Export:
Name: !Sub "${AWS::StackName}-CognitoUserPoolID"
CognitoUserPoolClientID:
Value:
Ref: CognitoUserPoolClient
Export:
Name: !Sub "${AWS::StackName}-CognitoUserPoolClientID"
CognitoIdentityPoolID:
Value:
Ref: CognitoIdentityPool
Export:
Name: !Sub "${AWS::StackName}-CognitoIdentityPoolID"
I see a similar question posted but no answer was provided: Access to Role 'cognito role' is forbidden
I figured it out. The unauthenticated role needs the Arn and not the logical ID.
CognitoIdentityPoolRoleMapping:
Type: "AWS::Cognito::IdentityPoolRoleAttachment"
Properties:
IdentityPoolId: !Ref CognitoIdentityPool
Roles:
authenticated: !GetAtt CognitoIdentityAuthenticatedRole.Arn
unauthenticated: !GetAtt CognitoIdentityUnauthenticatedRole.Arn

How to integrate API Gateway with SQS

Just like in the title. I try to integrate API Gateway method with a SQS using cloud formation. What I am missing is the correct URI for the SQS. If any of you already did that, what should the URI look like?
I came up with something like that, but have no idea where to put the SQS ARN
"arn:aws:apigateway:${AWS::Region}:sqs:action/SendMessage"
Here is the full configuration for the method:
PostMethod:
Type: "AWS::ApiGateway::Method"
Properties:
ApiKeyRequired: "true"
HttpMethod: "POST"
ResourceId: !Ref "SomeResource"
RestApiId: !Ref "SomeRestApi"
Integration:
IntegrationHttpMethod: "POST"
IntegrationResponses:
- StatusCode: 200
Type: "AWS"
Uri: "arn:aws:apigateway:${AWS::Region}:sqs:action/SendMessage"
And here is an example of URI if you integrate with a lambda function:
arn:aws:apigateway:us-west-2:lambda:path//2015-03-31/functions/arn:aws:lambda:us-west-2:123412341234:function:function_name/invocations
-
To answer my own question. Here is how you integrate SQS as a Service Proxy in API Gateway:
PostMethod:
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: "NONE"
ApiKeyRequired: "true"
HttpMethod: "POST"
ResourceId: !Ref "SomeResource"
RestApiId: !Ref "RestApi"
MethodResponses:
- StatusCode: 200
Integration:
Credentials: !GetAtt "RestApiRole.Arn"
IntegrationHttpMethod: "POST"
IntegrationResponses:
- StatusCode: 200
Type: "AWS"
Uri: !Sub "arn:aws:apigateway:${AWS::Region}:sqs:action/SendMessage"
RequestParameters:
integration.request.querystring.QueueUrl: !Sub "'${SomeQueue}'"
integration.request.querystring.MessageBody: "method.request.body"
I've finally found all answers to my questions in various documentation. RTFM I guess.
EDIT:
and here the code for RestApiRole:
RestApiRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "sts:AssumeRole"
Principal:
Service:
- "apigateway.amazonaws.com"
Effect: "Allow"
Policies:
- PolicyName: "InvokeLambda"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "lambda:InvokeFunction"
Resource: !GetAtt "LambdaFunction.Arn"
Effect: "Allow"
I'm pretty sure the SQS role and policy should look more like this (you seem to have pasted the lambda role instead):
SQSRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- apigateway.amazonaws.com
Action: sts:AssumeRole
Path: /
SQSRolePolicy:
Type: AWS::IAM::Policy
DependsOn: [SQSRole]
Description: IAM policy applied to the service role.
Properties:
PolicyName: send-messages-sqs
PolicyDocument:
Statement:
- Action:
- sqs:SendMessage
Resource:
- !Sub arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:QUEUE_NAME
Effect: Allow
Roles: [!Ref SQSRole]