I have implemented AWS Cognito successfully as below for Unauthorised users. With Unauthorised users, once the IdentityId is successfully returned I can then use Cognito Sync to create and manipulate Datasets successfully. I have been unable to use Cognito Sync with an Authorised user. Please can someone tell me how I obtain the IdentityId for a Authorised user, which then allows me to listRecords (which requires the IdentityId). IdentityId is always being returned as undefined.
AWS.config.credentials = new CognitoIdentityCredentials({
IdentityPoolId: REACT_APP_AWS_CONFIG_IDENTITY_POOL_ID,
});
const userPool = new CognitoUserPool({
UserPoolId: REACT_APP_AWS_CONFIG_USER_POOL_ID,
ClientId: REACT_APP_AWS_CONFIG_CLIENT_ID,
});
let cognitosync = new AWS.CognitoSync();
AWS.config.credentials.get(()=>{
IdentityId = AWS.config.credentials.identityId;
listRecords();
});
function listRecords(){
cognitosync.listRecords({
DatasetName : DATA_SET_NAME,
IdentityId : IdentityId,
IdentityPoolId : REACT_APP_AWS_CONFIG_IDENTITY_POOL_ID
}, function(error, data) {
//...
});
}
thanks in advance
I suspect you just need to add a Logins map to your AWS.config.credentials object for authenticated users.
What you put in the Logins map depends on how the user authenticated, but essentially you need a single entry, with the identity provider (IDP) as the key, and the id token you received from that IDP as the value.
For example, if your user authenticated with Google, you might have:
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx',
Logins: {
'accounts.google.com': googleUser.tokenId
}
});
where googleUser is the object returned by Google.
If your user authenticated with a Cognito User Pool, you might have:
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx',
Logins: {
'cognito-idp.us-east-1.amazonaws.com/us-east-1_xxxxxxxxx':
session.getIdToken().getJwtToken()
}
});
Where session is passed to a callback you provide to CognitoUser.getSession(). Additional code context for this use case in this Gist.
I've discovered what my problem was. To get the JWT for the Login key/value I was using :
result.getAccessToken().getJwtToken()
when it should have been:
result.getIdToken().getJwtToken()
all is working now
Related
I am creating an angular app, in which I want to add authentication via AWS Cognito (I am pretty new to AWS). I successfully added functionality for sign-up, sign-in, sign-out, mfa and more. In addition I want to create something like admin panel, where admins can change general users` attributes. But I am not sure how to implement these admin things. How should admins sign-in? How should admins sign-up? Is there a dedicated user pool for them? And then how to manage (change attributes of) the general users as an admin?
I have gone trough the AWS Documentation, but it is not clear enough. I see that there is some kind of actions prefixed with Admin like AdminUpdateUserAttributes but I am not really sure how to use them.
Edit: I have tried something like this:
const AWS = require('aws-sdk');
let cognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'});
let params = {
UserAttributes: [{
Name: 'custom:state',
Value: this.newValue
}],
UserPoolId: 'us-east-1_example',
Username: this.username
};
cognitoIdentityServiceProvider.adminUpdateUserAttributes(params, function(err, data) {
// do something with result
err && console.error(err);
data && console.log(data);
});
But I am getting the following error: CredentialsError: Missing credentials in config
How should I set these credentials?
In order to have admin permissions, you need to provide accessKeyId and secretAccessKey or idToken.
One way to do this, is to get these keys from the AWS Management console. They can be extracted from an IAM role, which has the permissions to modify the desired User Pool.
Then you can do:
AWS.config.update({accessKeyId: '...', secretAccessKey: '...'});
What I have personally done in my app is to create another user pool for admins. Then I added this user pool as Identity Provider to an Identity Pool. Then I edited the Authorized IAM role to be able to edit the user pool with the general users.
The following worked for me:
const userPool = new CognitoUserPool({
UserPoolId: this.adminUserPoolId,
ClientId: this.adminClientId
});
const authenticationDetails = new AuthenticationDetails({
Username: username,
Password: password
});
const cognitoUser = new CognitoUser({
Username: username,
Pool: userPool
});
cognitoUser.authenticateUser(authenticationDetails, ....);
const jwt = cognitoUser.getSignInSession().getIdToken().getJwtToken();
const provider = `cognito-idp.${this.region}.amazonaws.com/${this.adminUserPoolId}`;
AWS.config.update({
credentials: new CognitoIdentityCredentials({
IdentityPoolId: this.identityPoolId,
Logins: {
[provider]: jwt // when you provide the jwt, accessKeyId and secretAccessKey are extracted
}
})
});
const identityService = new CognitoIdentityServiceProvider();
identityService.adminUpdateUserAttributes(...);
I want to authenticate users using Cognito, with option to use Facebook. User can sign_in/sign_up using either of those options.
I have created Cognito User Pool and Cognito Federated Identity, and also I have created Facebook App for authentication. Both User Pool and Facebook app are connected to Federated identity.
When I sign_up and later authenticate Cognito User via Cognito User Pool, then Cognito returns accessToken, which I store in localStorage on front and use whenever needed for athentication.
I have /authenticate endpoint (express), that takes in username & password, and returns accessToken if all went well. Whenever I make API call that requires auth, I send accessToken that I have in local storage. It goes, more or less as this:
// POST user/authenticate
const authenticationData = {
Username: username,
Password: password
}
authenticationDetails = new AuthenticationDetails(authenticationData)
const userData = {
Username: username,
Pool: userPool()
}
cognitoUser = new CognitoUser(userData)
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: (res) => resolve(res), // here I get accessToken
onFailure: (err) => {
console.log('[authenticateUser error]', err)
reject(err)
},
//...
However
When I use Facebook, I do not get accessToken I could use in same fashion. I get accessToken from Facebook via FB.login, I pass it to Cognito to authenticate, and then I don't know what to do, because I cannot get any token that could be used to authenticate API calls, that require Cognito Authentication.
Here's what I do:
await window.FB.login((response) => {
props.userFacebookSignIn(response)
})
// ...
call(foo, 'users/facebook_sign_in', { accessToken: payload.facebookAccessToken })
// ...
// users/facebook_sign_in
AWS.config.region = config.AWSRegion
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'foo',
Logins: {
'graph.facebook.com': facebookAccessToken
}
})
AWS.config.credentials.get((err) => {
// Here I get no errors, I presume that I have logged Facebook user in
const accessKeyId = AWS.config.credentials.accessKeyId
const secretAccessKey = AWS.config.credentials.secretAccessKey
const sessionToken = AWS.config.credentials.sessionToken
// here I can do stuff probably,
// but I would like to receive token that would allow me to do stuff,
// rather than context I can do stuff in
})
While I am doing all of this, I have this feeling, that devs at AWS implemented Cognito as frontend solution, rather than something to be used in backend. Correct me if I am wrong.
Nevertheless, I would like to be able authenticate api calls using Cognito and Facebook interchangeably in express middleware.
Is that possible? Thanks.
I have used federated identity for salesforce single sign on but i imagine the steps will the same. After authenticating with facebook you will recieve and id_token from them in response. You have to pass this as a parameter in the getId method:
var params = {
IdentityPoolId: 'STRING_VALUE', /* required */
AccountId: 'STRING_VALUE',
Logins: {
'<IdentityProviderName>': 'STRING_VALUE',
/* 'graph.facebook.com': ... */
}
};
cognitoidentity.getId(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
In the result you will get an identity id which you can save somewhere so that you don't have to make this call everytime while authenticating. Now take this identity id and make the getCredentialsForIdentity call:
response = client.get_credentials_for_identity(
IdentityId='string',
Logins={
'string': 'string'
},
CustomRoleArn='string'
)
This will finally give you the temporary access key, secret key and session key you need.
I decided to use oAuth.
Here's quick & dirty look on how it's done
In AWS Cognito
1) Set up Cognito User Pool. Add App Client save App client id & App client secret as COGNITO_CLIENT_ID and COGNITO_CLIENT_SECRET
2) Go to Federation > Identity providers and add your Facebook app ID and App secret (both you will find in Facebook app panel)
3) Go to App integration > App client settings click "Select all", set up your Callback URL, mine is localhost:5000/facebook also select Authorization code grant and Allowed OAuth Scopes (save scopes to say: COGNITO_SCOPES)
4) Now go to App integration > Domain name and enter your custom domain; let's say example-app-debug so it's: https://example-app-debug.auth.us-east-1.amazoncognito.com
That's all there is to Cognito
no the Facebook part
5) Settings > Basic add example-app-debug.auth.us-east-1.amazoncognito.com to your App domains - Save Changes
6) In Facebook Login > Settings in Valid OAuth Redirect URIs add this URL: https://example-app-debug.auth.us-east-1.amazoncognito.com/oauth2/idpresponse and Save Changes
and the code
In browser, redirect user to this url when Login w. Facebook button is clicked:
window.location.href =
`https://example-app-debug.auth.us-east-1.amazoncognito.com/oauth2/authorize` +
`?identity_provider=Facebook` +
`&redirect_uri=http://localhost:5000/facebook` +
`&response_type=code` +
`&client_id=${COGNITO_CLIENT_ID}` +
`&scope=${COGNITO_SCOPES}`
this call should come back to you with a code, like this: http://localhost:5000/facebook?code=foo-bar-code Send this code to your backend.
In backend, do this:
const axios = require('axios')
const url = `` +
`https://${COGNITO_CLIENT_ID}:${COGNITO_CLIENT_SECRET}` +
`#example-app-debug.auth.us-east-1.amazoncognito.com/oauth2/token` +
`?grant_type=authorization_code` +
`&code=foo-bar-code` + // <- code that came from Facebook
`&redirect_uri=http://localhost:5000/facebook` +
`&client_id=${COGNITO_CLIENT_ID}`
const response = await axios.post(url)
// response should have access_token, refresh_token and id_token in data
You send access_token, refresh_token and id_token back to frontend and save them in local storage and use them to authenticate and Done.
What I am trying to do: Authenticate my users using ADFS, pass the SAML response token to AWS and get back credentials which I can then use to access AWS resources.
What I am able to do now: Sign in successfully through ADFS and get the SAML token back which confirms the successfully sign in.
What is not working: Calling the AWS.STS.assumeRoleWithSaml functions gets a 403 Access Denied error
How it works thus far:
Users click a button on my application, which calls the following:
var RPID = encodeURIComponent('urn:amazon:webservices');
var result = 'https://virtualMachine.eastus.cloudapp.azure.com/adfs/ls/IdpInitiatedSignOn.aspx?loginToRp=' + RPID;
window.location.href = result;
A successful sign in here returns back to the application a SAML response token
var saml = new URL(window.location.href);
var token = saml.searchParams.get('SAMLResponse');
The application then calls assumeRoleWithSAML to get back credentials. The Principal ARN refers to the identity provider I am trying to access and the RoleARN refers to a role which has full access to everything:
authenticateSAMLwithCognito(token) {
//define our security token service object
var sts = new AWS.STS();
//build our parameter object
var params = {
//cognito identity provider
PrincipalArn: 'arn:aws:iam::accountid:saml-provider/wmpo-adfs',
//role assuming
RoleArn: 'arn:aws:iam::accountid:role/ADFS-Dev',
//authorization
SAMLAssertion: token
}
console.log("Parameters sent", params);
sts.assumeRoleWithSAML(params, (err, data) => {
if(err) console.log(err);
else console.log("Success!", data);
})
}
However the response from this exchange is:
I am really unsure why this is, but if anyone has some helpful pushes that would be great! Thanks and happy new year
Wow that only took days, but though I was constantly stumbling, I finally made it to the point I was trying to get.
The answer was in the same credentials get function that I used when I authenticated users through a username password combo by way of a Cognito User Pool.
authenticateThroughCognito(token) {
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-west-2:IdentityPoolId',
Logins: {
'arn:aws:iam::accountId:saml-provider/wmpo-adfs' : token
}
});
(AWS.config.credentials as AWS.Credentials).get((err) => {
if(err) console.log(err);
else {
console.log("Success");
console.log(AWS.config.credentials);
}
})
}
I'm using a Cognito userpool and Cognito federated identities to pass an IAM role to authenticated and unauthenticated users. When I try to switch a user from unauthenticated to authenticated, the developer console doesn't register that the change has happened; it is showing that I have 100% unauthenticated users.
Right now I instantiate my AWS client as an unauthenticated user and then call a function to update the credentials when they are available so I can switch them to authenticated. E.g.:
AWS.config.region = 'us-west-2';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: identityPoolId,
region: 'us-west-2',
Logins: {}
});
const updateCredentials = () => {
const auth = store.getState().auth; //this gets the authentication credentials from a global store.
AWS.config.credentials.Logins = {
'cognito-idp.us-west-2.amazonaws.com/us-west-2_XXXXXXXXX': auth.idToken
};
AWS.config.credentials.expired = true;
};
As best as I can tell, this is the correct way to do this. See the documentation at the bottom of this page, and here, and here.
However, my console shows that I have no authenticated users, so updateCredentials is not switching users to authenticated. What can be done to fix this?
at the end call AWS.config.credentials.get(()....)
I want to delete cognito user using my nodejs application.
Example
cognitoUser.deleteUser (err, result) ->
if err
reject err
resolve result
when i try to delete cognito user error throws as follows
Error: User is not authenticated
cognitoUser.deleteUser is used by an authenticated user to delete himself but i want to delete all users using super user
Please give me some idea to solve this problem.
You can use the main aws javascript SDK and call the adminDeleteUser operation. It is an authenticated operation and it will require developer credentials for you to call it.
https://github.com/aws/aws-sdk-js/blob/master/apis/cognito-idp-2016-04-18.normal.json#L100
var aws = require('aws-sdk');
var CognitoIdentityServiceProvider = aws.CognitoIdentityServiceProvider;
var client = new CognitoIdentityServiceProvider({ apiVersion: '2016-04-19', region: 'us-east-1' });
//now you can call adminDeleteUser on the client object
You can do from AWS-CLI
aws cognito-idp admin-delete-user --user-pool-id ${user pool id} --username ${username usually email here}