DEV Community

Nader Dabit
Nader Dabit

Posted on • Updated on

The Complete Guide to User Authentication with the Amplify Framework

The AWS Amplify Authentication modules provide Authentication APIs and building blocks for developers who want to create apps with real-world production-ready user authentication.

With Amplify you can incorporate username / password based authentication as well as OAuth with Facebook, Google or Amazon.

We also provide a pre-built β€œHosted UI” that provides a full OAuth + username / password flow.

This post covers authentication for web applications. For the post on React Native, click here.

Introduction to Amazon Cognito

The Amplify Framework uses Amazon Cognito as the main authentication provider. Amazon Cognito User is a robust user directory service that handles user registration, authentication, account recovery & other operations.

Amplify interfaces with Cognito to store user data, including federation with other OpenID providers like Facebook & Google.

The Amplify CLI automates the access control policies for these AWS resources as well as provides fine grained access controls via GraphQL for protecting data in your APIs.

Most modern applications require multiple authentication options, i.e. Facebook login + Username / password login. Amazon Cognito makes this process easy by allowing you to use a single user registry to authenticate users across multiple authentication types.

In this post, you'll learn how to add authentication to your React application using both OAuth as well as username & password login.

Getting Started

Installing the Amplify CLI

To build authentication into your application with Amplify you first need to install the AWS Amplify CLI. The Amplify CLI is a command line tool that allows you to create & deploy various AWS services.

To install the CLI, we'll run the following command:

npm install -g @aws-amplify/cli

Next, we'll configure the CLI with a user from our AWS account:

amplify configure

For a video walkthrough of the process of configuring the CLI, click here.

Creating the React project

Next, we'll create the React application we'll be working with:

npx create-react-app rn-amplify

cd rn-amplify

Now, we'll install the Amplify library:

npm install aws-amplify

Creating the Amplify project

Now we can now initialize a new Amplify project from within the root of our React application:

amplify init

Here we'll be guided through a series of steps:

  • Enter a name for the project: amplifyauth (or your preferred project name)
  • Enter a name for the environment: local (or your preferred environment name)
  • Choose your default editor: Visual Studio Code (or your text editor)
  • Choose the type of app that you're building: javascript
  • What javascript framework are you using: react
  • Source Directory Path: src
  • Distribution Directory Path: build
  • Build Command: npm run-script build
  • Start Command: npm run-script start
  • Do you want to use an AWS profile? Y
  • Please choose the profile you want to use: YOUR_USER_PROFILE

Now, our Amplify project has been created & we can move on to the next steps.

Creating our App IDs

In our app we'll be having three types of authentication:

  • Facebook (OAuth)
  • Google (OAuth)
  • Cognito (username + password)

Next we'll need to create Facebook & Google Apps in order to get an App ID & App Secret for each of them.

For instructions around the Facebook setup click here.

For instructions around the Google setup click here.

After you've created the Facebook & Google OAuth credentials move on to the next step.

Creating & configuring the authentication service

Now that our Amplify project has been initialized & we have our App IDs & secrets from Facebook & Google we can add the authentication service. To do so, we can run the following command:

amplify add auth

# run amplify update auth if you already have a project configured & want to now add Social login

This will walk us through a series of steps:

  • Do you want to use the default authentication and security configuration? Default configuration with Social Provider (Federation)
  • How do you want users to be able to sign in when using your Cognito User Pool? Username
  • What attributes are required for signing up? Email
  • What domain name prefix you want us to create for you? amplifyauthXXXXXXXXX (use default or create custom prefix)
  • Enter your redirect signin URI: http://localhost:3000/ (this can be updated later for production environments)
  • Do you want to add another redirect signin URI: N
  • Enter your redirect signout URI: http://localhost:3000/ (this can be updated later for production environments)
  • Do you want to add another redirect signout URI: N
  • Select the social providers you want to configure for your user pool: Choose Facebook & Google

In the above step we chose Default configuration with Social Provider (Federation). This will allow a combination of Username / Password signin with OAuth. If you only want Username / Password, you could choose Default configuration or Manual Configuration.

Finally, you'll be prompted for your App IDs & Secrets for both Facebook & Google, enter them & press enter to continue.

Now that the authentication service has successfully been configured, we can create the service by running the following command:

amplify push

After running amplify push you should see a success message & the OAuth Endpoint should also be logged out to the console:

