AWS amplify cognito accessing pubsub with AccessToken, IdToken and RefreshToken - amazon-web-services

I am using AWS amplify with Angular and I followed the instructions to setup Amplify, add backend and then auth.
I also created a user in cognito pool and used it to login in my angular app. I am able to see AccessToken, IdToken and RefreshToken in the browser network tab. As per the guide I have to use these to obtain the pool tokens using which I can get temporary aws credentials to access aws services. I want to access pubsub to show messages being sent to aws iot_core.
But I was unable to find any information of how exactly we use these 3 tokens to access AWS resources..? Can anybody shed some light on this?
async signIn() {
Auth.signIn('xxxxx', 'xxxxx')
.then(user => {
if (user.challengeName === 'NEW_PASSWORD_REQUIRED') {
const { requiredAttributes } = user.challengeParam; // the array of required attributes,
e.g ['email', 'phone_number']
Auth.completeNewPassword(
user, // the Cognito User Object
'xxx', // the new password
// OPTIONAL, the required attributes
{
email: 'xxxx#example.com',
}
).then(user => {
// at this time the user is logged in if no MFA required
console.log(user);
}).catch(e => {
console.log(e);
});
} else {
console.log("Connecting to AWS IoT...")
//Apply plugin with configuration
Amplify.addPluggable(new AWSIoTProvider({
aws_pubsub_region: 'us-east',
aws_pubsub_endpoint: 'wss://xxxxx-ats.iot.us-east-1.amazonaws.com/mqtt',
}));
console.log("hello")
}
}).catch(e => {
console.log(e);
});
}

As per the Amplify docs on PubSub you have to:
Create IAM policies for AWS IoT
Attach your policy to your Amazon Cognito Identity
Allow the Amazon Cognito Authenticated Role to access IoT Services
https://docs.amplify.aws/lib/pubsub/getting-started/q/platform/js/#step-1-create-iam-policies-for-aws-iot

Related

Why is my AWS Congito user able to sign in, but "not authenticated" when using Auth.currentAuthenticatedUser()?

I have manually added an AWS Cognito user to my application's user pool via the AWS management console. The required user pool credentials have been confirmed for this user. I am setting up an auth guard to control access to the routes. Whenever I use the Auth.signIn() function, the promise resolves successfully, however, when calling Auth.currentAuthenticatedUser() after signing in, the promise within the canActivate() function returns an error message "not authenticated". I have read GitHub issues on cookieStorage causing issues with this, but do not have this configured.
sign-in.component.ts
signIn(username, password) {
return new Promise(() => {
Auth
.signIn(username, password)
.then(() => {
this.router.navigate(['main/dashboard']); // navigates to main/dashboard as expected
})
.catch(err => {
this.authenticationError = err.message;
})
});
}
auth.guard.ts
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
return Auth
.currentAuthenticatedUser()
.then(() => {
return true;
})
.catch((err) => {
this.router.navigate(['signin']);
console.log(err) // "not authenticated"
return false;
});
}
Users created via the AWS management console are not considered authenticated unless the status field for that user is CONFIRMED. The status field in my case was FORCE_CHANGE_PASSWORD. AWS expects that users created via the console change their password after the first sign in or verify their account using an email or mobile verification. In the case of my app, I did not want to use either email or mobile verification. To resolve this I used the following aws-cli command for that user.
aws cognito-idp admin-set-user-password --user-pool-id your_user_pool_id --username username --password password --permanent
I was facing this issue also after signing up, but it turns out you also need to log the user in too after you verify email with the code.
For example:
Auth.signUp
Auth.confirmSignUp
Auth.signIn
For more info https://docs.amplify.aws/lib/auth/emailpassword/q/platform/js

AWS Amplify - Disable Current User Account

AWS Amplify Authentication module has some methods for actions like sign in, sign up, forgot password etc. Even one can let the user to update his/her info through like:
import { Auth } from 'aws-amplify'
// Auth API Sign-in sample
Auth.signIn(username, password)
.then(user => console.log(user))
.catch(err => console.log(err))
// Auth API Change info sample
let result = await Auth.updateUserAttributes(user, {
'email': 'me#anotherdomain.com',
'family_name': 'Lastname'
})
However, I could not see anyway to disable (beware, not to delete) an account.
So, a user can sign up to a web application, but cannot deactivate it using AWS Amplify? If not, are there any other ways for disabling an AWS Cognito User Pool user via Javascript code?
I went through the AWS Documentation for the Cognito User Pools API and found some methods that let a function with admin privileges disable(not delete) a Cognito User Pool account!
Here is the link to the documentation on the AWS website:
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminDisableUser.html
There is also a method to reenable the user:
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminEnableUser.html
The javascript implementation for this can be found here:
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html#adminDisableUser-property
The code would be something like this using the AWS SDK for JS:
var cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'});
var params = {
UserPoolId: 'STRING_VALUE', /* required */
Username: 'STRING_VALUE' /* required */
};
cognitoidentityserviceprovider.adminDisableUser(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});

