Can not connect to AWS Amplify PubSub -> Socket error:undefined - amazon-web-services

I've been trying out all ways to get the Amplify/PubSub working without any luck. It seems all the documentation are rather outdated.
Here is what I have done so far. Please note all hashes are made up ;-)
Created a fresh React Native app
Installed amplify packages
Installed Amplify CLI
Invoked $ amplify configure
Invoked $ amplify init
Invoked $ amplify add auth
Invoked $ amplify push, which created the aws-exports.js object
Created a super simple component
import React from 'react';
import { View } from 'react-native';
import { withAuthenticator } from 'aws-amplify-react-native';
import Amplify, { Auth, PubSub } from 'aws-amplify';
import { AWSIoTProvider } from '#aws-amplify/pubsub/lib/Providers';
import awsmobile from './aws-exports';
Amplify.configure({
Auth: {
mandatorySignIn: true,
region: awsmobile.aws_cognito_region,
userPoolId: awsmobile.aws_user_pools_id,
identityPoolId: awsmobile.aws_cognito_identity_pool_id,
userPoolWebClientId: awsmobile.aws_user_pools_web_client_id,
},
Analytics: {
disabled: true,
},
});
Amplify.addPluggable(
new AWSIoTProvider({
aws_pubsub_region: 'ap-southeast-2',
aws_pubsub_endpoint:
'wss://a123456789d-ats.iot.ap-southeast-2.amazonaws.com/mqtt',
})
);
Amplify.Logger.LOG_LEVEL = 'DEBUG';
class App extends PureComponent {
componentDidMount() {
if (this.props.authState === 'signedIn') {
Auth.currentCredentials().then((creds) => {
// get the principal that needs to be attached to policy
console.log('principal to be attached', creds.identityId)
PubSub.subscribe('topic_1').subscribe({
next: (data) => console.log(JSON.stringify(data, null, 2)),
error: (msg) => console.log('ERROR: ', msg.error),
close: () => console.log('Done'),
});
});
}
}
render() {
return (
<View/>
);
}
}
export default withAuthenticator(App);
I attached AWS root certificate to my iPhone (see below)
10.Create a IAM policy for AWS IoT
IoTAppPolicy
iot:*
arn:aws:iot:ap-southeast-2:1234567890:*
11.Attach the principal I got from Auth.currentCredentials to the policy
aws iot attach-principal-policy --policy-name IoTAppPolicy --principal ap-southeast-2:db1234bc-5678-90123-4567-89ae0e123b4
12.Attach Policies to Auth Role
AWSIoTDataAccess
AWSIoTConfigAccess
Yet, when I run the app I get the following error log
[DEBUG] 51:42.745 SignIn - Sign In for test#test.com
[DEBUG] 51:44.616 AuthClass CognitoUserSession {idToken: CognitoIdToken, refreshToken: CognitoRefreshToken, accessToken: CognitoAccessToken, clockDrift: 0}
[DEBUG] 51:44.616 Credentials - set credentials from session
[DEBUG] 51:46.247 Credentials - Load credentials successfully
[DEBUG] 51:46.248 AuthClass - succeed to get cognito credentials
DEBUG] 51:47.150 Hub - Dispatching to auth with {event: "signIn", data: CognitoUser, message: "A user 2d...de has been signed in"}
[DEBUG] 51:47.151 SignIn CognitoUser {username: "2d...de", pool: CognitoUserPool, Session: null, client: Client, signInUserSession: CognitoUserSession, …}
[DEBUG] 51:47.152 AuthClass - Getting the session from this user: CognitoUser {username: "2d...de", pool: CognitoUserPool, Session: null, client: Client, signInUserSession: CognitoUserSession, …}
[DEBUG] 51:47.152 AuthClass - Succeed to get the user session CognitoUserSession {idToken: CognitoIdToken, refreshToken: CognitoRefreshToken, accessToken: CognitoAccessToken, clockDrift: 1}
[DEBUG] 51:47.574 AuthPiece - verified user attributes {verified: {…}, unverified: {…}}
[INFO] 51:47.575 Authenticator - Inside handleStateChange method current authState: signIn
[DEBUG] 51:47.575 VerifyContact - no unverified contact
[INFO] 51:47.578 Authenticator - authState has been updated to signedIn
[DEBUG] 51:47.581 AuthClass - getting current credentials
[DEBUG] 51:47.582 Credentials - getting credentials
[DEBUG] 51:47.582 Credentials - picking up credentials
[DEBUG] 51:47.582 Credentials - getting new cred promise
[DEBUG] 51:47.582 Credentials - checking if credentials exists and not expired
[DEBUG] 51:47.583 Credentials - credentials not changed and not expired, directly return
[DEBUG] 51:47.583 AnalyticsClass - Analytics has been disabled
[DEBUG] 51:47.584 PubSub - subscribe options undefined
[DEBUG] 51:47.584 MqttOverWSProvider - Subscribing to topic(s) topic1
[DEBUG] 51:47.584 Credentials - getting credentials
[DEBUG] 51:47.584 Credentials - picking up credentials
[DEBUG] 51:47.584 Credentials - getting new cred promise
[DEBUG] 51:47.585 Credentials - checking if credentials exists and not expired
[DEBUG] 51:47.585 Credentials - are these credentials expired?
[DEBUG] 51:47.585 Credentials - credentials not changed and not expired, directly return
[DEBUG] 51:47.586 Signer {region: "ap-southeast-2", service: "iotdevicegateway"}
[DEBUG] 51:47.590 MqttOverWSProvider - Creating new MQTT client cca4e07f-a15a-46ce-904d-483a83162018
[WARN] 52:50.152 MqttOverWSProvider - cca4e07f-a15a-46ce-904d-483a83162018 {
"errorCode": 7,
"errorMessage": "AMQJS0007E Socket error:undefined.",
"uri": "wss://a123456789d.iot.ap-southeast-2.amazonaws.com/mqtt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIAU7VGXF6UOWZIUFOM%2F20200502%2Fap-southeast-2%2Fiotdevicegateway%2Faws4_request&X-Amz-Date=20200502T055147Z&X-Amz-SignedHeaders=host&X-Amz-Signature=010d8..7dba&X-Amz-Security-Token=IQ..lk%3D"
}
ERROR: Disconnected, error code: 7
[DEBUG] 52:50.153 MqttOverWSProvider - Unsubscribing from topic(s) topic1
Any idea why I can't connect to the topic?