The OAuth endpoint should look something like this:

https://amplifyauth8e79c995-8e79c995-local.auth.eu-central-1.amazoncognito.com/

This OAuth endpoint is also available for reference in src/aws-exports.js if you need it at any point under the oauth -> domain key.

We will need to use this endpoint to finish configuring our Facebook & Google Oauth providers.

Configuring Facebook

Next, open the Facebook app we created earlier & click on Basic in the left hand menu.

Scroll to the book & click Add Platform, then choose Website:

For the _Site URL), input the OAuth Endpoint URL with /oauth2/idpresponse appended into Site URL:

Save changes.

Next, type your OAuth Endpoint into App Domains:

Save changes.

Next, from the navigation bar choose Products and then Set up from Facebook Login & choose Web.

For the Valid OAuth Redirect URIs use the OAuth Endpoint + /oauth2/idpresponse. If you're prompted for the site URL, also use this endpoint (i.e. https://amplifyauth8e79c995-8e79c995-local.auth.eu-central-1.amazoncognito.com/oauth2/idpresponse):

Valid OAuth Redirect URIs

Save changes.

Make sure your app is Live by clicking the On switch at the top of the page.

Configuring Google

Now that Facebook has been configured we can now configure Google. To do so, let's go to the Google Developer Console & update our OAuth client.

Click on the client ID to update the settings.

Under Authorized JavaScript origins, add the OAuth Endpoint.

For the Authorized redirect URIs, add the OAuth Endpoint with /oauth2/idpresponse appended to the URL:

Save changes.

Testing it out

Now, we should have our authentication service set up & ready to go. Let's test it out.

The easiest way to do this will be by using the Auth.federatedSignIn() method from the Auth class from AWS Amplify. This function will render the Hosted UI that will give users the option to sign up & sign in with Facebook, Google, or Username / Password without us having to write any of the code.

To try this out, let's first configure the React application to recognize our Amplify project. We do this by adding the following code below our last import in src/index.js:

// src/index.js

import Amplify from 'aws-amplify'
import config from './aws-exports'
Amplify.configure(config)

Next, open App.js & update the code to the following:

// src/App.js

import React from 'react'
import logo from './logo.svg'
import './App.css'

import { Auth } from 'aws-amplify'

function App(props) {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <button onClick={() => Auth.federatedSignIn()}>Sign In</button>
      </header>
    </div>
  )
}

export default App

Now, run the app:

npm start

Now, when the app launched we should be able to sign in using the Sign In button!

Adding more features

Now that we've added an easy way to sign in, what are the next steps? We will walk through the following:

  1. Learn how to sign out users & check the current signed in user
  2. Adding custom buttons for OAuth providers
  3. How to add custom form for Username / Password sign in with an example
  4. Listening to authentication changes (triggers when an authentication event happens)

How to sign out users & check the current signed in use

Now that we have users signed in, how do we know that they are indeed signed in? We can check for the status of a currently signed in user at any time using the Auth class from Amplify.

Let's update our code to the following so we can add a Sign Out button as well as a button to check the status of the currently signed in user:

// src/App.js

import React from 'react'
import logo from './logo.svg'
import './App.css'

import { Auth } from 'aws-amplify'

function checkUser() {
  Auth.currentAuthenticatedUser()
    .then(user => console.log({ user }))
    .catch(err => console.log(err))
}

function signOut() {
  Auth.signOut()
    .then(data => console.log(data))
    .catch(err => console.log(err))
}

function App(props) {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <button onClick={() => Auth.federatedSignIn()}>Sign In</button>
        <button onClick={checkUser}>Check User</button>
        <button onClick={signOut}>Sign Out</button>
      </header>
    </div>
  )
}

export default App

Now when we run our app we're able to log out information about the currently signed in user as well as sign the user out.

Adding custom buttons for OAuth providers

What if we don't want to use the Hosted UI & want to create our own UI from scratch? We can do this pretty easily. The Auth class also has a few methods that we can use to call OAuth providers directly:

Auth.federatedSignIn({provider: 'Google'})
Auth.federatedSignIn({provider: 'Facebook'})

Let's update our app to have a couple of custom OAuth buttons:

<button
  onClick={() => Auth.federatedSignIn({provider: 'Facebook'})}
>Sign In with Facebook</button>

<button
  onClick={() => Auth.federatedSignIn({provider: 'Google'})}