AWS Cognito /aws-amplify errors - crypto undefined

I'm trying to create a vuejs wrapper for aws cognito and am having trouble with their API. The documentation is poor, but what I have is giving me a "Cannot read property 'crypto' of undefined in the console.
Set up to configure amplify
import Amplify, { Auth } from 'aws-amplify'
import { CognitoUser } from 'amazon-cognito-identity-js'
Amplify.configure({
Auth: {
// REQUIRED - Amazon Cognito Identity Pool ID
identityPoolId: config.IdentityPoolId,
// REQUIRED - Amazon Cognito Region
region: config.Region,
// OPTIONAL - Amazon Cognito User Pool ID
userPoolId: config.UserPoolId,
// OPTIONAL - Amazon Cognito Web Client ID (26-char alphanumeric string)
userPoolWebClientId: config.ClientId,
// OPTIONAL - Enforce user authentication prior to accessing AWS resources or not
mandatorySignIn: false,
}
});
and this is all the code thats run on initialization to cause errors. what am i doing wrong? I just want to be able to access Auth to call methods on it.
For clarity, I'm exporting a function to register the vuex module and the below code (aside from imports) is inside the init action.
export default (store, config, namespace = 'cognito') => {
store.registerModule(namespace, {
namespaced: true,
state,
getters,
mutations,
actions
})
store.dispatch(`${namespace}/init`, config)
}

How to subscribe AWS SNS Topic in React Native (Android)?

I tried with the aws-sdk-react-native module:
https://github.com/awslabs/aws-sdk-react-native
The configuration took some time, but thanks to this links I could i.e. list the topics:
https://github.com/awslabs/aws-sdk-react-native/issues/35
https://github.com/awslabs/aws-sdk-react-native/blob/master/SNS/IntegrationTests/SNSTests.js
The test includes a sample how to subscribe to get emails but not how to get notifications in the app. I don't know how to get the platformEndpoint, the PlatformApplicationArn and the deviceToken.
endPoint = sns.createPlatformEndpoint({
PlatformApplicationArn: '{APPLICATION_ARN}',
Token: '{DEVICE_TOKEN}'
})
...
var subscribeRequest= {
"Protocol":"application",
"TopicArn":topicARN,
"Endpoint":endPoint
}
try{
await AWSSNS.Subscribe(subscribeRequest);
}catch(e){
console.error(e);
shouldResolve = false;
return shouldResolve;
}
Are there any samples for this?
I'm also looking for an authentication sample.
Would it be easier to use firebase?
Thanks
I have used GCM over SNS to send notifications. Here are the steps I went through assuming you already set up GCM and added required libraries from AWS React Native SDK:
First create a SNS app from AWS:
Then you need to create Federated Identity through Cognito service of AWS. This is required for sending your device token from mobile app to AWS SNS app. Choose Manage Federated Identities
Then create your pool, don't forget to check Enable Access to unauthenticated identities
When you create the pool you will need to create IAM Roles for unauthenticated and authenticated roles of that pool. AWS will help you create new roles for that but you need to go to IAM Roles menu and attach AmazonSNSFullAccess to created roles, otherwise from the mobile app you won't able to send device token.
After doing these steps you will able send your device token using Amazon's React Native SDK. I have written a helper class for sending token to AWS SNS as suggested here:
class AWSUtility {
constructor() {
const region = "us-west-1"; //change it with your region
const IDENTITY_POOL_ID = "pool id created from Federated Identities"
AWSCognitoCredentials.initWithOptions({region, identity_pool_id: IDENTITY_POOL_ID});
AWSSNS.initWithOptions({region});
}
addTokenToAWSSNS(token, snsEndpointARN) {
const applicationArn = "change with SNS application Amazon resource name";
return Promise.try(() => {
if (!snsEndpointARN) {
return this.createPlatformEndpoint(token, applicationArn);
} else {
return AWSSNS.GetEndpointAttributes({EndpointArn: snsEndpointARN})
.then((result) => {
const {Attributes = {}} = result;
const {Token, Enabled} = Attributes;
const updateNeeded = Token !== token || Enabled !== 'true';
if (updateNeeded) {
return this.updateEndpoint(token).then(() => result.EndpointArn);
}
return snsEndpointARN;
})
.catch(() => {
this.createPlatformEndpoint(token, applicationArn)
});
}
});
}
updateEndpoint(snsEndpointARN, token) {
//AWS is returning error saying that it requires 6 params to update endpoint, if anyone has any idea about it let me know please
return AWSSNS.SetEndpointAttributes({EndpointArn: snsEndpointARN, Attributes: {Token: token, Enabled: true}});
}
createPlatformEndpoint(token, applicationArn) {
return AWSSNS.CreatePlatformEndpoint({Token: token, PlatformApplicationArn: applicationArn})
.then(result => result.EndpointArn)
.catch((error = {}) => {
console.log(error);
});
}
}
export default new AWSUtility();

