I have set up an API Gateway authenticated using AWS Cognito. Once the user signs in, I use the following script to verify their credentials:
const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
const params = {
AuthFlow: 'ADMIN_NO_SRP_AUTH',
ClientId: APP_CLIENT_ID,
UserPoolId: USER_POOL_ID,
AuthParameters: {
'USERNAME': username,
'PASSWORD': password,
},
};
return cognitoidentityserviceprovider.adminInitiateAuth(params)
.promise();
And this will return a JSON like so:
{
"ChallengeParameters": {},
"AuthenticationResult": {
"AccessToken": "....",
"ExpiresIn": 3600,
"TokenType": "Bearer",
"RefreshToken": "....",
"IdToken": "...."
}
}
On the client side, I will take note of the IdToken and include it as a header with a name mentioned in the API Gateway's Authorizer.
Now, I'm trying to create a lambda function to sign the user out. So far, I've got this:
const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
const params = {
UserPoolId: USER_POOL_ID,
Username: username,
};
return cognitoidentityserviceprovider.adminUserGlobalSignOut(params)
.promise();
When I send a request to call this code, even though everything works just fine (no error is thrown), but the IdToken is still valid and I can still call authenticated requests with it. My question is, what is the proper way of signing out a user and why this is not working?
You are right. This is the current behavior of Amazon Cognito Tokens. If you do global signout than your accessToken and RefreshToken will be expired.
But your IdToken will be still valid till 1 hour.
If you call the Global SignOut again, Than you will see the message that access token is expired
I hope this helps!
Related
I can't extract the username/password pair from the request.
2022-11-30T03:11:13.958Z xxxxxxxxxxxxxxxxxxxxx INFO{
version: '1',
region: 'xxxxxxxxx',
userPoolId: 'xxxxxxxxxxxxx',
userName: 'test',
callerContext: {
awsSdkVersion: 'aws-sdk-unknown-unknown',
clientId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx'
},
triggerSource: 'PreAuthentication_Authentication',
request: { userAttributes: {}, validationData: null, userNotFound: true },
response: {}
}
Node script:
export const handler = async(event, context, callback) => {
console.log(event);
console.log(event.request);
console.log(context);
console.log(context.body);
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
Add all rights to Identity and Access Management (IAM), but unfortunately I can't see the password either, it's possible that it can't be extracted.
You (the AWS account user / role) cannot see, view, or extract the Cognito users' passwords ever, this is the purpose of Cognito as a service.
If you really want to do this you could do the signup outside of Cognito with your own system, then set the password manually for the user in Cognito with adminSetPassword.
I am trying to build a function in lambda that would get the user’s AWS credentials (in order to make an Authorization signature to request S3 resources.
event.headers.Authorization is a JWT from Cognito user pool sign in and is used in a lot of other functions and works properly.
Below in the code snippet I tried to use to get the credentials
EDIT: I can confirm that the JWT used in the Authorization header here is an ID token
console.log(event.headers.Authorization)
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'ap-northeast-2:443c5880-d302-4dcf-8cea-b1835723cdb4',
Logins: {
'cognito-idp.ap-northeast-2.amazonaws.com/ap-northeast-2_onyCNlZBF': event.headers.Authorization
}
});
console.log(AWS.config.credentials)
Below is the response , the access key and session token is missing
CognitoIdentityCredentials {
expired: true,
expireTime: null,
refreshCallbacks: [],
accessKeyId: undefined,
sessionToken: undefined,
params: {
IdentityPoolId: 'ap-northeast-2:443c5880-d302-4dcf-8cea-b1835723cdb4',
Logins: {
'cognito-idp.ap-northeast-2.amazonaws.com/ap-northeast-2_onyCNlZBF': 'eyJraWQiOiJJVlpiNkZTUUJudWlnZHRZMldrMkZuQTNXaHQ1dVNpTVhFdlVQaE0xSFdzPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI3MDdhNjQ0Ny04NzZiLTRlNDItYjc1OS0zNWIxZmI3YTQ2NzciLCJhdWQiOiIycjJjZ2V2dGkyOW9mZzg3bWJuaTZwazdwdSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJldmVudF9pZCI6ImUyNjM2ZDdkLTgzZTUtNDgwNC04ZmJjLTQwN2IyNTU3NmU3YyIsInRva2VuX3VzZSI6ImlkIiwiYXV0aF90aW1lIjoxNTkxMDY3NzI1LCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAuYXAtbm9ydGhlYXN0LTIuYW1hem9uYXdzLmNvbVwvYXAtbm9ydGhlYXN0LTJfb255Q05sWkJGIiwiY29nbml0bzp1c2VybmFtZSI6ImFsZXgiLCJleHAiOjE1OTEwNzEzMjYsImlhdCI6MTU5MTA2NzcyNiwiZW1haWwiOiJhbGV4Lnd0aG9AZ21haWwuY29tIn0.7fPwMZEVgOIroiO32bOxAyWxaBkFQco772j9i8m3LNpMx2NxW0UzlE-8J4bp6T0np6HK0MaPgg9BY0qfKjTFYWuMzf6mA7ah6aW30U7yosOyzsuK1CWz8Ksa_-QneLtMcbFVxyAZ8jWqK-TQXhS0IctPK4zehuugvymfjzC11GPcZ9sWoS3X-u2jSebUSta1pce_EEgL3rsL3XUZIxnZZiAqYw-vmFnz64ATqYa13ggsSoGYsATU5JTmO_tTut3xsitp_s7m5jCkqouzj11XvuBZDITXiZPN1ZY62jQ6Mhk9Kin1558DNxhgb2lJTcwUSr6577bVwWJsimp22ca0gw'
}
},
data: null,
_identityId: null,
_clientConfig: {}
}
I have an AWS Cognito user pool/identity pool set up to authorize a Lambda function behind API-gateway. My lambda is using the AWS SDK for Node. The login process works fine. In my function, I have an CognitoIdentityServiceProvider object that I'm using to call the getUser function. The Access Key I'm giving the function is coming from the lambda variable event.requestContext.identity.accessKey. However, the function call fails with the following error viewable through my Cloud Watch logs:
{
"message": "Invalid Access Token",
"code": "NotAuthorizedException",
"time": "2019-08-03T20:34:50.511Z",
"requestId": ...,
"statusCode": 400,
"retryable": false,
"retryDelay": 36.048605458111794
}
How is the token that is given to me in the authenticated request invalid? I'm not even sure how to debug this, or what problems I should be looking for.
An example of how I am trying to make the call in my lamdba handler:
var AWS = require('aws-sdk');
AWS.config.region = "us-west-2";
let poolData = {
UserPoolId: MY_POOL_ID,
ClientId: MY_CLIENT_ID
};
const cognito = new AWS.CognitoIdentityServiceProvider(poolData);
export async function myHandler(event, context, cb) {
var params = {
AccessToken: event.requestContext.identity.accessKey
};
cognito.getUser(params, (err, user) => {
if (err) {
console.log(JSON.stringify(err));
}
let response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": true,
},
body: { ... }
};
return cb(null, response);
});
}
I can test this API call from a React UI using AWS Amplify or a CLI test that both produce the same results in the backend. So I don't think it's necessarily how the function is being invoked. This is the method I use for CLI testing that will log in a user to the User Pool and make an API request on their behalf:
npx aws-api-gateway-cli-test \
--username='my-test-user#domain.com' \
--password='password' \
--user-pool-id='us-west-2_000000000' \
--app-client-id='00000000000000000000000' \
--cognito-region='us-west-2' \
--identity-pool-id='us-west-2:000000000-0000-0000-0000-000000000000' \
--invoke-url='https://00000000000.execute-api.us-west-2.amazonaws.com/dev' \
--api-gateway-region='us-west-2' \
--path-template='/myHandler' \
--method='GET'
The Access Token that is being passed from the API invocation has the "ASIA" prefix instead of the "AKIA" prefix if that is relevant here. I'm not sure what other info to include about my User Pool or anything else, so if that's relevant to the solution, I would be happy to provide it.
The accessKey you are passing to the getUser API is not the accessToken that it expects. event.requestContext.identity.accessKey is the IAM user access key and not the accessToken generated by AWS Cognito when user sign in. You will need to pass the JWT Access Token returned by Cognito initiateAuth API. Consider adding the access token in Authorization header when making the request.
I am having a hard time using Firebase as an Open ID Connect provider.
Can you please further describe the steps you have been through before and after to make this work?
For information, here is what I have done so far:
In AWS Console:
1 - Create an IAM Identity Provider ( OpenID Connect ) and used securetoken.google.com/<FIREBASE_PROJECT_ID> as an URL, <FIREBASE_PROJECT_ID>for Audience
2 - Checked the Thumbprint manually (it matches the one generated by AWS)
3 - Created a role with the permissions to access the desired services
4 - Created an Identity Pool in Cognito and selected my newly created role in the 'Authenticated role' Dropdown
5 - Selected my Identity Provider under the Authentication Providers > OpenID category (format is therefore): securetoken.google.com/<FIREBASE_PROJECT_ID>
In my code (I am using Vue.js) here are the logical steps I went through:
Import / setup AWS SDK
Invoke Firebase Auth service
Create a new CognitoIdentity
Use the getOpenIdTokenForDeveloperIdentity and push the tokenID received from Firebase
The issue is that I keep getting "Missing credentials in config" errors.
The code:
import axios from 'axios';
const AWS = require('aws-sdk');
AWS.config.region = 'eu-west-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'MY_COGNITO_POOL_ID',
});
export default {
name: 'My Vue.js component name',
data() {
return {
email: '',
password: '',
msg: '',
};
},
methods: {
submit() {
axios
.post(
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=MY_KEY',
{
email: this.email,
password: password,
returnSecureToken: true,
},
)
.then((res) => {
// stores tokens locally
localStorage.setItem('jwt', JSON.stringify(res.data));
const cognitoidentity = new AWS.CognitoIdentity();
const params = {
IdentityPoolId: 'MY_COGNITO_POOL_ID',
Logins: {
'securetoken.google.com/<PROJECT_ID>': res.data.idToken,
},
IdentityId: null,
TokenDuration: 3600,
};
cognitoidentity.getOpenIdTokenForDeveloperIdentity(params, (err, data) => {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
});
},
},
};
Here are the resources I have used so far while attempting to make this work:
http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html
Using Firebase OpenID Connect provider as AWS IAM Identity Provider
https://github.com/aws/amazon-cognito-identity-js/blob/master/examples/babel-webpack/src/main.jsx
http://docs.aws.amazon.com/cognitoidentity/latest/APIReference/API_GetCredentialsForIdentity.html
https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-authentication/
https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-authentication-part-2-developer-authenticated-identities/
https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-authentication-part-3-roles-and-policies/
https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-authentication-part-4-enhanced-flow/
The final code if that can be any help for anyone:
import axios from 'axios';
const AWS = require('aws-sdk');
const aws4 = require('aws4');
export default {
name: 'VUE_CPNT_NAME',
data() {
return {
email: '',
password: '',
msg: '',
idToken: '',
};
},
methods: {
submit() {
// Firebase SignIn API
// Doc: https://firebase.google.com/docs/reference/rest/auth/
axios
.post(
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=[MY_KEY]',
{
email: this.email,
password: this.password,
returnSecureToken: true,
},
)
.then((res) => {
this.idToken = res.data.idToken;
localStorage.setItem('jwt', JSON.stringify(res.data));
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'IDENTITY_POOL_ID',
Logins: {
'securetoken.google.com/<FIREBASE_PROJECT_ID>': res.data.idToken,
},
}, {
region: 'eu-west-1',
});
// AWS.config.crendentials.get() methods works as well
// or a call to cognitoidentity.getId() followed by a call to getCredentialsForIdentity()
// will achieve the same thing. Cool. But why!?
AWS.config.getCredentials((err) => {
if (err) {
console.log(err);
}
const request = {
host: 'API_GATEWAY_ENDPOINT.eu-west-1.amazonaws.com',
method: 'GET',
url: 'https://API_GATEWAY_ENDPOINT.eu-west-1.amazonaws.com/PATH',
path: '/API_ENDPOINT_PATH',
};
// Signing the requests to API Gateway when the Authorization is set AWS_IAM.
// Not required when Cognito User Pools are used
const signedRequest = aws4.sign(request,
{
secretAccessKey: AWS.config.credentials.secretAccessKey,
accessKeyId: AWS.config.credentials.accessKeyId,
sessionToken: AWS.config.credentials.sessionToken,
});
// removing the Host header to avoid errors in Chrome
delete signedRequest.headers.Host;
axios(signedRequest);
});
});
},
},
};
Try setting the login map i.e the firebase token in the CognitoIdentityCredentials object. See this doc.
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'MY_COGNITO_POOL_ID',
Logins: {
'securetoken.google.com/':
}
});
Try calling get method on the credentials object before initializing the Cognito client. You can also use getCredentials instead.
If the above steps do not work & they should, pass the credentials as an option while initializing the Cognito client. See this doc for options available while using the CognitoIdentity constructor.
const cognitoidentity = new AWS.CognitoIdentity({credentials: AWS.config.credentials});
If you still receive the error, try logging credentials object in the console after calling the get() method. Ideally, it should have temporary credentials (accessKey, secretKey & sessionToken)
I want to call an AWS API Gateway Endpoint that is protected with AWS_IAM using the generated JavaScript API SDK.
I have a Cognito UserPool and a Cognito Identity Pool. Both properly synced via ClientId.
I use this code to Sign in and get the Cognito Identity
AWS.config.region = 'us-east-1'; // Region
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:XXXXXXXXXXXXXXXXXXXXXXXX' // your identity pool id here
});
AWSCognito.config.region = 'us-east-1';
AWSCognito.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:XXXXXXXXXXXXXXXXXXXXXXXX' // your identity pool id here
});
var poolData = {
UserPoolId: 'us-east-1_XXXXXXXX',
ClientId: 'XXXXXXXXXXXXXXXXXXXXXXXX'
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
var authenticationData = {
Username: 'user',
Password: '12345678',
};
var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
var userData = {
Username: 'user',
Pool: userPool
};
var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log('access token + ' + result.getAccessToken().getJwtToken());
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:XXXXXXXXXXXXXXXXXXXX',
IdentityId: AWS.config.credentials.identityId,
Logins: {
'cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXXX': result.idToken.jwtToken
}
});
AWS.config.credentials.get(function (err) {
// now I'm using authenticated credentials
if(err)
{
console.log('error in autheticatig AWS'+err);
}
else
{
console.log(AWS.config.credentials.identityId);
}
});
},
onFailure: function (err) {
alert(err);
}
});
All this succeeds and I have an authorized Cognito Identity now.
Now I try to call the API Gateway Endpoint to execute the Lambda Function it points to.
var apigClient = apigClientFactory.newClient({
accessKey: AWS.config.credentials.accessKeyId, //'ACCESS_KEY',
secretKey: AWS.config.credentials.secretAccessKey, //'SECRET_KEY',
sessionToken: AWS.config.credentials.sessionToken, // 'SESSION_TOKEN', //OPTIONAL: If you are using temporary credentials you must include the session token
region: 'us-east-1' // OPTIONAL: The region where the API is deployed, by default this parameter is set to us-east-1
});
var params = {
// This is where any modeled request parameters should be added.
// The key is the parameter name, as it is defined in the API in API Gateway.
};
var body = {
// This is where you define the body of the request,
query: '{person {firstName lastName}}'
};
var additionalParams = {
// If there are any unmodeled query parameters or headers that must be
// sent with the request, add them here.
headers: {},
queryParams: {}
};
apigClient.graphqlPost(params, body, additionalParams)
.then(function (result) {
// Add success callback code here.
console.log(result);
}).catch(function (result) {
// Add error callback code here.
console.log(result);
});
But unfortunately this fails. The OPTIONS request succeeds with 200 but the POST then fails with 403.
I am pretty sure that there is no CORS problem here.
I am pretty sure the problem has to do with IAM Roles and AWS Resource Configurations.
My question is basically, can you please provide me with all the necessary AWS Resource Configurations and IAM Roles that are necessary for this to work please?
Resources I have are
API Gateway - with deployed API Endpoints
Lambda Function - called by the Endpoint
Cognito User Pool - with App synced to the Identity Pool
Cognito Identity Pool - with Authorized and Unauthorized Role mapped to it.
IAM Roles - for the Lambda Function and the Authorized and Unauthorized Role of the Cognito Identity Pool.
But I don't know how these Resources need to be configured properly to get this to work.
Thank you
What access permissions does the role of the Cognito Identity have? Make sure it has access to perform execute-api:Invoke on your API.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"execute-api:Invoke"
],
"Resource": [
"arn:aws:execute-api:us-east-1:<account>:<rest-api>/*/POST/graphql"
]
}
]
}
You can get the exact resource ARN from the method settings page in the web console.
Even after following everything I was getting the same error. And the reason was I missed the "sessionToken" while initialising the apigClient.
var apigClient = apigClientFactory.newClient({
accessKey: AWS.config.credentials.accessKeyId, //'ACCESS_KEY',
secretKey: AWS.config.credentials.secretAccessKey, //'SECRET_KEY',
sessionToken: AWS.config.credentials.sessionToken, // 'SESSION_TOKEN', //OPTIONAL: If you are using temporary credentials you must include the session token
region: 'us-east-1' // OPTIONAL: The region where the API is deployed, by default this parameter is set to us-east-1 });
//OPTIONAL: If you are using temporary credentials you must include the session token -- is not really optional