>Sign In with Google</button>

Now, we have created a custom button for signing in with our OAuth providers.

If you're interested in a live demo of this along with the code, check out amplifyauth.dev & view the code in GitHub here.

Adding a custom form for Username / Password sign in

What if we wanted to also create a custom form for signing in users? We can do that using our existing configuration using the Auth class.

The Auth class has over 30 methods available for managing users for all authentication tasks like signing users up, signing users in, handling MFA, & all of the functionality that goes along with user management in general. (View AuthClass API here).

To get started with a custom form using our existing setup, you can use the following methods to sign users up, confirm sign up (MFA), & sign users in:

// sign user up
Auth.signUp({
  username: someUsername, password: somePassword, attributes: { email: someEmail }
})

// confirm sign up
Auth.confirmSignUp(someUsername, authenticationCode)

// sign user in
Auth.signIn(someUsername, somePassword)

These methods are asynchronous & return promises so you can check to see whether they were successful or not.

To view a custom form using this flow, check out this file.

If you're interested in a live demo of this along with the code, check out amplifyauth.dev & view the code in GitHub here.

You can also check out this repo for end to end examples in different frameworks, complete with protected routes using a custom authentication flow.

Listening to authentication events

Now that we have our users signing in & signing out, what if we want to perform some type of action based on this signed in state? We can listen for all authentication changes easily using the Amplify library.

The class we will be using for this is Hub.

Let's create a listener that listens for all auth events & logs them out:

// src/App.js

// import useEffect hook
import React, { useEffect } from 'react';
import logo from './logo.svg';
import './App.css';

// import Hub
import { Auth, Hub } from 'aws-amplify'

function checkUser() {
  Auth.currentAuthenticatedUser()
    .then(user => console.log({ user }))
    .catch(err => console.log(err));
}

function signOut() {
  Auth.signOut()
    .then(data => console.log(data))
    .catch(err => console.log(err));
}

function App(props) {
  // in useEffect, we create the listener
  useEffect(() => {
    Hub.listen('auth', (data) => {
      const { payload } = data
      console.log('A new auth event has happened: ', data)
       if (payload.event === 'signIn') {
         console.log('a user has signed in!')
       }
       if (payload.event === 'signOut') {
         console.log('a user has signed out!')
       }
    })
  }, [])
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <button onClick={() => Auth.federatedSignIn()}>Sign In</button>
        <button onClick={checkUser}>Check User</button>
        <button onClick={signOut}>Sign Out</button>
        <button onClick={() => Auth.federatedSignIn({provider: 'Facebook'})}>Sign In with Facebook</button>
        <button onClick={() => Auth.federatedSignIn({provider: 'Google'})}>Sign In with Google</button>

      </header>
    </div>
  );
}

export default App

Now whenever a user performs any authentication event, the authentication data will be logged out to the console.

Next Steps

Now that you've added authentication to you app you can begin adding secure backends & APIs to your app with GraphQL or AWS Lamba. To learn more, click here.

If you'd like to host your app using the Amplify Console, click here or check out this video to learn how.

My Name is Nader Dabit. I am a Developer Advocate at Amazon Web Services working with projects like AWS AppSync and AWS Amplify. I specialize in cross-platform & cloud-enabled application development.

Top comments (55)

Collapse
 
chrismowbray92 profile image
Chris Mowbray

Hey, great article! any news on the react native version?

Collapse
 
dabit3 profile image
Nader Dabit

React Native article is way late, but available at dev.to/dabit3/the-complete-react-n...

Collapse
 
pperron profile image
pperron

Any update on the React Native version ?

Been trying to make it work but stuck at when the hosted UI (using withOAuth + Amplify) get me back to my app (hard link), Hub.Listen doesn't seems to fire with the signIn event, so my app doesn't know that the user is logged or not. If I check on Amazon Incognito, the Facebook user as been added to the pool, so that part seems ok.

Thanks !

Collapse
 
dabit3 profile image
Nader Dabit
Collapse
 
josephgoksu profile image
Joseph Goksu

If you get an error for Auth.federatedSignIn({ provider: 'Google' })

try this ⬇️
import { CognitoHostedUIIdentityProvider } from "@aws-amplify/auth/lib/types";

Auth.federatedSignIn({
provider: CognitoHostedUIIdentityProvider.Google
});

Collapse
 
