I work with AWS Cognito. I have the attached code to globally signout a user based on access token :
var signOut = (accessToken) =>
new Promise((resolve, reject) => {
var params = {
//UserPoolId: process.env.USER_POOL_ID, /* required */
AccessToken: accessToken /* required */
};
var cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider({
apiVersion: '2016-04-18',
region: 'us-east-1'
})
console.log("Signing out user .. ");
cognitoidentityserviceprovider.globalSignOut(params, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
reject(err)
} else {
console.log(data);
resolve(data)
}
});
});
and when I invoke the code I get the following error:
2019-08-20T14:33:29.736Z 011d65cb-ee4d-4053-9283-6e6242560c6b { NotAuthorizedException: Access Token has been revoked
at Request.extractError (/var/task/sign-out/node_modules/aws-sdk/lib/protocol/json.js:51:27)
at Request.callListeners (/var/task/sign-out/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
at Request.emit (/var/task/sign-out/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
at Request.emit (/var/task/sign-out/node_modules/aws-sdk/lib/request.js:683:14)
at Request.transition (/var/task/sign-out/node_modules/aws-sdk/lib/request.js:22:10)
at AcceptorStateMachine.runTo (/var/task/sign-out/node_modules/aws-sdk/lib/state_machine.js:14:12)
at /var/task/sign-out/node_modules/aws-sdk/lib/state_machine.js:26:10
at Request. (/var/task/sign-out/node_modules/aws-sdk/lib/request.js:38:9)
at Request. (/var/task/sign-out/node_modules/aws-sdk/lib/request.js:685:12)
at Request.callListeners (/var/task/sign-out/node_modules/aws-sdk/lib/sequential_executor.js:116:18)
message: 'Access Token has been revoked',
Does anyone know how to resolve this issue?
This exception throws as Access token you are using was already has been revoked by the global sign out it self.
Which means you have did already signed out from the cognito.
Try to invoke the same function with new Access Token generated by signing in (aka Login) API.
Related
Summary of problem:
We have an AWS Redshift cluster in Account A, this has a database called 'products'
In Account B we have a lambda function which needs to execute a SQL statement against 'products' using the Redshift Data API
We have setup a new secret in AWS Secrets manager containing the redshift cluster credentials. This secret has been shared with Account B. We've confirmed Account B can access this information from AWS Secrets Manager.
When we call the Redshift Data API action 'executeStatement' we get the following error:
ValidationException: Cluster doesn't exist in this region.
at Request.extractError (C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\protocol\json.js:52:27)
at Request.callListeners (C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\sequential_executor.js:106:20)
at Request.emit (C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\sequential_executor.js:78:10)
at Request.emit (C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\request.js:688:14)
at Request.transition (C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\request.js:22:10)
at AcceptorStateMachine.runTo (C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\state_machine.js:14:12)
at C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\state_machine.js:26:10
at Request.<anonymous> (C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\request.js:38:9)
at Request.<anonymous> (C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\request.js:690:12)
at Request.callListeners (C:\git\repositories\sandbox\redshift\node_modules\aws-sdk\lib\sequential_executor.js:116:18)
The error message suggest it's perhaps not going to the correct account, since the secret contains this information I would have expected it to know.
Code Sample:
Here's my code:
var redshiftdata = new aws.RedshiftData({ region: 'eu-west-2'});
const params : aws.RedshiftData.ExecuteStatementInput = {
ClusterIdentifier: '<clusteridentifier>',
Database: 'products',
SecretArn: 'arn:aws:secretsmanager:<region>:<accountNo>:secret:<secretname>',
Sql: `select * from product_table where id = xxx`,
StatementName: 'statement-name',
WithEvent: true
};
redshiftdata.executeStatement(params,
async function(err, data){
if (err) console.log(err, err.stack);
else {
const resultParams : aws.RedshiftData.GetStatementResultRequest = { Id: data.Id! };
redshiftdata.getStatementResult(resultParams, function(err, data){
if (err) console.log(err, err.stack);
else console.dir(data, {depth: null});
})
}
});
Any suggestions or pointers would be really appreciated.
Thanks for the answer Parsifal. Here's a code snippet of the working solution.
import aws from "aws-sdk";
var roleToAssume = {RoleArn: 'arn:aws:iam::<accountid>:role/<rolename>',
RoleSessionName: 'example',
DurationSeconds: 900,};
var sts = new aws.STS({ region: '<region>'});
sts.assumeRole(roleToAssume, function(err, data) {
if (err)
{
console.log(err, err.stack);
}
else
{
aws.config.update({
accessKeyId: data.Credentials?.AccessKeyId,
secretAccessKey: data.Credentials?.SecretAccessKey,
sessionToken: data.Credentials?.SessionToken
})
// Redshift code here...
}
});
I already have aws assume role credentials in .aws/credetials file.
how to use it to creat sts or dynamodb like:
const { DynamoDB } = require('aws-sdk');
const { DocumentClient } = DynamoDB;
const dynamo = new DynamoDB({
endpoint: process.env.AWS_ENDPOINT,
region: process.env.AWS_REGION,
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
secretToken: process.env.aws_security_token
});
I mean I got error as:
root#myubuntu:~/work/contacts_api# node ./seed/runner.js
```
Checking if 'contacts' table exists
{ UnrecognizedClientException: The security token included in the request is invalid.
at Request.extractError (/root/work/contacts_api/node_modules/aws-sdk/lib/protocol/json.js:51:27)
at Request.callListeners (/root/work/contacts_api/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
at Request.emit (/root/work/contacts_api/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
at Request.emit (/root/work/contacts_api/node_modules/aws-sdk/lib/request.js:683:14)
at Request.transition (/root/work/contacts_api/node_modules/aws-sdk/lib/request.js:22:10)
at AcceptorStateMachine.runTo (/root/work/contacts_api/node_modules/aws-sdk/lib/state_machine.js:14:12)
at /root/work/contacts_api/node_modules/aws-sdk/lib/state_machine.js:26:10
at Request. (/root/work/contacts_api/node_modules/aws-sdk/lib/request.js:38:9)
at Request. (/root/work/contacts_api/node_modules/aws-sdk/lib/request.js:685:12)
at Request.callListeners (/root/work/contacts_api/node_modules/aws-sdk/lib/sequential_executor.js:116:18)
message: 'The security token included in the request is invalid.',
code: 'UnrecognizedClientException',
time: 2019-01-07T05:39:54.907Z,
requestId: 'A5CFV62P0TGHJH7VDIBSL0JRC3VV4KQNSO5AEMVJF66Q9ASUAAJG',
statusCode: 400,
retryable: false,
retryDelay: 5.013458338738063 }
```
I want to know the correct way to initial credetials, if I want to use mfa credetials.
I'm guessing that the error here should give you a clue:
"The security token included in the request is invalid"
Did you try printing out the environment value
env | grep aws_security_token
If it's empty you'll have to set the value prior to running your code.
Also, I've noticed that your other aws keys are all caps whereas your aws_security_token is all lowercase.
I suspect secretToken isn't a thing. Here are two examples of how it could be done (how I've done it before).
That said I would encourage the construction and use of a Credentials where ever possible (the second example), but if you wanted to do it inline- that should work too.
/** assume a role and build a DocumentClient object to make a single scan **/
;(async () => {
const sts = new AWS.STS()
const assumeRole = await sts
.assumeRole({
RoleArn: process.env.ROLE_ARN,
RoleSessionName: process.env.ROLE_SESSION_NAME,
})
.promise()
const dynamodb = new AWS.DynamoDB.DocumentClient({
region: process.env.REGION,
credentials: {
accessKeyId: assumeRole.Credentials?.AccessKeyId,
secretAccessKey: assumeRole.Credentials?.SecretAccessKey,
sessionToken: assumeRole.Credentials?.SessionToken,
},
})
const scan = await dynamodb
.scan({
TableName: process.env.TABLE_NAME,
})
.promise()
console.log(scan)
})()
/**
* assume a role and build a Credentials object and use it
* to build a DocumentClient object to make a single scan
**/
;(async () => {
const sts = new AWS.STS()
const assumeRole = await sts
.assumeRole({
RoleArn: process.env.ROLE_ARN,
RoleSessionName: process.env.ROLE_SESSION_NAME,
})
.promise()
const credentials = new AWS.Credentials({
accessKeyId: assumeRole.Credentials?.AccessKeyId,
secretAccessKey: assumeRole.Credentials?.SecretAccessKey,
sessionToken: assumeRole.Credentials?.SessionToken,
})
const dynamodb = new AWS.DynamoDB.DocumentClient({
region: process.env.REGION,
credentials: credentials,
})
const scan = await dynamodb
.scan({
TableName: process.env.TABLE_NAME,
})
.promise()
console.log(scan)
})()
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)
Is it possible to change with my android App, Cognito user pool user status from FORCE_CHANGE_PASSWORD to CONFIRMED? or from RESET_REQUIRED to CONFIRMED?
If yes which API call can I use?
In fact, I imported users to Cognito and I don't find a way or any example on how to turn them to CONFIRMED status using my App.
Thanks
To change the cognito user pool user status from FORCE_CHANGE_PASSWORD to CONFIRMED-
1.with aws-cli:
get a session token with the temporary password
aws cognito-idp admin-initiate-auth --user-pool-id us-west-2_xxxxxxx --client-id xxxxxxx --auth-flow ADMIN_NO_SRP_AUTH --auth-parameters USERNAME=xxx,PASSWORD=xxx
set new password with the session token
aws cognito-idp admin-respond-to-auth-challenge --user-pool-id xxxx --client-id xxxx --challenge-name NEW_PASSWORD_REQUIRED --challenge-responses NEW_PASSWORD=xxx,USERNAME=xxx --session session_key_from_previous_token
2.with aws-sdk:
get a session token with the temporary password
cognitoidentityserviceprovider.adminInitiateAuth(
{
AuthFlow: 'ADMIN_NO_SRP_AUTH',
ClientId: 'xxx',
UserPoolId: 'xxx',
AuthParameters:
{ USERNAME: 'xxx', PASSWORD: 'temporary_password' }
}, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
set new password with the session token
var params = {
ChallengeName: 'NEW_PASSWORD_REQUIRED',
ClientId: 'xxxx',
ChallengeResponses: {
USERNAME: 'xxx',
NEW_PASSWORD: 'xxx'
},
Session: 'session_key_from_previous_token'
};
cognitoidentityserviceprovider.respondToAuthChallenge(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
Note: If get an error about "Unable to verify secret hash for client", create another app client without a secret and use that.
To change the status of the user you just need to go through the respective flows. To change FORCE_CHANGE_PASSWORD to CONFIRMED, you would need to use the one time password and login and change your password. For RESET_REQUIRED, you would need to use the Forgot Password flow and that will change the status to CONFIRMED.
I'm using the aws-sdk javascript in my back-end and I can use AWS fine but when I try to use the getOpenIdTokenForDeveloperIdentity method I get a "Missing region in config error" as a response.
var config = new AWS.Config({
accessKeyId: "MYACCESSKEY", secretAccessKey: "MYSECRETYKEY", region: 'us-east-1'
});
var params = {
IdentityPoolId: 'MYIDENTITYPOOLID', /* required */
Logins: { /* required */
"login.my.myapp": 'string',
/* anotherKey: ... */
},
IdentityId: null,
TokenDuration: 0
};
cognitoidentity.getOpenIdTokenForDeveloperIdentity(params,function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
In the docs it said that:
By default, credentials and region settings are left unconfigured. This should be configured by the application before using any AWS service APIs.
So I set my region but why am I still getting an error?
You are setting the region in a local config variable. It should be set in the global AWS.config, like this:
AWS.config.region = 'us-east-1';
The same applies for the credentials. In case you want to use Amazon Cognito Credentials for all your AWS clients, you should initialize AWS.config.credentials like this:
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'YOUR_POOL_ID'
});
I hope this helps!