I've successfully configured IAM-authenticated access to my Lambda function with AWS API Gateway front-end, but unable to find how to pass IAM user identity to my Lambda function.
I need exactly IAM user identity and can not run Lambda function under calling IAM-user credentials.
All I need - is to get calling IAM-user identity in my Lambda function.
Is there option for that?
Support for accessing identity and other information from the Amazon API Gateway request context hadn't been available when you posted the question, but recently been added, see Announcement: Context Variables:
You can now access context variables from within mapping templates to retrieve contextual information about the API call. You can access data such as stage, resource path, and HTTP method, as well as information about the identity of the caller. This information can then be passed along to your backend integration using the $context variable. [emphasis mine]
The referenced documentation on Accessing the $context Variable features a $context Variable Reference and there are various $context.identity.* parameters that should address your use case.
Cognito Identity
As outlined in Soenke's answer to the OPs similar question in the Amazon API Gateway forum, there is an as of yet undocumented integration parameter that results in the Cognito identifier being included in this $context.identity.* context variables:
in order to have the Cognito (not IAM!) IdentityId and IdentityPoolId
available in Lambda, you have to enable "Invoke with caller
credentials" on the API Gateway "Integration Request" page of the API
GW Resource. This results in a new context struct "identity"
(containing "cognitoIdentityId" and "cognitoIdentityPoolId" being
passed to the Lambda function).
You can use Cognito with a "public" pool id, then attach role to the Cognito pool id, the role being accessing your Lambda, I think it is called InvokeLambdaRole or something
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'REGION:YOUR_POOL_ID',
});
Use AWS STS to get temporary credentials with limited privileges. After that you can use API Gateway with AWS_IAM authentication, then end point will invoke the Lambda methods for you. Or you can invoke lambda directly with the credentials you got, but then again you have to attache the right roles for the identity pool you created.
NB: Put strictly minimum roles on your pole, that is a publicly available id, every body can use it to get a temporary or a fixed (to track users accross devices) user_/app_ id.
Related
I'm creating an Axios call to an API hosted on APIGateway with no Authorizer etc. I'm able to access the lambda via a direct postman request and also on the service lambda when it's ran in offline mode, such as:
https://localhost:3000/my/api
However when i run it on the dev stage:
12345.execute-api.eu-west-1.amazonaws.com/my/api
I'm getting a a 403: Forbidden error thrown back.
I've seen comments from other posts where they needed to append the staging environment at the end of the request but this isn't the case in this instance as it's just creating a default endpoint and all other lambdas within this service can be hit when ran on dev, it's just this one that makes a call to another APIGateway API.
The calling API is behind an authorizer with a wildcard policy so should allow all traffic and I'd like to reiterate, it works on both localhost and a direct call to the invoked api.
I'm wondering if it's something to do with the policies attached to it but I've set them all to be wildcarded as well so it should allow everything.
Any ideas would be really helpful, I've been wracking my brains over this all day.
Edit: The authorizer has no policy denying access to the API, same as the resource policy.
There are two common reasons why an API Gateway REST API with a Lambda authorizer returns a 403 error:
The Lambda authorizer function returns an AWS Identity and Access Management (IAM) policy document that explicitly denies access to the caller.
https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-lambda-authorizer-output.html
The second reason will be, The API has an attached resource policy that explicitly denies access to the caller.
https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-resource-policies.html
If both are in place, please update the question with those details.
I'm rather new to AWS Cognito and AWS Lambda. So far I've played around with Serverless and deployed my REST API via AWS Lambda. However, I would like to make my API available for several external parties. As this is service to service, there is no end user directly calling my API. I make the API available for other businesses which use it in their applications. All functionalities exposed via the API are rather simple, meaning they are not invoking any other AWS services like a Dynamo DB etc.
I have several questions, some are rather high-level others are quite specific to the setup of AWS Cognito. I decided to put all in one post as it makes it much more self-contained and easier understandable.
1. Question, Cognito vs API key: What is the advantage of using AWS Cognito vs AWS Lambda in combination with restricting the access via an API key and IP-Whitelisting? Is one much more secure than other?
Let's assume I want to use AWS Cognito. As this is a service to service case, I've read that the standard is to use token endpoints where the grant_type is client_credential. I've found the following on medium. The first few steps consist of
Create a user pool in AWS Cognito.
Create an App client
Add Resource Servers
Enable the client credentials checkbox for Allower OAuth flows
2. Question, App client: Within the added App client I find the corresponding App client id and App client secret. If I expose my API to several different parties, do I need to add for each party another App client? Otherwise they use all the same credentials. Is this the right way to do it?
3. Question, Resource Server: Here I'm completely stuck. What exactly is the meaning for this? At then end, all I want is that my clients can do a post request against my API and access is granted through Cognito. Some clarification what this is for and how this applies in my case would be appreciated. More than happy to share more insights if needed.
The next part is to configure Cognito Authorizer for the API Gateway. This should be fine.
4. Question, client workflow Regarding my clients workflow. Am I correct that this consist of the following steps:
First, I provide to the client his client_id and client_secret.
then the client implements on his side the following workflow:
Whenever he wants to use my API exposed via API Gateway, he first uses his provided client_id and client_secret to retrieve his bearer token.
He uses this bearer token to make a request to API Gateway with the bearer token in the Authorization header.
If access granted, the client retrieves the output of my API.
Is this correct or am I missing anything?
1-Question, Cognito vs API key
Well as you can see here.
Amazon Cognito user pools let you create customizable authentication
and authorization solutions for your REST APIs.
Usage plans let you provide API keys to your customers — and then
track and limit usage of your API stages and methods for each API key
So the purpose is different, the API Key is used basically to count a customer usage while AWS Cognito is there to authenticate/authorize calls to your API.
Let us say, you have a requirement that a trail user can't call your API more than 100 times.
Then using AWS Cognito, you will allow a user to sign up, also you will provide the same user with an API Key, you will identify the source of the calls using Cognito and with each call API Gateway will decrease the limit assigned to user's API Key by 1.
And you will have the following cases:
When token (obtained through login using username and password), and
API Key are valid, then the call will be successful.
When token is wrong/missing, your caller will get 401 status code.
When API Key is wrong/missing, your caller will get 403 status code.
When API Key is correct but limit is exceeded, your caller will get
429 status code.
2. Question, App client:
Well, client id and client secrets are meant to identify a trusted client (app) rather than a user and each app should have its own client id, so if the caller is an application not a user so yes, create a client id for each separate app. Please be aware that client secrets must be kept confidential so if the caller app can't achieve that, such as single-page Javascript apps or native apps, then the secret shouldn't be issued.
3. Question, Resource Server:
It is your API server.
Check this page.
The resource server is the OAuth 2.0 term for your API server. The
resource server handles authenticated requests after the application
has obtained an access token.
Large scale deployments may have more than one resource server.
Google’s services, for example, have dozens of resource servers, such
as the Google Cloud platform, Google Maps, Google Drive, Youtube,
Google+, and many others. Each of these resource servers are
distinctly separate, but they all share the same authorization server.
4. Question, client workflow
Check Client credentials grant in here
The steps for the process are as follows:
An app makes a POST request to https://AUTH_DOMAIN/oauth2/token, and specifies the following parameters:
grant_type – Set to “client_credentials” for this grant type.
client_id – The ID for the desired user pool app client.
scope – A space-separated list of scopes to request for the generated access token.
In order to indicate that the app is authorized to make the request, the Authorization header for this request is set as “Basic
BASE64(CLIENT_ID:CLIENT_SECRET)“, where
BASE64(CLIENT_ID:CLIENT_SECRET) is the base64 representation of the
app client ID and app client secret, concatenated with a colon.
The Amazon Cognito authorization server returns a JSON object with the following keys:
access_token – A valid user pool access token.
expires_in – The length of time (in seconds) that the provided access token is valid for.
token_type – Set to ” Bearer“.
Note that, for this grant type, an ID token and a refresh token aren’t returned.
The app uses the access token to make requests to an associated resource server.
The resource server validates the received token and, if everything checks out, executes the request from the app.
I'd like to generate a custom policy that provides fine grained access to DynamoDB tables inside an AWS custom authorizer. Is this possible?
In serverless, my configuration looks like this:
functions:
APIAuthorizer:
handler: src/services/auth/handlers.apiAuthorizer
cors: true
GraphQLAPI:
handler: src/services/graphql/handlers.apiHandler
events:
- http:
path: "/api"
method: post
cors: true
authorizer:
name: APIAuthorizer
type: request
resultTtlInSeconds: 0
I've verified that my custom authorizer is being called, and that various permissions it generates (sts:AssumeRole, lambda:InvokeFunction, execute-api:Invoke, and others) are required for successfully invoking the API handler. So my custom authorizer is working and the result it provides is necessary.
However, when the authorizer includes dynamodb permissions, e.g., a statement like
{ Effect: "Allow", Action: "dynamodb:", "Resource": "" }
my API handler (the GraphQLAPI function) fails with a message like
User: arn:aws:sts::<myaccountid>:assumed-role/<mydefaultrole>/myservice-mystage-GraphQLAPI is not authorized to perform: dynamodb:Query on resource: arn:aws:dynamodb:us-east-1:<myaccountId>:table/<mytable>/index/<someIndex>
(I noticed the complaint is about an index permission, so also tried adding specific permissions for that index and/or for all indexes, but this has no effect.)
The bottom line, after many different attempts, is that dynamodb permissions issued by the custom authorizer are completely ignored. My lambda node.js code is using the AWS node SDK, which should be picking up permissions from the instance environment. I assumed this would include permissions generated by the custom authorizer.
Finally, I noticed that the AWS javascript SDK documentation on how credentials are loaded says only "The execution role provides the Lambda function with the credentials it needs to run and to invoke other web services". I.e., it doesn't mention the dynamically generated credentials issued by the custom authorizer.
This seems to explain the behavior I'm seeing. My API handler only has permissions from the statically defined execution role (the error message indicates that too), and isn't granted permissions generated by the custom authorizer.
Is there anyway to use the permissions my custom authorizer generates inside my API handler?
I think you misunderstood the IAM policy output from Lambda authorizers. The purpose of the IAM policy output from the authorizer is to reflect what the outcome should be for API gateway with respect to continuing to process the request.
The policy returned is not necessarily applied to the invoked function, but applied to the invocation of the function. It merely tells API Gateway which APIs the requesting user is authorized to access.
If you wish to give the API functions being invoked specific access to resources such as DynamoDB tables, or any other AWS resources, those have to be configured in the role assigned to the Lambda function. Otherwise, you may be able to specify a role that the Lambda function to be invoked would assume that grants it additional permissions. This role can be passed via the context parameters from the authorizer.
I am fairly new to lambda and trying to wrap my head around it. I created a basic hello world function and invoked it through
aws lambda invoke
My question is
Dont I have to create an API gateway and expose the lambda function through the API gateway for it work.
How does aws lambda invoke if I have not created a gateway and exposed the function?
You do not have to necessarily create API gateway for invoking lambda function. Every lambda function is already available to be accessed via Amazon's Web Service using API:
POST /2015-03-31/functions/FunctionName/invocations?Qualifier=Qualifier HTTP/1.1
See http://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html for more details.
However, the above API expects that the request payload is signed using aws signature version 4 . The CLI call aws lambda invoke automatically takes care of that piece once you have configured valid access and secret keys.
The API Gateway in front of lambda allows you to add:
Custom resource names
Custom authentication schemes (even no authentication if desired)
Custom way of sending payload
and more...
In summary, API Gateway gives you more control over the API resource and can even abstract the user from internals of AWS API.
I have a website that uses AWS Cognito (via Amplify) for user login. The API is on a separate stack that deploys with Serverless.
I am trying to have an API endpoint that can access the current logged-in user's Cognito User Pool data (username, email) if it is available. The only way I've been able to achieve this is by using a cognito user pool authorizer via API Gateway.
Example:
functions:
getMe:
handler: /endpoints/myService.get
events:
- http:
path: /myService
method: GET
cors: true
authorizer:
type: COGNITO_USER_POOLS
authorizerId: ${self:custom.apiGatewayAuthorizerId.${self:custom.stage}}
Where authorizerId is set to the 6-character Authorizer ID found on the AWS Console's API Gateway Authorizers page. However, this blocks all traffic that is not authenticated with Cognito. That isn't what I want, since I have a number of services that should be accessible by both anonymous and logged-in users. I just want to personalize the data for users that are logged-in.
Is there any way to allow traffic and pass the cognito user parameters through the API Gateway to Lambda if they are available?
All resources I've been able to find regarding Cognito + API Gateway + Lambda are specifically about restricting access to endpoints and not layering on data to the requests...
Based on comments above you want Anonymous and Logged-in users pass through same gateway end point ?
You can still use the same setup but remove the authentication from API Gateway and take the logic in your application.
If users try to access your services while being logged in AWS amplify will send through the Authorization header with Id token to API Gateway and API Gateway will pass this header as it is to the application. You will have to check inside your application for this Authorization header and crack open Id token passed to find the user claims/attributes and do your logic. For any other user that doesn't have this token can be considered anonymous.
You still need to Validate the token if you find one in request to make sure it's a valid token and extract claims/Attributes thereafter.