padunk profile image
Abraham A. Agung

Hi Nader,
This is awesome, just like what we need in time.
But I encounter some problems, why is the CLI not working in Git Bash?
And when I use windows command prompt, and type amplify add auth it will always return to my prompt after the first step.
I use windows 10 and latest CLI.

Thanks.

Collapse
 
dabit3 profile image
Nader Dabit

Hi Abraham, I'm sorry you're having issues with the CLI in that environment. I would suggest opening a GitHub issue at github.com/aws-amplify/amplify-cli... & giving us your system & env info to see if we can figure out what issue you're having.

Collapse
 
padunk profile image
Abraham A. Agung

Hello again,
I check Github and found the answer.
Turns out I need to update NodeJS to version 11 or 12.
I don't know why this is not working with Node10, but everything seems good now.

Thanks.

Collapse
 
lanceharper profile image
Lance Harper

Does Amplify support Cognito + a custom OIDC provider (e.g. yahoo/twitch/salesforce?) such that the access token from the OIDC provider (not Cognito) will be returned for interacting with said OIDC's API? I only receive tokens for use with Cognito which is only half of what I'm trying to obtain.

Collapse
 
arelaxtest profile image
arelaxtest

No, you also have to create your own Lambda function to do that: such that github.com/arelaxend/fays-openid

Collapse
 
dancouper profile image
Dan Couper

Cheers for this: I hadn't thought to look at Hub to support Auth, it very neatly solves one of the problems I'm working on at the minute, this came at just the right time!

I've found Amplify very useful so far: only major pain point has been that the CLI has proven effectively useless for me, as the way we have our resources set up means I never want automatic provisioning -- being able to say to the CLI "I want to use this AppSync API + this Cognito Pool + etc etc" would be incredibly useful (particularly w/r/t generating the configs + the AppSync GQL codegen functionality).

Collapse
 
dabit3 profile image
Nader Dabit

Hey Dan, thanks for the feedback, this is great! I'm passing this on directly to the team. Hopefully we can get something figured out & on our roadmap that will address this.

Collapse
 
syang profile image
syang

Hi Nader, thanks a lot for providing the simple/short article to bring beginner up to speed.

Can you give a detailed under the hood mechanism explanation to how 'the redirect signin URI: localhost:3000/' is used during the sign-in process? In a production code, I assume this URI should be changed, right? But to be changed to what?

I guess I would like to understand how the following three URL and web app / web browser interact during the Oauth signed process to fully understand it.

Can you give a detailed walk through of how each URI during a life of Social Sign In process.

Thanks

Collapse
 
arelaxtest profile image
arelaxtest

For those who have issue with the redirect URLs or want to add more than one redirect URLs for dev, prod, localhost, here is a closed topic: github.com/aws-amplify/amplify-js/...

You can find a demo here that also work in localhost / online: master.d3h5j4begww46c.amplifyapp.com/
And a github fork (from dabit3): github.com/arelaxtest/amplify-auth...

Collapse
 
adrienfabre profile image
Adrien Fabre

Hi Nader,

Thank you for this article, it helped me a lot.

I am trying to adapt it, sign up with Google (OAuth) or with Cognito (email + password).

My issue is that if I sign up with Google and then Cognito, I create a duplicate in my User Pool. Also, if I sign up with Cognito and then Google, it also create a duplicate in my User Pool.

However, if I try to sign up twice with Cognito, it would notice the email already exist.

I am trying to find a good way to prevent those duplicate, or by raising an error or by merging the duplicate but I have not found out, would you have suggestions?

Thank you

Collapse
 
rudyhadoux profile image
rudyhadoux

If you use Auth.federatedSignIn({provider: 'Google'}), you don't have to use anything else.
Why twice ?

Collapse
 
indrajitb profile image
indrajitb

Hi rudyhadoux,
If you sign in with Facebook and Google with same email, it creates two identities.
Thanks.

Thread Thread
 
rudyhadoux profile image
rudyhadoux

Two Cognito identities with the same email ?
Is it possible ?

Collapse
 
indrajitb profile image
indrajitb

Hi Adrien,

Did you find a solution to this problem?

Thanks.

Collapse
 
alsmith808 profile image
Alan Smith

Hi Nader,