Who would have thought that a package was missing.
After installing
yarn add amazon-cognito-identity-js
everything worked fine on my physical iPhone.
I went to the AWS IoT Core page and published a test message from there and voilà I get the following object back
{
"message": "Hello from AWS IoT console"
}

Related

How can I attach an IoT Core policy to a Cognito Identity Id on a post authentication Lambda trigger for an Amplify deployed web application?

Situation:
I have a ReactJS web application that is deployed through AWS Amplify and uses Amplify Studio backend to handle authentication through Cognito services and aws-amplify sdk (sign in, sign up).
The web application also implements the use of react native aws-amplify sdk pubsub to subscribe to an MQTT topic in IoT Core and retrieve messages but only to authenticated users.
As per step 2 in the pubsub documentation: Attach your policy to your Amazon Cognito Identity.
If I use the AWS CLI to attach the IoT policy to the user and then I sign in with that user through the web application I am able to successfully subscribe and receive MQTT messages -- it works perectly!
Problem:
The application allows Cognito self-service user sign up (Self-registration) and expects to have many users.
I implemented a post authentication lambda trigger function in the Cognito user pool created by the Amplify service.
The lambda function runs the following sample from AWS documentation with two (2) additional console logs:
exports.handler = (event, context, callback) => {
// Send post authentication data to Cloudwatch logs
console.log ("Authentication successful");
console.log ("Trigger function =", event.triggerSource);
console.log ("User pool = ", event.userPoolId);
console.log ("App client ID = ", event.callerContext.clientId);
console.log ("User ID = ", event.userName);
*** console.log("Event = ", event);
console.log("Context = ", context);***
// Return to Amazon Cognito
callback(null, event);
};
I then authenticate with the user again through the application and go to CloudWatch logs for that Lambda function.
This is the information I get from logging Event and Context after post authentication trigger:
Event log:
{
version: '1',
region: 'us-east-1',
userPoolId: 'us-east-1_*********',
userName: '4eea4a48-92b6-45da-b26e-*********',
callerContext: {
awsSdkVersion: 'aws-sdk-unknown-unknown',
clientId: '*********'
},
triggerSource: 'PostAuthentication_Authentication',
request: {
userAttributes: {
sub: '4eea4a48-92b6-45da-b26e-*********',
'cognito:email_alias': '*********.com',
'cognito:user_status': 'CONFIRMED',
email_verified: 'true',
name: 'asdfasdf',
email: '*********.com'
},
newDeviceUsed: false
},
response: {}
}
Context log:
{
callbackWaitsForEmptyEventLoop: [Getter/Setter],
succeed: [Function (anonymous)],
fail: [Function (anonymous)],
done: [Function (anonymous)],
functionVersion: '$LATEST',
functionName: 'userAccess_iotCore_attachPolicyToCognitoIdentityID',
memoryLimitInMB: '128',
logGroupName: '/aws/lambda/userAccess_iotCore_attachPolicyToCognitoIdentityID',
logStreamName: '2023/01/13/[$LATEST]4eb4287aa4db4dd8a6b6efd810a7***',
clientContext: undefined,
identity: undefined,
invokedFunctionArn: 'arn:aws:lambda:us-east-1:*********:function:userAccess_iotCore_attachPolicyToCognitoIdentityID',
awsRequestId: 'bf6afd1c-117c-4a9e-9d3b-*********',
getRemainingTimeInMillis: [Function: getRemainingTimeInMillis]
}
The big issue here is that context.identity is undefined so I am not able to get that authenticated user's Amazon Cognito Identity Id to attach the required IoT policy for PubSub to work through the application.
Questions:
How can I get the Amazon Cognito Identity Id after post authentication trigger to then attach an IoT policy?
From the web application using the aws-amplify sdk I am able to get this Id after sign in. Is there any API I can use from the application to attach this policy?
Thanks.