AWS API Gateway / Cognito Userpools / Lambdas not able to pass caller credentials

I'm working on an AWS API Gateway implementation with a Lambda backend. I use the API Gateway integration with the Cognito Userpools (fairly new) instead of building a custom authorizer using Lambda (which was the recommended way before it was integrated).
I've created a proof of concept (javascript) that authenticates a user with Cognito and then makes a call to the API Gateway with those credentials. So, basically, the end call to the API Gateway is with the JWT token that I received from Cognito (result.idToken.jwtToken) in the Authorization header. This all works and I can validate that only with this token you can access the API.
All working fine, but now I want to get access to the Cognito identity in my Lambda; for instance the identy id or the name or email. I have read how to map all the parameters, but I'm actually just using the standard 'Method Request Passthrough' template in the integration request. I log all the parameters in the lambda and all the 'cognito' parameters are empty.
I've looked through many similar questions and they all propose to enable the 'Invoke with caller credentials' checkbox on the integration request. That makes perfect sense.
However, this checkbox can only be enabled if you are using AWS_IAM as authorization and not if you have selected your cognito UserPool. So it is just not possible to select it and is actually disabled.
Does anybody know what to do in this case? Is this still work in progress, or is there a reason why you can't enable this and get the cognito credentials in your Lambda?
Many thanks.
If you need to log the user information in your backend, you can use $context.authorizer.claims.sub and $context.authorizer.claims.email to get the sub and email for your Cognito user pool.
Here is the documentation about Use Amazon Cognito Your User Pool in API Gateway
For anyone else still struggling to obtain the IdentityId in a Lambda Function invoked via API-Gateway with a Cognito User Pool Authorizer, I finally was able to use the jwtToken passed into the Authorization header to get the IdentityId by using the following code in my JavaScript Lambda Function:
const IDENTITY_POOL_ID = "us-west-2:7y812k8a-1w26-8dk4-84iw-2kdi849sku72"
const USER_POOL_ID = "cognito-idp.us-west-2.amazonaws.com/us-west-2_an976DxVk"
const { CognitoIdentityClient } = require("#aws-sdk/client-cognito-identity");
const { fromCognitoIdentityPool } = require("#aws-sdk/credential-provider-cognito-identity");
exports.handler = async (event,context) => {
const cognitoidentity = new CognitoIdentityClient({
credentials: fromCognitoIdentityPool({
client: new CognitoIdentityClient(),
identityPoolId: IDENTITY_POOL_ID,
logins: {
[USER_POOL_ID]:event.headers.Authorization
}
}),
});
var credentials = await cognitoidentity.config.credentials()
var identity_ID = credentials.identityId
console.log( identity_ID)
const response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods" : "OPTIONS,POST,GET,PUT"
},
body:JSON.stringify(identity_ID)
};
return response;
}
After a Cognito User has signed in to my application, I can use the Auth directive of aws-amplify and fetch() in my React-Native app to invoke the lambda function shown above by sending a request to my API-Gateway trigger (authenticated with a Cognito User Pool Authorizer) by calling the following code:
import { Auth } from 'aws-amplify';
var APIGatewayEndpointURL = 'https://5lstgsolr2.execute-api.us-west-2.amazonaws.com/default/-'
var response = {}
async function getIdentityId () {
var session = await Auth.currentSession()
var IdToken = await session.getIdToken()
var jwtToken = await IdToken.getJwtToken()
var payload = {}
await fetch(APIGatewayEndpointURL, {method:"POST", body:JSON.stringify(payload), headers:{Authorization:jwtToken}})
.then(async(result) => {
response = await result.json()
console.log(response)
})
}
More info on how to Authenticate using aws-amplify can be found here https://docs.amplify.aws/ui/auth/authenticator/q/framework/react-native/#using-withauthenticator-hoc