Thanks for all your Amplify material, really helping me a lot.  Quick question, this could possibly be a problem on my end as I was having an issue with css not loading correctly in another project also.  Anyway I have added Cognito to my local project which I'm mocking, but my Cognito login screen is appearing with no css, have you or anyone come across this problem, the signup has no link so I cant signup, thank you!

Alan

Collapse
 
pandaa880 profile image
Prashant Chaudhari • Edited

Great article !
Is there any way so that i don't have to use Amazon Cognito ? We have separate user authentication system and I need to upload files from frontend to s3 directly.

If I don't add auth in the configure part then i am getting No Credentials. So Can i use normal aws IAM user to access S3 using Amplify ?

Collapse
 
kalinchernev profile image
Kalin Chernev

Hi Nader,

Thank you for the detailed tutorial and your hard on making AWS Amplify more accessible for developers with so many examples on the web!

I want to ask if it's possible and how to take the results from this tutorial and add a GraphQL api which can be accessed by any authenticated user?

I am able to provide a repository with current state of a working project: github.com/kalinchernev/knowledge-...

The problem I'm having here is that when I change the security scheme of the API from API key to Cognito user pool, I'm getting "No current user" on users authenticated with the Google login.

I found an older issue here github.com/aws-amplify/amplify-js/... which feels like an important thread, but with unclear solution or best practice at the moment.

Is it that manual updates are still needed in the console and configuration files? Is it that we need to rely on multiple authentication schemes on AppSync GraphQL layer? How to make use of that through the Amplify CLI if it's the case? Maybe I need to use GraphQL transform, but as far as I read @auth is not match for both api key and user pools neither...

Any guidance is welcome!

Collapse
 
ankurzibo profile image
ankur-zibo • Edited

Hi Nader,
It was really helpful. Thanks

I tried to integrate my React code to Cognito User Pool through Amplify SDK.
I have a question around the security of the password we are passing to Cognito during SignUp and Reset Password flow. I see that during the SignIn flow, SRP is used while for signUp and reset password it is passed as plain text. Is there a way to encode and send it to Cognito?

Collapse
 
valenciahq profile image
Alejandro Valencia

hi! is there a way to avoid "uncaught (in promise) not authenticated" error being showed in console on:

await Auth.currentAuthenticatedUser()
.then(user => {
...some stuff
})
.catch(e => {
history.push("/auth");
});

The app will redirect to /auth each time is not authenticated but we dont want to see not authenticated error in console in production

Collapse
 
rtdemetri profile image
Demetrios Christopher

In your .then(), provide an onRejected clause:

Auth.currentAuthenticatedUser().then((user) => { ... }, (error) => { ... }).catch((error) => { ... })

The catch is there to catch something wrong that you did in your onResolved or onRejected clauses, not to catch the fact that the promise was rejected and never resolved. I hope this helps!

Alternatively, you can turn the promises into observables and deal with them as such.

Collapse
 
mrichman profile image
Mark Richman

Did you ever figure this out? I get that same error.

Collapse
 
lanceharper profile image
Lance Harper

There seems to be a serious bug w/ amplify. When I go through the steps in your tutorial, I get to the step where it asks "What domain name prefix you want us to create for you?" and it provides a default. Hitting enter to accept the default tells me there is an error where the value must be a valid domain name format.

Collapse
 
dabit3 profile image
Nader Dabit

Hey, thanks what version of the CLI are you using?

Collapse
 
lanceharper profile image
Lance Harper • Edited

The latest. It turns out that the prefix that is generated by the CLI is not permitted. My repo had dashes and the name of the host prefix had dashes but it wouldn't validate the underscores that amplify swapped my dashes out with for some reason. Pretty confusing but I figured it out. Thanks for the quick reply.

Collapse
 
rudyhadoux profile image
rudyhadoux

Hi, it seems impossible to create both API_KEY and Cognito User Pool in the same amplify project. So, how to achieve that ?

Collapse
 
dabit3 profile image
Nader Dabit • Edited

You can set multiple auth types in the CLI when initializing or running amplify update auth, just make sure you are on the latest version of the CLI. You can then make requests using either the base authentication type, or passing in a custom type aws-amplify.github.io/docs/js/api#...

Collapse
 
rudyhadoux profile image
rudyhadoux

A lot of changes indeed. Thanks.

With authMode: 'AMAZON_COGNITO_USER_POOLS', there is a JWT verification for each request ?

Some comments may only be visible to logged-in visitors. Sign in to view all comments.