AWS Cognito created by Serverless doesn't send confirmation emails

I'm working on an serverless authentication and I've got a problem with sending confirmation email.
When I create a user by AWS Console, the user is created and email with confirmation code is sent.
But when I do a request to API, user is created in Cognito but no email is sent.
What could be wrong with my code or request?
This is Cognito resource defined in serverless.yml:
CognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: ${self:service}-${self:provider.stage}-user-pool
UsernameAttributes:
- email
AutoVerifiedAttributes:
- email
EmailConfiguration:
EmailSendingAccount: COGNITO_DEFAULT
And it is request which I send to API:
POST / HTTP/1.1
Host: cognito-idp.eu-central-1.amazonaws.com
X-Amz-Target: AWSCognitoIdentityProviderService.SignUp
X-Amz-User-Agent: aws-amplify/0.1.x js amplify-authenticator
Content-Type: application/x-amz-json-1.1
Content-Length: ###
{
"ClientId": "####################",
"Username": "##########.##",
"Password": "####################",
"ValidationData": null
}
I resolved the problem by creating preSignUp lambda trigger with auto-confirming user:
const handler: PreSignUpTriggerHandler = async (event: PreSignUpTriggerEvent, context, callback) => {
event.response.autoConfirmUser = true;
callback(null, event);
};
Docs
AWS: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-sign-up.html
Serverless: https://www.serverless.com/framework/docs/providers/aws/events/cognito-user-pool/
I work with different framework (Amplify), but as the both frameworks use Cognito User pools, I guess the issue would be that you are missing "domain name".
In AWS Console, check your User Pool, then in (left panel) find "App integration" and then open and setup "Domain Name".
Again, I am just guessing (sorry, if my advice will not help), but I experienced similar problem.

AWS Cognito cookie storage

