diff --git a/src/fragments/lib/auth/common/device_features/common.mdx b/src/fragments/lib/auth/common/device_features/common.mdx index a8d69217b11..c73409d3455 100644 --- a/src/fragments/lib/auth/common/device_features/common.mdx +++ b/src/fragments/lib/auth/common/device_features/common.mdx @@ -44,7 +44,7 @@ import android1 from "/src/fragments/lib/auth/android/device_features/10_remembe import js2 from "/src/fragments/lib/auth/js/device_features/10_rememberDevice.mdx"; - + import flutter3 from "/src/fragments/lib/auth/flutter/device_features/10_rememberDevice.mdx"; @@ -63,7 +63,7 @@ import android5 from "/src/fragments/lib/auth/android/device_features/20_forgetD import js6 from "/src/fragments/lib/auth/js/device_features/20_forgetDevice.mdx"; - + import flutter7 from "/src/fragments/lib/auth/flutter/device_features/20_forgetDevice.mdx"; @@ -82,7 +82,7 @@ import android9 from "/src/fragments/lib/auth/android/device_features/30_fetchDe import js10 from "/src/fragments/lib/auth/js/device_features/30_fetchDevice.mdx"; - + import flutter11 from "/src/fragments/lib/auth/flutter/device_features/30_fetchDevice.mdx"; @@ -96,4 +96,4 @@ import flutter11 from "/src/fragments/lib/auth/flutter/device_features/30_fetchD * **Not Remembered** * A not-remembered device is a tracked device where Cognito has been configured to require users to "Opt-in" to remember a device, but the user has not opt-ed in to having the device remembered. This use case is used for users signing into their application from a device that they don't own. * **Forgotten** - * In the event that you no longer want to remember or track a device, you can use the `Amplify.Auth.forgetDevice()` API to remove this device from being both remembered and tracked. + * In the event that you no longer want to remember or track a device, you can use the `Auth.forgetDevice()` API to remove this device from being both remembered and tracked. diff --git a/src/fragments/lib/auth/js/emailpassword.mdx b/src/fragments/lib/auth/js/emailpassword.mdx index 4e5537eba86..24f47407720 100644 --- a/src/fragments/lib/auth/js/emailpassword.mdx +++ b/src/fragments/lib/auth/js/emailpassword.mdx @@ -36,26 +36,20 @@ The `Auth.signUp` promise returns a data object of type [`ISignUpResult`](https: } ``` -### Auto sign in after sign up - -If you enabled `autoSignIn`, the `sign up` function will dispatch `autoSignIn` hub event after successful confirmation. -If authentication was successful, the event will contain `CognitoUser` in data object. If auto sign in failed, it will dispatch `autoSignIn_failure` event. +### Re-send sign up confirmation code +If user didn't get a confirmation code, you can use `resendSignUp` function to send a new one. ```js -import { Hub } from 'aws-amplify'; +import { Auth } from 'aws-amplify'; -function listenToAutoSignInEvent() { - Hub.listen('auth', ({ payload }) => { - const { event } = payload; - if (event === 'autoSignIn') { - const user = payload.data; - // assign user - } else if (event === 'autoSignIn_failure') { - // redirect to sign in page - } - }) +async function resendConfirmationCode() { + try { + await Auth.resendSignUp(username); + console.log('code resent successfully'); + } catch (err) { + console.log('error resending code: ', err); + } } - ``` ### Confirm sign up @@ -74,20 +68,26 @@ async function confirmSignUp() { } ``` -### Re-send sign up confirmation code +### Auto sign in after sign up + +If you enabled `autoSignIn`, the `sign up` function will dispatch `autoSignIn` hub event after successful confirmation. +If authentication was successful, the event will contain `CognitoUser` in data object. If auto sign in failed, it will dispatch `autoSignIn_failure` event. -If user didn't get a confirmation code, you can use `resendSignUp` function to send a new one. ```js -import { Auth } from 'aws-amplify'; +import { Hub } from 'aws-amplify'; -async function resendConfirmationCode() { - try { - await Auth.resendSignUp(username); - console.log('code resent successfully'); - } catch (err) { - console.log('error resending code: ', err); - } +function listenToAutoSignInEvent() { + Hub.listen('auth', ({ payload }) => { + const { event } = payload; + if (event === 'autoSignIn') { + const user = payload.data; + // assign user + } else if (event === 'autoSignIn_failure') { + // redirect to sign in page + } + }) } + ``` ### Custom Attributes @@ -105,7 +105,7 @@ Auth.signUp({ }) ``` -> Amazon Cognito does not dynamically create custom attributes on sign up. In order to use a custom attribute, the attribute must be first created in the user pool. To open the User Pool to create custom attributes using the Amplify ClI, run `amplify console auth`. If you are not using the Amplify CLI, you can view the user pool by visiting the AWS console and opening the Amazon Cognito dashboard. +> Amazon Cognito does not dynamically create custom attributes on sign up. In order to use a custom attribute, the attribute must be first [created in the user pool](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html). To open the User Pool to create custom attributes using the Amplify ClI, run `amplify console auth`. If you are not using the Amplify CLI, you can view the user pool by visiting the AWS console and opening the Amazon Cognito dashboard. ## Sign-in diff --git a/src/fragments/lib/auth/js/mfa.mdx b/src/fragments/lib/auth/js/mfa.mdx index 7b648a05f99..3dc5a60795f 100644 --- a/src/fragments/lib/auth/js/mfa.mdx +++ b/src/fragments/lib/auth/js/mfa.mdx @@ -82,38 +82,6 @@ Auth.getPreferredMFA(user, { }); ``` -## Allow users to select MFA type - -When working with multiple MFA Types, you can let the app user select the desired authentication method. `SelectMFAType` UI Component, which is provided with `aws-amplify-react` package, renders a list of available MFA types. - -```javascript -import { Amplify } from 'aws-amplify'; -import awsconfig from './aws-exports'; -import { SelectMFAType } from 'aws-amplify-react'; - -Amplify.configure(awsconfig); - -// Please have at least TWO types -// Please make sure you set it properly according to your Cognito User pool -const MFATypes = { - SMS: true, // if SMS enabled in your user pool - TOTP: true, // if TOTP enabled in your user pool - Optional: true, // if MFA is set to optional in your user pool -} - -class App extends Component { - // ... - render() { - return ( - // ... - - ) - } -} - -export default withAuthenticator(App, true); -``` - ## Advanced use cases ### Sign-in with custom auth challenges diff --git a/src/fragments/lib/auth/js/react-native-mfa.mdx b/src/fragments/lib/auth/js/react-native-mfa.mdx new file mode 100644 index 00000000000..74f6df6f575 --- /dev/null +++ b/src/fragments/lib/auth/js/react-native-mfa.mdx @@ -0,0 +1,184 @@ + +Note: If you create or update an SMS MFA configuration for your Cognito user pool, the Cognito service will send a test SMS message to an internal number in order to verify your configuration. You will be charged for these test messages by Amazon SNS. + +For information about Amazon SNS pricing, see [Worldwide SMS Pricing](https://aws.amazon.com/sns/sms-pricing/). + + +MFA (Multi-factor authentication increases security for your app by adding an authentication method and not relying solely on the username (or alias) and password. AWS Amplify uses Amazon Cognito to provide MFA. Please see [Amazon Cognito Developer Guide](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-mfa.html) for more information about setting up MFA in Amazon Cognito. + +Once you enable MFA on Amazon Cognito, you can configure your app to work with MFA. + +## Setup TOTP + +With TOTP (Time-based One-time Password), your app user is challenged to complete authentication using a time-based one-time (TOTP) password after their username and password have been verified. + +You can setup TOTP for a user in your app: + +```javascript +import { Auth } from 'aws-amplify'; + +// To setup TOTP, first you need to get a `authorization code` from Amazon Cognito +// `user` is the current Authenticated user +Auth.setupTOTP(user).then((code) => { + // display setup code to user which can be used to manually add an account to Authenticator apps +}); + +// ... + +// Then you will have your TOTP account in your TOTP-generating app (like Google Authenticator) +// Use the generated one-time password to verify the setup +Auth.verifyTotpToken(user, challengeAnswer) + .then(() => { + // don't forget to set TOTP as the preferred MFA method + Auth.setPreferredMFA(user, 'TOTP'); + // ... + }) + .catch((e) => { + // Token is not verified + }); +``` + +## Setup MFA type + +Multiple MFA types supported by Amazon Cognito. You can set the preferred method in your code: + +```javascript +import { Auth } from 'aws-amplify'; + +// You can select preferred mfa type, for example: +// Select TOTP as preferred +Auth.setPreferredMFA(user, 'TOTP') + .then((data) => { + console.log(data); + // ... + }) + .catch((e) => {}); + +// Select SMS as preferred +Auth.setPreferredMFA(user, 'SMS'); + +// Select no-mfa +Auth.setPreferredMFA(user, 'NOMFA'); +``` + +## Retrieve current preferred MFA type + +You can get current preferred MFA type in your code: + +```javascript +import { Auth } from 'aws-amplify'; + +// Will retrieve the current mfa type from cache +Auth.getPreferredMFA(user, { + // Optional, by default is false. + // If set to true, it will get the MFA type from server side instead of from local cache. + bypassCache: false +}).then((data) => { + console.log('Current preferred MFA type is: ' + data); +}); +``` + +## Advanced use cases + +### Sign-in with custom auth challenges + +When signing in with user name and password, you will either sign in directly or be asked to pass some challenges before getting authenticated. + +The `user` object returned from `Auth.signIn` will contain `challengeName` and `challengeParam` if the user needs to pass those challenges. You can call corresponding functions based on those two parameters. + +ChallengeName: + +- `SMS_MFA`: The user needs to input the code received from SMS message. You can submit the code by `Auth.confirmSignIn`. +- `SOFTWARE_TOKEN_MFA`: The user needs to input the OTP(one time password). You can submit the code by `Auth.confirmSignIn`. +- `NEW_PASSWORD_REQUIRED`: This happens when the user account is created through the Cognito console. The user needs to input the new password and required attributes. You can submit those data by `Auth.completeNewPassword`. +- `MFA_SETUP`: This happens when the MFA method is TOTP(the one time password) which requires the user to go through some steps to generate those passwords. You can start the setup process by `Auth.setupTOTP`. + +The following code is only for demonstration purpose: + +```javascript +import { Auth } from 'aws-amplify'; + +async function signIn() { + try { + const user = await Auth.signIn(username, password); + if ( + user.challengeName === 'SMS_MFA' || + user.challengeName === 'SOFTWARE_TOKEN_MFA' + ) { + // You need to get the code from the UI inputs + // and then trigger the following function with a button click + const code = getCodeFromUserInput(); + // If MFA is enabled, sign-in should be confirmed with the confirmation code + const loggedUser = await Auth.confirmSignIn( + user, // Return object from Auth.signIn() + code, // Confirmation code + mfaType // MFA Type e.g. SMS_MFA, SOFTWARE_TOKEN_MFA + ); + } else if (user.challengeName === 'NEW_PASSWORD_REQUIRED') { + const { requiredAttributes } = user.challengeParam; // the array of required attributes, e.g ['email', 'phone_number'] + // You need to get the new password and required attributes from the UI inputs + // and then trigger the following function with a button click + // For example, the email and phone_number are required attributes + const { username, email, phone_number } = getInfoFromUserInput(); + const loggedUser = await Auth.completeNewPassword( + user, // the Cognito User Object + newPassword, // the new password + // OPTIONAL, the required attributes + { + email, + phone_number + } + ); + } else if (user.challengeName === 'MFA_SETUP') { + // This happens when the MFA method is TOTP + // The user needs to setup the TOTP before using it + // More info please check the Enabling MFA part + Auth.setupTOTP(user); + } else { + // The user directly signs in + console.log(user); + } + } catch (err) { + if (err.code === 'UserNotConfirmedException') { + // The error happens if the user didn't finish the confirmation step when signing up + // In this case you need to resend the code and confirm the user + // About how to resend the code and confirm the user, please check the signUp part + } else if (err.code === 'PasswordResetRequiredException') { + // The error happens when the password is reset in the Cognito console + // In this case you need to call forgotPassword to reset the password + // Please check the Forgot Password part. + } else if (err.code === 'NotAuthorizedException') { + // The error happens when the incorrect password is provided + } else if (err.code === 'UserNotFoundException') { + // The error happens when the supplied username/email does not exist in the Cognito user pool + } else { + console.log(err); + } + } +} +``` + +### Sign-in with custom validation data for Lambda Trigger + +You can also pass an object which has the username, password and validationData which is sent to a PreAuthentication Lambda trigger + +```js +try { + const user = await Auth.signIn({ + username, // Required, the username + password, // Optional, the password + validationData // Optional, an arbitrary key-value pair map which can contain any key and will be passed to your PreAuthentication Lambda trigger as-is. It can be used to implement additional validations around authentication + }); + console.log('user is signed in!', user); +} catch (error) { + console.log('error signing in:', error); +} +``` + +### Forcing Email Uniqueness in Cognito User Pools + +When your Cognito User Pool sign-in options are set to "_Username_", and "_Also allow sign in with verified email address_", the _signUp()_ method creates a new user account every time it's called, without validating email uniqueness. In this case you will end up having multiple user pool identities and all previously created accounts will have their _email_verified_ attribute changed to _false_. + +To enforce Cognito User Pool signups with a unique email, you need to change your User Pool's _Attributes_ setting in [Amazon Cognito console](https://console.aws.amazon.com/cognito) as the following: + +![cup](/images/cognito_user_pool_settings.png) diff --git a/src/fragments/lib/auth/js/react-native-social.mdx b/src/fragments/lib/auth/js/react-native-social.mdx new file mode 100644 index 00000000000..43d47883d4d --- /dev/null +++ b/src/fragments/lib/auth/js/react-native-social.mdx @@ -0,0 +1,406 @@ +## OAuth and Federation Overview + +[OAuth 2.0](https://en.wikipedia.org/wiki/OAuth) is the common Authorization framework used by web and mobile applications for getting access to user information ("scopes") in a limited manner. Common analogies you will hear in OAuth is that of boarding a plane or staying in a hotel - showing your identification is the Authentication piece (signing into an app) and using the boarding pass/hotel key is what you are Authorized to access. + +OAuth support in Amplify uses Cognito User Pools and supports federation with social providers, which will automatically create a corresponding user in the User Pool after a login. [OIDC](https://en.wikipedia.org/wiki/OpenID_Connect) tokens are available in the app after the application has completed this process. + +import all0 from "/src/fragments/lib/auth/common/social_signin_web_ui/setup_auth_provider.mdx"; + + + +## Configure Auth Category +Once you have the social provider configured, run the following in your project’s root folder: + +```bash +amplify add auth ## "amplify update auth" if already configured +``` + +Choose the following options (the last steps are specific to Facebook here but are similar for other providers): + +```console +? 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? + `Username` +? Do you want to configure advanced settings? + `No, I am done.` +? What domain name prefix you want us to create for you? + `(default)` +? Enter your redirect signin URI: + `http://localhost:3000/` +? Do you want to add another redirect signin URI + `No` +? Enter your redirect signout URI: + `http://localhost:3000/` +? Do you want to add another redirect signout URI + `No` +? Select the social providers you want to configure for your user pool: + `` +``` + +** Redirect URIs** + +For React Native applications, you need to define a custom URL scheme for your application before testing locally or publishing to the app store. This is different for Expo or vanilla React Native. For Expo, follow the steps in [Expo Linking Guide](https://docs.expo.io/guides/linking/). For vanilla React Native, follow the steps in [React Native Linking Guide](https://reactnative.dev/docs/linking). After completing those steps, assuming you are using `myapp` as the name of your URL Scheme (or whatever friendly name you have chosen), you will use these URLs as *Sign in Redirect URI(s)* and/or *Sign out redirect URI(s)* inputs. Your URIs could look like any of these: + +- `myapp://` +- `exp://127.0.0.1:19000/--/` (Local development if your app is running [in the Expo client](https://docs.expo.io/versions/latest/workflow/linking/#linking-to-your-app)). + +*React Native - iOS - Info.plist* + +```xml + + + + + + + + + CFBundleURLTypes + + + CFBundleURLSchemes + + myapp + + + + + + +``` + +*React Native - Android - AndroidManifest.xml* + +- Set the `launchMode` of MainActivity to `singleTask` +- Add new intent filter (below) with `scheme="myapp"` + +```xml + + + + + + + + + + + + +``` + +import all1 from "/src/fragments/lib/auth/common/social_signin_web_ui/configure_auth_category.mdx"; + + + +### Known Limitations +When using the federated OAuth flow with Cognito User Pools, the [device tracking and remembering](https://aws.amazon.com/blogs/mobile/tracking-and-remembering-devices-using-amazon-cognito-your-user-pools/) features are currently not available within the library. If you are looking for this feature within the library, please open a feature request [here](https://github.com/aws-amplify/amplify-js/issues/new?assignees=&labels=feature-request&template=feature_request.md&title=) and provide upvotes in order for us to take this into consideration for the future of the library. + +## Setup frontend + +After configuring the OAuth endpoints (Cognito Hosted UI), you can integrate your App by invoking `Auth.federatedSignIn()` function. Passing `LoginWithAmazon`, `Facebook`, `Google`, or `SignInWithApple` on the `provider` argument (e.g `Auth.federatedSignIn({ provider: 'LoginWithAmazon' })`), it will bypass the Hosted UI and federate immediately with the social provider as shown in the below React example. If you are looking to add a custom state, you are able to do so by passing a string (e.g. `Auth.federatedSignIn({ customState: 'xyz' })`) value and listening for the custom state via Hub. + +```javascript +import React, { useEffect, useState } from 'react'; +import { Amplify, Auth, Hub } from 'aws-amplify'; +import { CognitoHostedUIIdentityProvider } from '@aws-amplify/auth'; +import awsconfig from './aws-exports'; + +Amplify.configure(awsconfig); + +function App() { + const [user, setUser] = useState(null); + const [customState, setCustomState] = useState(null); + + useEffect(() => { + const unsubscribe = Hub.listen("auth", ({ payload: { event, data } }) => { + switch (event) { + case "signIn": + setUser(data); + break; + case "signOut": + setUser(null); + break; + case "customOAuthState": + setCustomState(data); + } + }); + + Auth.currentAuthenticatedUser() + .then(currentUser => setUser(currentUser)) + .catch(() => console.log("Not signed in")); + + return unsubscribe; + }, []); + + return ( +
+ + + + +
+ ); +} +``` + + + You can also use the Authenticator UI component to add social sign in flow to your application. Visit Social Provider section to learn more. + + +### Deploying to Amplify Console + +To deploy your app to Amplify Console with continuous deployment of the frontend and backend, please follow [these instructions](https://docs.aws.amazon.com/amplify/latest/userguide/environment-variables.html#creating-a-new-backend-environment-with-authentication-parameters). + +### Full Samples + + + + + +```js +import React, { useEffect, useState } from 'react'; +import { Amplify, Auth, Hub } from 'aws-amplify'; +import awsconfig from './aws-exports'; + +Amplify.configure(awsconfig); + +function App() { + const [user, setUser] = useState(null); + + useEffect(() => { + Hub.listen('auth', ({ payload: { event, data } }) => { + switch (event) { + case 'signIn': + case 'cognitoHostedUI': + getUser().then(userData => setUser(userData)); + break; + case 'signOut': + setUser(null); + break; + case 'signIn_failure': + case 'cognitoHostedUI_failure': + console.log('Sign in failure', data); + break; + } + }); + + getUser().then(userData => setUser(userData)); + }, []); + + function getUser() { + return Auth.currentAuthenticatedUser() + .then(userData => userData) + .catch(() => console.log('Not signed in')); + } + + return ( +
+

User: {user ? JSON.stringify(user.attributes) : 'None'}

+ {user ? ( + + ) : ( + + )} +
+ ); +} + +export default App; +``` +
+ + + + + +**Note for iOS Apps** + +In order for Amplify to listen for data from Cognito when linking back to your app, you will need to setup the `Linking` module in `AppDelegate.m` (see [React Native docs](https://reactnative.dev/docs/linking#enabling-deep-links) for more information): + +```objectivec +#import + +- (BOOL)application:(UIApplication *)application + openURL:(NSURL *)url + options:(NSDictionary *)options +{ + return [RCTLinkingManager application:application openURL:url options:options]; +} +``` + + +**In-App Browser Setup (optional, but recommended)** + +By default, Amplify will open the Cognito Hosted UI in Safari/Chrome, but you can override that behavior by providing a custom `urlOpener`. The sample below uses [react-native-inappbrowser-reborn](https://github.com/proyecto26/react-native-inappbrowser), but you can use any other in-app browser available. + +**Sample** + +```js +import React, { useEffect, useState } from 'react'; +import { Button, Linking, Text, View } from 'react-native'; +import { Amplify, Auth, Hub } from 'aws-amplify'; +import InAppBrowser from 'react-native-inappbrowser-reborn'; +import awsconfig from './aws-exports'; + +async function urlOpener(url, redirectUrl) { + await InAppBrowser.isAvailable(); + const { type, url: newUrl } = await InAppBrowser.openAuth(url, redirectUrl, { + showTitle: false, + enableUrlBarHiding: true, + enableDefaultShare: false, + ephemeralWebSession: false, + }); + + if (type === 'success') { + Linking.openURL(newUrl); + } +} + +Amplify.configure({ + ...awsconfig, + oauth: { + ...awsconfig.oauth, + urlOpener, + }, +}); + +function App() { + const [user, setUser] = useState(null); + + useEffect(() => { + Hub.listen('auth', ({ payload: { event, data } }) => { + switch (event) { + case 'signIn': + case 'cognitoHostedUI': + getUser().then(userData => setUser(userData)); + break; + case 'signOut': + setUser(null); + break; + case 'signIn_failure': + case 'cognitoHostedUI_failure': + console.log('Sign in failure', data); + break; + } + }); + + getUser().then(userData => setUser(userData)); + }, []); + + function getUser() { + return Auth.currentAuthenticatedUser() + .then(userData => userData) + .catch(() => console.log('Not signed in')); + } + + return ( + + User: {user ? JSON.stringify(user.attributes) : 'None'} + {user ? ( +