I am just trying to get a basic hello world running with NextJS and aws-amplify but it seems the moment I npm install the two libraries
aws-amplify & aws-amplify-react
I get 'react module missing' & window is not defined.
import React from 'react'
import Amplify from 'aws-amplify';
Amplify.configure({
Auth: {
// REQUIRED - Amazon Cognito Identity Pool ID
identityPoolId: 'XX-XXXX-X:XXXXXXXX-XXXX-1234-abcd-1234567890ab',
// REQUIRED - Amazon Cognito Region
region: 'XX-XXXX-X',
// OPTIONAL - Amazon Cognito User Pool ID
userPoolId: 'XX-XXXX-X_abcd1234',
// OPTIONAL - Amazon Cognito Web Client ID
userPoolWebClientId: 'XX-XXXX-X_abcd1234',
}
});
export default class extends React.Component {
static async getInitialProps({ req }) {
const userAgent = req ? req.headers['user-agent'] : navigator.userAgent
return { userAgent }
}
render() {
return (
<div>
Hello World
<style jsx>{`
`}</style>
</div>
)
}
}
You need to make some kind of polyfill to avoid that window is not defined error. Also maybe you need to check your node_modules folder to see if react is correctly installed.
The polyfill example:
```
(<any>global).window = (<any>global).window || {};
(<any>global).localStorage = (<any>global).localStorage || {
store: {},
getItem(key){
if (this.store[key]) {
return this.store[key];
}
return null;
},
setItem(key, value){
this.store[key] = value;
},
removeItem(key){ delete this.store[key]; }
};
```
Try this. Install tslib and then test
npm install tslib
Related
I'm trying to setup SignInWithApple on my webpage. Currently there is the basic auth and the google auth. Because of some required fields in my current user pool which not work together with apple, I created a second user pool. Now I am trying to make both of them work by switching the configuration.
manual config (not using the cli for the aws-exports)
export default () => {
return {
default: {
region: process.env.AWS_COGNITO_REGION,
userPoolId: process.env.AWS_COGNITO_USER_POOL_ID_DEFAULT,
userPoolWebClientId: process.env.AWS_COGNITO_APP_CLIENT_ID_DEFAULT,
mandatorySignIn: false,
oauth: {
domain: process.env.AWS_COGNITO_DOMAIN_DEFAULT,
scope: [
'profile',
'phone',
'openid',
'email',
'aws.cognito.signin.user.admin'
],
redirectSignIn: process.env.AWS_COGNITO_REDIRECT_SIGN_IN,
redirectSignOut: process.env.AWS_COGNITO_REDIRECT_SIGN_OUT,
responseType: 'code'
}
},
SignInWithApple: {
region: process.env.AWS_COGNITO_REGION,
userPoolId: process.env.AWS_COGNITO_USER_POOL_ID_APPLE,
userPoolWebClientId: process.env.AWS_COGNITO_APP_CLIENT_ID_APPLE,
mandatorySignIn: false,
oauth: {
domain: process.env.AWS_COGNITO_DOMAIN_APPLE,
scope: ['openid'],
redirectSignIn: process.env.AWS_COGNITO_REDIRECT_SIGN_IN,
redirectSignOut: process.env.AWS_COGNITO_REDIRECT_SIGN_OUT,
responseType: 'code'
}
}
}
}
And then I have a vue component with a Google and Apple login button, triggering the same function but passing either "Google" or "SignInWithApple".
import Amplify, { Auth } from 'aws-amplify'
import amplifyResources from '#/plugins/amplifyResources'
export default {
data() {
return {
bucket: null
}
},
methods: {
federatedAuth(provider) {
if ('SignInWithApple' === provider)
this.bucket = amplifyResources().SignInWithApple
if ('Google' === provider)
this.bucket = amplifyResources().default
Amplify.configure(this.bucket) **RECONFIGURE OF AMPLIFY CONFIG **
Auth.federatedSignIn({ provider })
}
}
}
And I am getting the following error:
34:03.80 AuthError -
Error: Amplify has not been configured correctly.
The configuration object is missing required auth properties.
So the funny thing is, if I init the "default" or "apple" config as a plugin, which I link and load in my nuxt.config.js file, the authentication works. The users are getting registered and logged in. With google on the default user pool auth or with apple at the new user pool. Both work then.
But I am trying to switch the userpool in some component directly, based on the auth method the user is choosing. There I am "reconfiguring" the amplify config with Amplify.configure(this.bucket). From that point I am getting the error message from above. But the config is indeed switching. Otherwise I wouldn't get redirected to apple on the apple button or to google on the google button. Both have completely different configs. So I know the "reconfiguration" is happening. I also know that in general I have the right properties in the default & apple config, since the login for both is working, if I set the config as a plugin in the nuxt.config.js file.
I'm just now diving into Cognito. The AWS setup has been fairly straight-forward, easy.
We have a variety of apps, webapps, and services and we'd like those to make use of the Cognito service. I've experience setting up similar with Auth0, but because we've been leveraging a number of Amazon Web Services, it really makes sense to use Cognito as well.
Everywhere I look, every guide eventually references Amplify client-side library and cli. We have existing apps and services, and really don't want to change tooling or import anything unnecessary to add bloat and complexity. Is there a way to use Cognito service without Amplify libraries? Is there a lightweight Cognito-only client library for interfacing with the Cognito service, authentication-and-authorization flow?
Update 03 Dec 2021
After re:Invent 2021, "Amplify Admin UI" was renamed to "Amplify Studio". With extra powers now:
automatically translates designs made in Figma to human-readable React UI component code
https://aws.amazon.com/blogs/mobile/aws-amplify-studio-figma-to-fullstack-react-app-with-minimal-programming/
===============
Original Answer
To start, I want to clarify that "Amplify" is an umbrella term for multiple things. We have:
Amplify Libraries (UI/JS)
Amplify CLI (to create cloud-native applications)
Amplify Console (ci/cd and hosting for full-stack web apps)
Amplify Admin UI (UI to create and configure full-stack web apps)
You can check the homepage for more clarification - https://docs.amplify.aws/
Is there a lightweight Cognito-only client library for interfacing with the Cognito service, authentication-and-authorization flow?
Behind the scenes, Amplify uses amazon-cognito-identity-js library to interface with Amazon Cognito. You can install that directly via npm install amazon-cognito-identity-js.
The source code has been moved to the Amplify Libraries (e.g. amplify-js) repository. Once again, is part of the "Amplify" umbrella under the first category "Amplify Libraries".
Is there a way to use Cognito service without Amplify libraries?
Another approach that you can do, is to use Amazon Cognito as an OAuth server. When you create an Amazon Cognito Hosted UI Domain, it provides you an OAuth 2.0 compliant authorization server.
You can create your own API/Backend for Signup/Login endpoints and exchange tokens/credentials with the Amazon Cognito OAuth server without using aws-sdk or any 3rd party dependency library.
I wrote a walkthrough example, how to configure your User Pool, endpoints that you need to talk to using Node.js, you can find it here: https://github.com/oieduardorabelo/node-amazon-cognito-oauth
You can follow the same idea for any other language.
As mentioned by #oieduardorabelo, you can simply install 'amazon-cognito-identity-js' where you can also find well done examples on npm.
Here is my test code to easily understand this lib. You must have already built the infrastructure on AWS (userPool, userClient and add a new user to test sign in - in my case the user has to change the password on first login so I added this use case on my script):
import * as AmazonCognitoIdentity from 'amazon-cognito-identity-js';
var authenticationData = {
Username: 'email',
Password: 'password',
};
var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
var poolData = {
UserPoolId: 'us-east-1_userpoolid',
ClientId: '26pjexamplejpkvt'
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
var cognitoUser = userPool.getCurrentUser();
console.log(cognitoUser);
if (!cognitoUser) {
var userData = {
Username: authenticationData.Username,
Pool: userPool
};
var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
var accessToken = result.getAccessToken().getJwtToken();
var idToken = result.idToken.jwtToken;
console.log('Success', accessToken, idToken);
},
newPasswordRequired: function (userAttributes, requiredAttributes) {
delete userAttributes.email_verified;
cognitoUser.completeNewPasswordChallenge('DemoPassword1!', userAttributes, {
onSuccess: (data) => {
console.log(data);
},
onFailure: function (err) {
alert(err);
}
});
},
onFailure: function (err) {
alert(err);
},
});
}
If someone is interested in setup this test project from scratch run:
npm init -y
npm i -D webpack webpack-cli
npm i amazon-cognito-identity-js
in webpack.config.js:
var path = require('path');
module.exports = {
entry: './src/app.js',
mode: 'development',
output: {
path: path.resolve(__dirname, "dist"),
filename: 'main.js',
}
}
Create a new file in ./src/app.js where add the previous amazonCognitoIdentity code with the right AWS info ref and create ./dist/index.html whith:
...
<body>
<script src="main.js"></script>
</body>
in package.json add script "watch":
...
"scripts": {
"watch": "webpack --watch",
}
Finally run it:
npm run watch
and open the index.html directly on the browser with dev console as well.
Hopefully useful for someone.
As a result of research on the topic of using Amazon Cognito without Amplify in React, I came across such a sandbox. Switching from router 5 to router 6 probably won't be a problem. The main gold here is this hook. The rest of the implementation can be found in the sandbox: https://codesandbox.io/s/cognito-forked-f02htu
const Pool_Data = {
UserPoolId: "xxx",
ClientId: "yyy"
};
export default function useHandler() {
const [state, setstate] = useState({
loading: false,
isAuthenticated: false
});
const { loading, isAuthenticated } = state;
const userPool = new CognitoUserPool(Pool_Data);
const getAuthenticatedUser = useCallback(() => {
return userPool.getCurrentUser();
}, []);
console.log(getAuthenticatedUser());
useEffect(() => {
getAuthenticatedUser();
}, [getAuthenticatedUser]);
const signOut = () => {
return userPool.getCurrentUser()?.signOut();
};
console.log("I am here", getAuthenticatedUser()?.getUsername());
return {
loading,
isAuthenticated,
userPool,
getAuthenticatedUser,
signOut
};
}
I wrote an article a couple of years ago explaining how to do this.
The article talks about Amplify but as was mentioned in another response, that's more of an umbrella term, in the article we are using mostly UI components provided by the Amplify project.
you can find it here: https://medium.com/#mim3dot/aws-amplify-cognito-part-2-ui-components-935876fabad3
I used Amplify for authentication and it seems to work fine. Now I want to setup an admin app for CRUD with the user pool. It seems that I have to leave Amplify and use the JavaScript SDK to use the appropriate api's.
How does this work? I've failed at figuring out how to get the tokens I receive in Amplify into AWS.config or wherever they are supposed to go.
What a struggle this had been. It seems that the code in the docs is dated and what little advice is online is worse. I suspect that is because an Amplify object contains the config options and I have to bring those to the AWS.config object. I've tried below and failed. Any idea what I need to do? I'm sure answers here will be useful for many AWS newbies.
I have this code in my Angular app but thinking about Lambda. I have an EC2 server with Node.js as another option.
This is for dev on my MBP but I'm integrating it with AWS.
With the code below I get an error message that contains in part:
Error in getCognitoUsers: Error: Missing credentials in config
at credError (config.js:345)
at getStaticCredentials (config.js:366)
at Config.getCredentials (config.js:375)
The JWT I inserted into the object below is the AccessKeyID that is in my browser storage and I used for authentication.
In console.log cognitoidentityserviceprovider I have this object, in part:
config: Config
apiVersion: "2016-04-18"
credentialProvider: null
credentials: "eyJraWQiOiJwaUdRSnc4TWtVSlR...
endpoint: "cognito-idp.us-west-2.amazonaws.com"
region: "us-west-2"
endpoint: Endpoint
host: "cognito-idp.us-west-2.amazonaws.com"
hostname: "cognito-idp.us-west-2.amazonaws.com"
href: "https://cognito-idp.us-west-2.amazonaws.com/"
These functions flow down as a sequence. I left some vars in the bodies in case someone wants to know how to get this data from the user object. I used them in various attempts build objects but most aren't needed here, maybe. The all produce the correct results from the Amplify user object.
import { AmplifyService } from 'aws-amplify-angular';
import Amplify, { Auth } from 'aws-amplify';
import { CognitoIdentityServiceProvider } from 'aws-sdk';
import * as AWS from 'aws-sdk';
#Injectable()
export class CognitoApisService {
private cognitoConfig = Amplify.Auth.configure(); // Data from main.ts
private cognitoIdPoolID = this.cognitoConfig.identityPoolId;
private cognitoUserPoolClient = this.cognitoConfig.userPoolWebClientId;
private cognitoIdPoolRegion = this.cognitoConfig.region;
private cognitoUserPoolID = this.cognitoConfig.userPoolId;
...
constructor(
private amplifyService: AmplifyService,
) { }
public getAccessToken() {
return this.amplifyService
.auth() // Calls class that includes currentAuthenticaedUser.
.currentAuthenticatedUser() // Sets up a promise and gets user session info.
.then(user => {
console.log('user: ', user);
this.accessKeyId = user.signInUserSession.accessToken.jwtToken;
this.buildAWSConfig();
return true;
})
.catch(err => {
console.log('getAccessToken err: ', err);
});
}
public buildAWSConfig() {
// Constructor for the global config.
this.AWSconfig = new AWS.Config({
apiVersion: '2016-04-18',
credentials: this.accessKeyId,
region: this.cognitoIdPoolRegion
});
this.cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider(this.AWSconfig);
/* This doesn't get creds, probably because of Amplify.
this.cognitoidentityserviceprovider.config.getCredentials(function(err) {
if (err) console.log('No creds: ', err); // Error: Missing credentials
else console.log("Access Key:", AWS.config.credentials.accessKeyId);
});
*/
console.log('cognitoidentityserviceprovider: ', this.cognitoidentityserviceprovider);
this.getCognitoUsers();
}
public getCognitoUsers() {
// Used for listUsers() below.
const params = {
UserPoolId: this.cognitoUserPoolID,
AttributesToGet: [
'username',
'given_name',
'family_name',
],
Filter: '',
Limit: 10,
PaginationToken: '',
};
this.cognitoidentityserviceprovider.listUsers(params, function (err, data) {
if
(err) console.log('Error in getCognitoUsers: ', err); // an error occurred
else
console.log('all users in service: ', data);
});
}
The one problem was that the credentials needs the whole user object from Amplify, not just the access token as I show above. By the way, I have the Cognito settings in main.ts. They can also go in environment.ts. A better security option is to migrate this to the server side. Not sure how to do that yet.
// Constructor for the global config.
this.AWSconfig = new AWS.Config({
apiVersion: '2016-04-18',
credentials: this.accessKeyId, // Won't work.
region: this.cognitoIdPoolRegion
});
My complete code is simpler and now an observable. Notice another major issue I had to figure out. Import the AWS object from Amplify, not the SDK. See below.
Yeah, this goes against current docs and tutorials. If you want more background on how this has recently changed, even while I was working on it, see the bottom of this Github issue. Amplify is mostly for authentication and the JavaScript SDK is for the service APIs.
import { AmplifyService } from 'aws-amplify-angular';
// Import the config object from main.ts but must match Cognito config in AWS console.
import Amplify, { Auth } from 'aws-amplify';
import { AWS } from '#aws-amplify/core';
import { CognitoIdentityServiceProvider } from 'aws-sdk';
// import * as AWS from 'aws-sdk'; // Don't do this.
#Injectable()
export class CognitoApisService {
private cognitoConfig = Amplify.Auth.configure(); // Data from main.ts
private cognitoIdPoolRegion = this.cognitoConfig.region;
private cognitoUserPoolID = this.cognitoConfig.userPoolId;
private cognitoGroup;
private AWSconfig;
// Used in listUsers() below.
private params = {
AttributesToGet: [
'given_name',
'family_name',
'locale',
'email',
'phone_number'
],
// Filter: '',
UserPoolId: this.cognitoUserPoolID
};
constructor(
private amplifyService: AmplifyService,
) { }
public getCognitoUsers() {
const getUsers$ = new Observable(observer => {
Auth
.currentCredentials()
.then(user => {
// Constructor for the global config.
this.AWSconfig = new AWS.Config({
apiVersion: '2016-04-18',
credentials: user, // The whole user object goes in the config.credentials field! Key issue.
region: this.cognitoIdPoolRegion
});
const cognitoidentityserviceprovider = new CognitoIdentityServiceProvider(this.AWSconfig);
cognitoidentityserviceprovider.listUsers(this.params, function (err, userData) {
if (err) {
console.log('Error in getCognitoUsers: ', err);
} else {
observer.next(userData);
}
});
});
});
return getUsers$;
}
Let's call this service from a component. I'm putting the JS object parsing in the component but for now, I left the console.log here for you to get started and see if the code works for your app.
// Called from button on html component.
public getAllCognitoUsers() {
this.cognitoApisService.getCognitoUsers()
.subscribe(userData => {
console.log('data in cognito component: ', userData);
})
}
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)
}
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();