I'm trying to set up Cognito to use cookies instead of localStorage for credentials so that I can keep the user logged in between domains, e.g. x.foo.com and y.foo.com. The first step is to get it working on localhost but I'm stuck.
The documentation shows a simple config change should do the trick?
The following debug messages are comitted to the console:
[DEBUG] 37:08.223 AuthClass
Object { idToken: {…}, refreshToken: {…}, accessToken: {…}, clockDrift: 0 }
ConsoleLogger.js:87
[DEBUG] 37:08.228 Credentials - No Cache module registered in Amplify ConsoleLogger.js:84
[DEBUG] 37:08.230 Credentials - set credentials from session ConsoleLogger.js:84
[DEBUG] 37:08.230 Credentials - No Cognito Federated Identity pool provided ConsoleLogger.js:84
[DEBUG] 37:08.230 AuthClass - cannot get cognito credentials No Cognito Federated Identity pool provided ConsoleLogger.js:94
[DEBUG] 37:08.231 AuthClass - Failed to get user from user pool ConsoleLogger.js:84
[ERROR] 37:08.232 AuthClass - Failed to get the signed in user No current user
It seems when you specify the cookieStorage config you need to manually apply a cache instance? How do I do that and will it solve the problem?
This config works:
{
region: 'eu-west-1',
userPoolId: 'eu-west-1_XXXXXX',
userPoolWebClientId: 'XXXXXX',
mandatorySignIn: false,
cookieStorage: {
domain: 'localhost',
secure: false,
path: '/',
expires: 365,
},
}
In particular, secure must be false for localhost unless you are using https (Firefox ignores this for localhost, but Chrome and Safari don't).

AWS Elastic search service - IAM Role (Refresh session token - Node.js)

I'm using AWS Elastic search service and written a Node.js wrapper (runs within a ECS dockerized container) . IAM roles are used to create a singleton connection with Elastic search.
I'm trying to refresh my session token by checking AWS.config.credentials.needsRefresh() before every request - however it always returns true even after the session has expired. Obviously AWS is complaining with a 403 error. Any ideas will be greatly appreciated .
var AWS = require('aws-sdk');
var config = require('config');
var connectionClass = require('http-aws-es');
var elasticsearch = require('elasticsearch');
AWS.config.getCredentials(function() {
AWS.config.update({
credentials: new AWS.Credentials(AWS.config.credentials.accessKeyId,AWS.config.credentials.secretAccessKey,AWS.config.credentials.sessionToken),
region: 'us-east-1'
});
}
)
var client = new elasticsearch.Client({
host: `${config.get('elasticSearch.host')}`,
log: 'debug',
connectionClass: connectionClass,
amazonES: {
credentials: new AWS.EnvironmentCredentials('AWS')
}
});
module.exports = client;

AWS Appsync 401 and 403 errors in React Native

I've been pulling out my hair trying to set Appsync and Cognito in my React Native app.
I've tried the two following ways:
Amplify.configure(config);
OR
Amplify.configure({
Auth: {
region: config.aws_cognito_region, // REQUIRED - Amazon Cognito Region
userPoolId: config.aws_user_pools_id, // OPTIONAL - Amazon Cognito User Pool ID
userPoolWebClientId: config.aws_user_pools_web_client_id, // User Pool App Client ID
},
});
AND
const client = new AWSAppSyncClient({
url: appSyncConfig.graphqlEndpoint,
region: appSyncConfig.region,
auth: {
type: appSyncConfig.authType,
jwtToken: async () => (await Auth.currentSession()).getIdToken().getJwtToken(),
},
});
OR
const client = new AWSAppSyncClient({
url: appSyncConfig.graphqlEndpoint,
region: appSyncConfig.region,
auth: {
type: appSyncConfig.authType,
apiKey: appSyncConfig.apiKey,
},
});
I've also followed these two tutorials Tackling user auth, Building a notes app.
In both cases, I get the following error in GraphQL with no description:
Error: Network error: Response not successful: Received status code
This is while in Authorization Type is Amazon Cognito User Pool. I've also tried AWS Identity and Access Management (IAM), but that gives me a 403 error. Can someone point me in a direction where I can debug this further?
It might be caused a typo in the docs / article you've read. Trying replacing :
auth: {
type: appSyncConfig.authType,
apiKey: appSyncConfig.apiKey
}
with :
auth: {
type: appSyncConfig.authenticationType,
apiKey: appSyncConfig.apiKey
}
I have the following code and its working for me:
import Amplify, { Auth } from 'aws-amplify';
import API, { graphqlOperation } from '#aws-amplify/api'
window.LOG_LEVEL = 'DEBUG';
Amplify.configure({
Auth: {
"identityPoolId":'ap-southeast-1:xxxxxx',
"mandatorySignIn": false,
"region": "ap-southeast-1",
"userPoolId": "ap-southeast-1_xxxx",
"userPoolWebClientId": "xxxxxxx"
},
API:{
"aws_appsync_graphqlEndpoint": 'https://xxxx.ap-southeast-1.amazonaws.com/graphql',
"aws_appsync_region": 'ap-southwest-1',
"aws_appsync_authenticationType": 'AMAZON_COGNITO_USER_POOLS',
"aws_appsync_apiKey": 'null',
}
});