I noticed that the developers have updated the contents about webpack. but the setup for webpack is incomplete. AWS should provide working examples for the use of Angular 1.X (forget about 2.0 right now) or React framework.
Considering the current trend, I think that a working example using React is a must for this library. Angular 1.x example is also needed. But the Angular team is heading for Angular 2.0, which is full of issues at this point.
Logging in and out is the most critical functionality, instead of future directions, of this library, even of a website.
There is a reasonably concise React + Firebase framework here. https://blog.tighten.co/react-101-building-a-gif-search-engine
It would be great if a similar React + AWS tutorial is available, which will definitely save lives of developers.
I guess that your team has no clear idea of how this library is incorporated into a Javascript frame like React or Angular.
Your team should hire more competent developers to make this library work.
Where is the clear work flow from email confirmation to token management?
Another question is: your team is developing this library as your job or as an amateur interest?
If it is your job, you guys are imcompenent!
If it is just your after-work hobby, why AWS treats this critical part of cloud service as a trash?
Hello,
As a company, we don't usually comment on the future direction of products and features, but I will mention this feature request within the team so that we prioritize it accordingly.
Also, see the relevant discussion about the AWS SDK which we are dependent below
https://github.com/aws/aws-sdk-js/issues/740
After two weeks struggle, I am finally able to use this library for my React/Router/Redux project using AWS S3 as static hosting.
I re-opened this issue, wanting to tell other developers that #79 provided correction to the User Case 4.
Warning: the Use Case 4 can NOT work, the correction lies in the closed issue #79 and itrestian has known that. But for some reason, he failed to provide correction to the Use Case 4 on the homepage. I am just telling you the truth!
Special thanks to @mgoria, who provided the correction!
Not really sure what the confusion is but this library handles User Pools so the use cases presented here are use cases related to User Pools features for user management and authentication like: creating a user with User Pools, confirming that user, authenticating with User Pools, retrieving, updating user attributes within User Pools.
Optionally, with the ID token provided by User Pools, you can obtain AWS credentials by calling Cognito Identity which is a different service than User Pools. Cognito Identity is hosted in the AWS SDK so not part of this library.
Obviously, if particular use cases emerge (not everyone is interested only in User Authentication and management), or if developers feel something is unclear, we have always accepted pull requests to update the documentation.
And documentation related to https://github.com/aws/amazon-cognito-identity-js/issues/79 updated on 13th July when I closed issue https://github.com/aws/amazon-cognito-identity-js/issues/79
See update by Sarah related to Use case 16 (use case 17 now).** Integrating User Pools with Cognito Identity.
https://github.com/aws/amazon-cognito-identity-js/commit/bd56b8cecbfcd234cdf1aa49093d79c5bbf7f533#diff-04c6e90faac2675aa89e2176d2eec7d8R359
@itrestian
Thank you for your response.
I spent some time making a demo written in React in hope of helping other developers, since I promised to do so when I was struggling with this library. The following explanation may not be very clear, since Chinese is my first language.
I organize this post into the following 4 sections:
(1) About the ClientId
(2) About the cognitoUser
(3) A working example in a React framework
(4) minor yet critical details
ClientId and cognitoUser are the two variables appearing in Use Case 1. But these two variables are not defined or explained. itrestian recommended me to generate a client using the AWS CLI, yet I recommend making a client using the AWS Cognito Console
(1) ClientId
We can generate one Client when setting up the User Pools. Browser use represents one Client. One Client can have many users who use browser to log in. Hit Create App and you will get a new ClientId. A good OAuth2 tutorial (http://scottksmith.com/blog/2014/07/02/beer-locker-building-a-restful-api-with-node-oauth2-server/) explains the idea of Client quite well.

A Cognito Identity Pool should be created with the following setup:
Use Cognito as the Authentication Provider.
Input the User Pool Id and your App Client Id (browser is also a client). This App Client Id is the browser Client Id generated from the previous step.
(2) cognitoUser
I saw cognitoUser is defined repeatedly in the Use Cases. I think it is confusing. We can use ONE cognitoUser variable in a module and implement the workflow from email confirmation to token management. I made the following demo to illustrate how variables in all the Use Cases should be managed in a module. The following code also shows the use of ReduxForm. Clicking on Submit1 will signUp the user; then find out the 6-bit code in your email. Then input the code in emailCode and then click on Submit2. If everything is OK, click on Submit3. Now you will get the tokens.
import React, {Component} from 'react'
import {reduxForm} from 'redux-form'
import {connect} from 'react-redux'
import {AWSConfig} from '../../aws.config'
AWSCognito.config.region = 'us-east-1'
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(AWSConfig.poolData)
/*I stored the poolData in aws.config.js*/
var cognitoUser = {}
class Signup extends Component {
constructor(context) {
super(context)
}
componentDidMount = () => {
}
handleSubmit2 = (credentials) => {
console.log("handleSubmit2()", credentials)
if(Object.keys(cognitoUser).length != 0)
{
cognitoUser.confirmRegistration(credentials.emailCode, true, function(err, result) {
if(err) {
alert(err)
return
}
console.log('Call result: ' + result)
})
}
}
handleSubmit3 = (credentials) => {
console.log("handleSubmit3()", credentials)
if(Object.keys(cognitoUser).length != 0) {
var authencationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails({
Username: credentials.username,
Password: credentials.password
})
cognitoUser.authenticateUser(authencationDetails, {
onSuccess: function (result) {
console.log('access token' + result.getAccessToken().getJwtToken())
console.log("onSuccess", Object.keys(AWS.config))
AWS.config.region = 'us-east-1'
AWS.config.apiVersion = '2016-04-18'
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:50f31b56-043f-43b1-aefd-004fb1e441b9',
Logins: {
'cognito-idp.us-east-1.amazonaws.com/us-east-1_8ip87RSdO': result.getIdToken().getJwtToken()
}
})
AWS.config.credentials.refresh((error) => {
if (error) {
console.error(error);
} else {
console.log('Successfully logged!')
}
})
},
onFailure: function (err) {
alert(err)
}
})
}
}
handleFormSubmit = (credentials) => {
console.log("handleFormSubmit()")
var attributeList = []
var attributeEmail = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserAttribute({
Name: 'email',
Value: credentials.email
})
attributeList.push(attributeEmail)
userPool.signUp(credentials.username, credentials.password, attributeList, null, function(err, result) {
if(err) {
alert(err)
return
}
cognitoUser = result.user
console.log('Username: ' + cognitoUser.getUsername())
})
}
render() {
const {handleSubmit, fields: {username, password, email, emailCode}} = this.props
return (
<section>
<div className='right-col' role='main'>
<form className='form-horizontal' onSubmit={handleSubmit(this.handleFormSubmit)}>
<div className='form-group'>
<label>Username</label>
<input type='text' className='form-control' {...username}/>
</div>
<div className='form-group'>
<label>Password</label>
<input type='password' className='form-control' {...password}/>
</div>
<div className='form-group'>
<label>Email</label>
<input type='email' className='form-control' {...email}/>
</div>
<div className='form-group'>
<label>Email Code</label>
<input {...emailCode} type='text' className='form-control'/>
</div>
<div className='form-group'>
<button type='submit'>Submit1</button>
<button type='button' onClick={() => this.handleSubmit2(this.props.values)}>Submit2</button>
<button type='button' onClick={() => this.handleSubmit3(this.props.values)}>Submit3</button>
</div>
</form>
</div>
</section>
)
}
}
export default reduxForm({
form: 'signup',
fields: ['username', 'password', 'email', 'emailCode']
})(Signup)
(3) A working example in a React framework
the index.html can be made in webpack.config.js. I avoid using template such as jade.
I just paste the files in the following order
(3.1) webpack config file for development
(3.2) index.html
(3.3.) package.json
(3.4.) client.js
(3.5) routes.js
require('babel-polyfill')
// Webpack config for creating the production bundle.
var path = require('path')
var webpack = require('webpack')
var CleanPlugin = require('clean-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var strip = require('strip-loader')
var CopyWebpackPlugin = require('copy-webpack-plugin')
var projectRootPath = path.resolve(__dirname, '../')
var srcPath = path.resolve(__dirname, '../', 'src')
var buildPath = path.resolve(projectRootPath, './build')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
devtool: 'inline-source-map',
context: path.resolve(__dirname, '../'),
entry: {
'main': [
'bootstrap-sass!./src/theme/bootstrap.config.js',
'font-awesome-webpack!./src/theme/font-awesome.config.js',
'./src/client.js'
]
},
output: {
path: buildPath,
filename: '[name].js'
},
module: {
loaders: [
{ test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel'},
{ test: /\.json$/, loader: 'json-loader' },
{ test: /\.less$/, loader: 'style!css?modules&importLoaders=2&sourceMap&localIdentName=[local]___[hash:base64:5]!autoprefixer?browsers=last 2 version!less?outputStyle=expanded&sourceMap' },
{ test: /\.scss$/, loader: 'style!css?modules&importLoaders=2&sourceMap&localIdentName=[local]___[hash:base64:5]!autoprefixer?browsers=last 2 version!sass?outputStyle=expanded&sourceMap' },
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/font-woff" },
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/font-woff" },
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/octet-stream" },
{ test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: "file" },
{ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=image/svg+xml" },
{ test: /(png|jpg|gif)$/, loader: 'url-loader?limit=10240' }
]
},
progress: true,
resolve: {
modulesDirectories: [
'src',
'node_modules'
],
extensions: ['', '.json', '.js', '.jsx']
},
devServer: {
contentBase: projectRootPath,
historyApiFallback: true,
hot: true,
inline: true,
progress: true
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new CopyWebpackPlugin([
{from: path.join(srcPath, 'assets', 'favicon.ico'), to: '/'},
{from: path.join(srcPath, 'assets', 'dist', 'amazon-cognito-identity.min.js'), to: '/dist'},
{from: path.join(srcPath, 'assets', 'dist', 'amazon-cognito-identity.min.js.map'), to: '/dist'},
{from: path.join(srcPath, 'assets', 'dist', 'aws-cognito-sdk.js'), to: '/dist'},
{from: path.join(srcPath, 'assets', 'dist', 'aws-cognito-sdk.min.js'), to: '/dist'},
{from: path.join(srcPath, 'assets', 'dist', 'aws-cognito-sdk.min.js.map'), to: '/dist'},
{from: path.join(srcPath, 'assets', 'dist', 'aws-sdk.min.js'), to: '/dist'},
{from: path.join(srcPath, 'assets', 'dist', 'jsbn.js'), to: '/dist'},
{from: path.join(srcPath, 'assets', 'dist', 'jsbn2.js'), to: '/dist'},
{from: path.join(srcPath, 'assets', 'dist', 'sjcl.js'), to: '/dist'}
]),
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
}),
new HtmlWebpackPlugin({
title: '鍖楁枟瀹夊悍',
template: path.join(srcPath, 'assets', 'index.html')
}),
// ignore dev config
new webpack.IgnorePlugin(/\.\/dev/, /\/config$/),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
PORT: JSON.stringify(process.env.PORT),
APIPORT: JSON.stringify(process.env.APIPORT)
},
__CLIENT__: true,
__SERVER__: false,
__DEVELOPMENT__: true,
__DEVTOOLS__: true
})
]
}
<!DOCTYPE html>
<html lang='zh-cn'>
<head>
<meta charset='UTF-8'/>
<script src="dist/jsbn.js"></script>
<script src="dist/jsbn2.js"></script>
<script src="dist/sjcl.js"></script>
<script src="dist/aws-cognito-sdk.min.js"></script>
<script src="dist/amazon-cognito-identity.min.js"></script>
<script src="dist/aws-sdk.min.js"></script>
</head>
<body>
<div id='app'></div>
</body>
</html>
{
"name": "bdjkkj",
"version": "0.1.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "better-npm-run build",
"dev": "better-npm-run dev",
"prod": "better-npm-run prod",
"start-prod-api": "better-npm-run start-prod-api",
"test": "better-npm-run test"
},
"betterScripts": {
"prod": {
"command": "node ./src/server.js",
"env": {
"NODE_PATH": "./src",
"NODE_ENV": "production",
"HOSt": "localhost",
"PORT": 3000,
"APIHOSt": "localhost",
"APIPORT": 3030
}
},
"start-prod-api": {
"command": "node ./bin/api.js",
"env": {
"NODE_PATH": "./api",
"NODE_ENV": "production",
"HOSt": "localhost",
"PORT": 8080,
"APIHOSt": "localhost",
"APIPORT": 3030
}
},
"dev": {
"command": "webpack-dev-server --config ./webpack/dev.config.js",
"env": {
"NODE_PATH": "./src",
"NODE_ENV": "development",
"HOSt": "localhost",
"PORT": 8080,
"APIHOSt": "localhost",
"APIPORT": 3030
}
},
"build": {
"command": "webpack -p --verbose --colors --display-error-details --config webpack/prod.config.js",
"env": {
"NODE_PATH": "./src",
"NODE_ENV": "production",
"HOSt": "localhost",
"PORT": 3000,
"APIHOSt": "localhost",
"APIPORT": 3030
}
}
},
"keywords": [],
"author": "",
"license": "MIT",
"devDependencies": {
"autoprefixer-loader": "^3.2.0",
"better-npm-run": "0.0.8",
"bootstrap-sass": "^3.3.6",
"bootstrap-sass-loader": "^1.0.10",
"clean-webpack-plugin": "^0.1.9",
"copy-webpack-plugin": "^3.0.1",
"css-loader": "^0.23.1",
"extract-text-webpack-plugin": "^1.0.1",
"font-awesome": "^4.6.3",
"font-awesome-webpack": "0.0.4",
"html-webpack-plugin": "^2.22.0",
"json-loader": "^0.5.4",
"node-sass": "^3.5.2",
"sass-loader": "^4.0.0",
"style-loader": "^0.13.0",
"webpack": "^1.12.14",
"webpack-dev-server": "^1.14.1"
},
"dependencies": {
"babel-core": "^6.5.2",
"babel-loader": "^6.2.1",
"babel-plugin-add-module-exports": "^0.1.2",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-plugin-transform-react-display-name": "^6.3.13",
"babel-plugin-transform-runtime": "^6.3.13",
"babel-polyfill": "^6.3.14",
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-preset-stage-0": "^6.3.13",
"babel-register": "^6.3.13",
"babel-runtime": "^6.3.19",
"body-parser": "^1.15.2",
"classnames": "^2.2.5",
"express": "^4.14.0",
"express-session": "^1.14.0",
"file-loader": "^0.9.0",
"firebase": "^2.4.2",
"firebase-token-generator": "^2.0.0",
"jquery": "^2.2.3",
"less": "^2.7.1",
"less-loader": "^2.2.3",
"morgan": "^1.7.0",
"prettyjson": "^1.1.3",
"react": "^0.14.7",
"react-bootstrap": "^0.29.5",
"react-dom": "^0.14.7",
"react-helmet": "^2.2.0",
"react-modal": "^0.6.1",
"react-redux": "^4.0.0",
"react-router": "^2.0.0",
"react-router-bootstrap": "^0.20.1",
"react-router-redux": "^4.0.0",
"redux": "^3.0.4",
"redux-async-connect": "^1.0.0-rc4",
"redux-form": "^5.3.1",
"scroll-behavior": "^0.3.2",
"socket.io": "^1.4.8",
"strip-loader": "^0.1.2",
"style-loader": "^0.13.1",
"superagent": "^1.4.0",
"url-loader": "^0.5.7",
"webpack-isomorphic-tools": "^2.3.2"
}
}
import 'babel-polyfill'
import React from 'react'
import ReactDOM from 'react-dom'
import {Provider} from 'react-redux'
import {Router, browserHistory} from 'react-router'
import {syncHistoryWithStore} from 'react-router-redux'
import {ReduxAsyncConnect} from 'redux-async-connect'
import useScroll from 'scroll-behavior/lib/useStandardScroll'
import createStore from './redux/create'
import ApiClient from './helpers/ApiClient'
//diff
//import Firebase from 'firebase'
import getRoutes from './routes'
import {poolData} from './aws.config'
//const firebase = new Firebase('https://bdak.firebaseio.com')
const client = new ApiClient()
const _browserHistory = useScroll(() => browserHistory)()
const dest = document.getElementById('app')
const store = createStore(_browserHistory, client, window.__data)
const history = syncHistoryWithStore(_browserHistory, store)
//
AWSCognito.config.region = 'us-east-1'
//
const component = (
<Router render={(props) => <ReduxAsyncConnect {...props} helpers={{client}} filter={item => !item.deferred} /> } history={history}>
{getRoutes(store)}
</Router>
)
ReactDOM.render(
<Provider store={store} key='provider'>
{component}
</Provider>,
document.getElementById('app')
)
import React from 'react'
import {Route, IndexRoute} from 'react-router'
import {
App,
Home,
About,
Contact,
Blog,
Services,
Login,
Signup,
Account,
AccountHome,
} from 'containers'
export default (store) => {
return (
<Route path='/' component={App}>
<IndexRoute component={Home}/>
<Route component={About} path='about'/>
<Route component={Account} path='account'>
<IndexRoute component={AccountHome} />
</Route>
<Route component={Contact} path='contact'/>
<Route component={Login} path='login'/>
<Route component={Services} path='services'/>
<Route component={Signup} path='signup'/>
</Route>
)
}
(4) In the end, I recommend using the config.region and config.apiVersion as shown in the above code. Otherwise, developers may get stuck, with an generic noninformative error report agian, again, and again.
Thank you @sdruipeng
Anything that can help other developers is more than welcome.
FYI, I submitted the webpack support as an external contributor - when I started having trouble using it from webpack I saw I could make it better for everyone, but I'm still doing this (mostly) after-hours at work, so I don't have all that much time to put into this.
The current major issue with the examples is the use of AWSCognito, which it isn't clear you should get by an var AWSCognito = require('aws-sdk'); (note the example setup doesn't include babel, so we shouldn't be using import here)
The other is needing to set region globally, since:
I've put up #120 to try and get rid of that one, but there are more conservative options there too.
Where I'd _like_ to see the usage go is the top-level README is something like:
Intro blah blah blah...
## Configuration
Pretty pictures of AWS Console here...
## Setup
- [Loose Scripts](docs/setup/scripts)
- [Using Webpack](docs/setup/webpack)
## Usage
Basics:
- [Signup](docs/usage/signup)
- [Authenticate (Log-in)](docs/usage/authenticate)
- [Integrating with AWS services](docs/usage/aws-credentials)
- [Log out](docs/usage/logout)
- ...
Multi-page vs. single-page apps:
- [Get current user](docs/usage/get-current-user)
- [Token caching](docs/usage/token-caching)
- ...
Login token caching:
- ...
MFA:
- ...
Custom challenges:
- ...
...
## Examples
- [Basic multi-page HTML login form](examples/html-form)
- [Simple single-page Webpack + React + Redux){examples/react-redux)
- [Angular JS](examples/angular)
...
But I've got a bunch of weird API warts I'd be putting work into before I got around to all that:
CognitoUserPool, they are a bit strange to use now: new CognitoUser({ Username, Pool }).forgotPassword();getFoo() in favor of documented, read-only properties.aws-sdk without too much trouble since you don't need SRP.... so you can see I'm probably not going to get around to it any time soon.
@simonbuchan
It seems that you have a lot of work to do. Yet I have a different opinion.
Firstly, I do believe that the first priority of this project is you must provide an API documentation for all the related classes, especially for the most commonly used classes, such as CognitoUser. I noticed that you have write comments inline, but that is far from enough. I don't think Promise is an issue here, since developers can figure out how to use Promise in their respective environments.
Secondly, you need to explain why you use two asynchronous functions in Use Case 17, and under what conditions the tokens need to be refreshed and why you use AWS.config.credentials, which makes your code convoluted.
Thirdly, you should provide a clear workflow of signing up, logging in, and logging out. You can just do it in jQuery, and I think other developers can handle their own environments. Currently, you snippets of use cases don't make sense for a real development environment.
Finally, hire more competent developers. You guys lag behind with respects to clear work-flow, well-prepared documentation, a functional Node module, let alone working examples for various frameworks, such as Angular 1.X, Vue.js, React.js, or React-Redux.
``````
Use Case 17
var cognitoUser = userPool.getCurrentUser();
if (cognitoUser != null) {
cognitoUser.getSession(function(err, result) {
if (result) {
console.log('You are now logged in.');
// Add the User's Id Token to the Cognito credentials login map.
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'YOUR_IDENTITY_POOL_ID',
Logins: {
'cognito-idp.<region>.amazonaws.com/<YOUR_USER_POOL_ID>': result.getIdToken().getJwtToken()
}
});
}
});
}
//call refresh method in order to authenticate user and get new temp credentials
AWS.config.credentials.refresh((error) => {
if (error) {
console.error(error);
} else {
console.log('Successfully logged!');
}
});```
``````
You seem to be under the misapprehension that I work for Amazon. Since I don't, I can safely say stop being a dick. If you want to see a change made, the best way to do so is to do it yourself, and submit a PR, like I and several others have.
Be sure to make sure that it works though, your example change refreshes the token at the wrong time... because it looks like you don't understand callbacks very well and would be better off using async / await... which uses promises.
I just copy the above code from this Forum,and I do believe it is a bad practice to code like this.
Actually, I understand Promise very well.
An API documentation should describe how to use different methods. Now I have to look through the source code to figure out.
@simonbuchan Is it cool if I make a new issue for improving the docs? I'd be happy to make a start, having just implemented it in a project.
It is just hard to believe such an important part of AWS service like Cognito is in such a messy state! Developers have to look into the source code to figure out what's happening.
You can have a look at AWS Amplify which exposes a higher order auth component for this SDK. Maybe that can suit your use case. Also note that we will continue developing this library in the AWS Amplify repo.
@itrestian does this mean that this project will no longer be maintained in favour of Amplify?
It means this project is maintained as part of the Amplify github repo. Same project, different repos. You are welcome to use this project as it is from NPM (version 2.0.0) or try Amplify which exposes higher level components above this library.
Thanks for the clarification, @itrestian 馃憤
Most helpful comment
You seem to be under the misapprehension that I work for Amazon. Since I don't, I can safely say stop being a dick. If you want to see a change made, the best way to do so is to do it yourself, and submit a PR, like I and several others have.
Be sure to make sure that it works though, your example change refreshes the token at the wrong time... because it looks like you don't understand callbacks very well and would be better off using async / await... which uses promises.