- Follow demo.sh for Amplify & Auth setup
- Create Create/Define/VerifyAuthChallenge JS files at line 57 of demo.sh (only for custom auth flow in Amplify)
- If you are following custom auth flow, you can have the user email auto-verified on sign-up
- Create lambda function with the PreSignUp.js code
- Go to Cognito -> your User Pool -> General settings -> Triggers -> Pre sign-up: choose the lambda function from the step above
- Use authFuncs.js as a starting point for application integration with the Amplify Auth setup
Last active
July 10, 2020 16:53
-
-
Save syardumi/4b1f371ad53a4bfaac1db9065c414d61 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { CognitoUser } from 'amazon-cognito-identity-js' | |
import { Amplify, Auth } from 'aws-amplify' | |
import 'react-native-get-random-values' | |
import { v4 as uuidv4 } from 'uuid' | |
import awsconfig from './aws-exports' //auto-generated by amplify init/add auth | |
Amplify.configure(awsconfig) | |
let cognitoUser: CognitoUser | undefined | |
const authFuncs = () => ({ | |
/** | |
* createCode -> signIn | |
*/ | |
createCode: async (email: string) => { | |
try { | |
//try signing in with just email | |
cognitoUser = await Auth.signIn(email.toLowerCase()); | |
} catch (err) { | |
console.log(err); | |
//if this user doesn't exist in the cognito user pool yet, create it (a pre-signup lambda can auto-verify the email address) | |
await Auth.signUp({ | |
username: email.toLowerCase(), | |
password: uuidv4(), | |
attributes: { | |
email: email.toLowerCase(), | |
}, | |
}); | |
//once the user is created, then sign them in | |
cognitoUser = await Auth.signIn(email.toLowerCase()); | |
} | |
}, | |
signIn: async (code: any, callback: () => void) => { | |
if (cognitoUser) { | |
//send in the code answer | |
cognitoUser = await Auth.sendCustomChallengeAnswer(cognitoUser, code); | |
try { | |
// This will throw an error if the user is not yet authenticated | |
const data = await Auth.currentSession(); | |
//TODO: if it passes, log us in and save the access token | |
callback(); | |
} catch { | |
console.log("Apparently the user did not enter the right code"); | |
//TODO: show an error message on screen | |
} | |
} | |
}, | |
/** | |
* facebookPopUp -> facebookSignIn | |
*/ | |
facebookPopUp: async () => { | |
await Auth.federatedSignIn({ provider: "Facebook" }); | |
}, | |
facebookSignIn: async () => { | |
//TODO: save access type as 'FACEBOOK' and any access token we get from OAuth | |
}, | |
/** | |
* signOut | |
*/ | |
signOut: async (willDoLogic: boolean) => { | |
if (willDoLogic) { | |
//purge user | |
cognitoUser = undefined | |
//sign out of amplify | |
await Auth.signOut() | |
} | |
if (state.accessType !== 'FACEBOOK') { | |
//TODO: if not FB OAuth, update state | |
} | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const cryptoRandomString = require('crypto-random-string') | |
const ses = new (require('aws-sdk').SES)() | |
const sendEmail = async (emailAddress, secretLoginCode) => { | |
const params = { | |
Destination: { ToAddresses: [emailAddress] }, | |
Message: { | |
Body: { | |
Html: { | |
Charset: 'UTF-8', | |
Data: `<html><body><p>This is your Adieu login code:</p><h3>${secretLoginCode}</h3><p>...or <a href="${process.env.LOGINREDIRECTURL + secretLoginCode}">follow this link</a></p></body></html>` | |
}, | |
Text: { | |
Charset: 'UTF-8', | |
Data: `Your Adieu login code: ${secretLoginCode} | |
...or follow this link: ${process.env.LOGINREDIRECTURL + secretLoginCode} | |
` | |
} | |
}, | |
Subject: { | |
Charset: 'UTF-8', | |
Data: 'Your Adieu login code' | |
} | |
}, | |
Source: process.env.SESFROMADDRESS | |
} | |
await ses.sendEmail(params).promise() | |
} | |
exports.handler = async (event, context) => { | |
let secretLoginCode | |
if (!event.request.session || !event.request.session.length) { | |
// This is a new auth session | |
// Generate a new secret login code and mail it to the user | |
secretLoginCode = cryptoRandomString({length: 6, type: 'numeric'}) | |
await sendEmail(event.request.userAttributes.email, secretLoginCode) | |
} else { | |
// There's an existing session. Don't generate new digits but | |
// re-use the code from the current session. This allows the user to | |
// make a mistake when keying in the code and to then retry, rather | |
// the needing to e-mail the user an all new code again. | |
const previousChallenge = event.request.session.slice(-1)[0] | |
secretLoginCode = previousChallenge.challengeMetadata.match( | |
/CODE-(\d*)/ | |
)[1] | |
} | |
// This is sent back to the client app | |
event.response.publicChallengeParameters = { | |
email: event.request.userAttributes.email | |
} | |
// Add the secret login code to the private challenge parameters | |
// so it can be verified by the "Verify Auth Challenge Response" trigger | |
event.response.privateChallengeParameters = { secretLoginCode } | |
// Add the secret login code to the session so it is available | |
// in a next invocation of the "Create Auth Challenge" trigger | |
event.response.challengeMetadata = `CODE-${secretLoginCode}` | |
context.done(null, event) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
exports.handler = (event, context) => { | |
if ( | |
event.request.session && | |
event.request.session.find( | |
(attempt) => attempt.challengeName !== 'CUSTOM_CHALLENGE' | |
) | |
) { | |
// We only accept custom challenges fail auth | |
event.response.issueTokens = false | |
event.response.failAuthentication = true | |
} else if ( | |
event.request.session && | |
event.request.session.length >= 3 && | |
event.request.session.slice(-1)[0].challengeResult === false | |
) { | |
// The user provided a wrong answer 3 times fail auth | |
event.response.issueTokens = false | |
event.response.failAuthentication = true | |
} else if ( | |
event.request.session && | |
event.request.session.length && | |
event.request.session.slice(-1)[0].challengeName === 'CUSTOM_CHALLENGE' && // Doubly stitched, holds better | |
event.request.session.slice(-1)[0].challengeResult === true | |
) { | |
// The user provided the right answer succeed auth | |
event.response.issueTokens = true | |
event.response.failAuthentication = false | |
} else { | |
// The user did not provide a correct answer yet present challenge | |
event.response.issueTokens = false | |
event.response.failAuthentication = false | |
event.response.challengeName = 'CUSTOM_CHALLENGE' | |
} | |
context.done(null, event) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# NOT MEANT TO BE RUN | |
$ amplify init | |
# enter your project details, make sure you have an AWS credential named 'default' in ~/.aws/credentials | |
$ amplify add auth | |
# Do you want to use the default authentication and security configuration? | |
# Default configuration | |
# ❯ Default configuration with Social Provider (Federation) | |
# Manual configuration | |
# I want to learn more. | |
# Warning: you will not be able to edit these selections. | |
# How do you want users to be able to sign in? | |
# Username | |
# Phone Number | |
# Email or Phone Number | |
# I want to learn more. | |
# Do you want to configure advanced settings? | |
# No, I am done. | |
# ❯ Yes, I want to make some additional changes. | |
# Warning: you will not be able to edit these selections. | |
# What attributes are required for signing up? (Press <space> to select, <a> to toggle all, <i> to invert selection) | |
# ❯◯ Address (This attribute is not supported by Facebook, Google, Login With Amazon.) | |
# ◯ Birthdate (This attribute is not supported by Login With Amazon.) | |
# ◯ Family Name (This attribute is not supported by Login With Amazon.) | |
# ◯ Middle Name (This attribute is not supported by Google, Login With Amazon.) | |
# ◯ Gender (This attribute is not supported by Login With Amazon.) | |
# ◯ Locale (This attribute is not supported by Facebook, Google.) | |
# (Move up and down to reveal more choices) | |
# Do you want to enable any of the following capabilities? | |
# ◯ Add Google reCaptcha Challenge | |
# ◯ Email Verification Link with Redirect | |
# ◯ Add User to Group | |
# ◯ Email Domain Filtering (blacklist) | |
# ◯ Email Domain Filtering (whitelist) | |
# ❯◉ Custom Auth Challenge Flow (basic scaffolding - not for production) [THIS OPTION ALLOWS FOR LOGGING IN USING EMAIL & VERIFY CODE] | |
# ◯ Override ID Token Claims | |
# Set a domain prefix | |
# Set any OAuth redirect signin URIs | |
# Set any OAuth redirect signout URIs | |
# Select the social providers you want to configure for your user pool: (Press <space> to select, <a> to toggle all, <i> to invert selection) | |
# ◯ Login With Amazon | |
# Follow Social App setup instructions here: https://docs.amplify.aws/lib/auth/social/q/platform/js#setup-your-auth-provider | |
# Enter App IDs and secrets depending on the social options chosen | |
# [ONLY FOR CUSTOM AUTH FLOW] Prompted to set up Create, Define, and Verify Auth challenge lambda functions | |
$ amplify push | |
# sends the whole local config to AWS for diffing & build out |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { CognitoUserPoolTriggerHandler } from 'aws-lambda' | |
export const handler: CognitoUserPoolTriggerHandler = async (event) => { | |
event.response.autoConfirmUser = true | |
if (event.request.userAttributes.hasOwnProperty("email")) { | |
event.response.autoVerifyEmail = true | |
} | |
return event | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
exports.handler = (event, context) => { | |
const expectedAnswer = | |
event.request.privateChallengeParameters.secretLoginCode | |
if (event.request.challengeAnswer === expectedAnswer) { | |
event.response.answerCorrect = true | |
} else { | |
event.response.answerCorrect = false | |
} | |
context.done(null, event) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment