Skip to content

chore(flutter) v2 auth fixes #7612

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/fragments/lib-v1/auth/android/signin/20_confirmSignUp.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,9 @@ RxAmplify.Auth.confirmSignUp("username", "the code you received via email")

</Block>
</BlockSwitcher>

You will know the sign up flow is complete if you see the following in your console window:

```console
Confirm signUp succeeded
```
6 changes: 6 additions & 0 deletions src/fragments/lib-v1/auth/android/signin/30_signIn.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,9 @@ RxAmplify.Auth.signIn("username", "password")

</Block>
</BlockSwitcher>

You will know the sign in flow is complete if you see the following in your console window:

```console
Sign in succeeded
```
210 changes: 208 additions & 2 deletions src/fragments/lib-v1/auth/common/mfa/flows.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,27 @@ async function handleSignUp(username, password, phone_number, email) {

</InlineFilter>

<InlineFilter filters={["flutter"]}>
```dart
Future<void> signUpWithPhoneVerification(
String username,
String password,
) async {
await Amplify.Auth.signUp(
username: username,
password: password,
options: SignUpOptions(
userAttributes: <AuthUserAttributeKey, String>{
// ... if required
AuthUserAttributeKey.email: 'test@example.com',
AuthUserAttributeKey.phoneNumber: '+18885551234',
},
),
);
}
```
</InlineFilter>

By default, you have to verify a user account after they sign up using the `confirmSignUp` API, which will send a one-time password to the user's phone number or email, depending on your Amazon Cognito configuration.

<InlineFilter filters={['javascript', "angular", "nextjs", "react", "vue"]}>
Expand Down Expand Up @@ -159,6 +180,20 @@ async function handleSignUpConfirmation(username, confirmationCode) {

</InlineFilter>

<InlineFilter filters={["flutter"]}>
```dart
Future<void> confirmSignUpPhoneVerification(
String username,
String otpCode,
) async {
await Amplify.Auth.confirmSignUp(
username: username,
confirmationCode: otpCode,
);
}
```
</InlineFilter>

### Handling SMS MFA challenge during Sign In

After a user signs in, if they have MFA enabled for their account, a challenge will be returned that you would need to call the `confirmSignIn` API where the user provides their confirmation code sent to their phone number.
Expand Down Expand Up @@ -200,6 +235,20 @@ async function handleSignIn(username, password) {

</InlineFilter>

<InlineFilter filters={["flutter"]}>
```dart
Future<void> signInWithPhoneVerification(
String username,
String password,
) async {
await Amplify.Auth.signIn(
username: username,
password: password,
);
}
```
</InlineFilter>

If MFA is **ON** or enabled for the user, you must call `confirmSignIn` with the OTP sent to their phone.

<InlineFilter filters={['javascript', "angular", "nextjs", "react", "vue"]}>
Expand Down Expand Up @@ -240,6 +289,16 @@ async function handleSignInConfirmation(otpCode) {

</InlineFilter>

<InlineFilter filters={["flutter"]}>
```dart
Future<void> confirmSignInPhoneVerification(String otpCode) async {
await Amplify.Auth.confirmSignIn(
confirmationValue: otpCode,
);
}
```
</InlineFilter>

After a user has been signed in, call `updateMFAPreference` to record the MFA type as enabled for the user and optionally set it as preferred so that subsequent logins default to using this MFA type.

<InlineFilter filters={['javascript', "angular", "nextjs", "react", "vue"]}>
Expand All @@ -258,6 +317,18 @@ async function handleUpdateMFAPreference() {

</InlineFilter>

<InlineFilter filters={["flutter"]}>
```dart
Future<void> updateMfaPreferences() async {
final cognitoPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey);

await cognitoPlugin.updateMfaPreference(
sms: MfaPreference.enabled, // or .preferred
);
}
```
</InlineFilter>

## Multi-factor authentication with TOTP

You can use Time-based One-Time Password (TOTP) for multi-factor authentication (MFA) in your web or mobile applications. The Amplify Auth category includes support for TOTP setup and verification using authenticator apps, offering an integrated solution and enhanced security for your users. These apps, such as Google Authenticator, Microsoft Authenticator, have the TOTP algorithm built-in and work by using a shared secret key and the current time to generate short-lived, six digit passwords.
Expand Down Expand Up @@ -350,6 +421,33 @@ function handleSignInNextSteps(output) {

</InlineFilter>

<InlineFilter filters={["flutter"]}>
```dart
Future<void> signInUser(String username, String password) async {
try {
final result = await Amplify.Auth.signIn(
username: username,
password: password,
);
return _handleSignInResult(result);
} on AuthException catch (e) {
safePrint('Error signing in: ${e.message}');
}
}

Future<void> _handleSignInResult(SignInResult result) async {
switch (result.nextStep.signInStep) {
// ···
case AuthSignInStep.continueSignInWithTotpSetup:
final totpSetupDetails = result.nextStep.totpSetupDetails!;
final setupUri = totpSetupDetails.getSetupUri(appName: 'MyApp');
safePrint('Open URI to complete setup: $setupUri');
// ···
}
}
```
</InlineFilter>

The TOTP code can be obtained from the user via a text field or any other means. Once the user provides the TOTP code, call `confirmSignIn` with the TOTP code as the `challengeResponse` parameter.

<InlineFilter filters={['javascript', "angular", "nextjs", "react", "vue"]}>
Expand Down Expand Up @@ -390,9 +488,25 @@ async function handleSignInConfirmation(totpCode) {

</InlineFilter>

<InlineFilter filters={["flutter"]}>
```dart
Future<void> confirmTotpUser(String totpCode) async {
try {
final result = await Amplify.Auth.confirmSignIn(
confirmationValue: totpCode,
);
return _handleSignInResult(result);
} on AuthException catch (e) {
safePrint('Error confirming TOTP code: ${e.message}');
}
}
```
</InlineFilter>

<InlineFilter filters={["javascript", "angular", "nextjs", "react", "vue"]}>

After a user has been signed in, call `updateMFAPreference` to record the MFA type as enabled for the user and optionally set it as preferred so that subsequent logins default to using this MFA type.

<InlineFilter filters={['javascript', "angular", "nextjs", "react", "vue"]}>

```ts
import { updateMFAPreference } from 'aws-amplify/auth';
Expand Down Expand Up @@ -440,6 +554,20 @@ async function handleTOTPSetup() {

</InlineFilter>

<InlineFilter filters={["flutter"]}>
```dart
Future<void> setUpTotp() async {
try {
final totpSetupDetails = await Amplify.Auth.setUpTotp();
final setupUri = totpSetupDetails.getSetupUri(appName: 'MyApp');
safePrint('Open URI to complete setup: $setupUri');
} on AuthException catch (e) {
safePrint('An error occurred setting up TOTP: $e');
}
}
```
</InlineFilter>

Once the Authenticator app is set up, the user must generate a TOTP code and provide it to the library. Pass the code to `verifyTOTPSetup` to complete the TOTP setup process.

<InlineFilter filters={['javascript', "angular", "nextjs", "react", "vue"]}>
Expand Down Expand Up @@ -480,10 +608,22 @@ async function handleTOTPVerification(totpCode) {

</InlineFilter>

After TOTP setup is complete, call `updateMFAPreference` to record the MFA type as enabled for the user and optionally set it as preferred so that subsequent logins default to using this MFA type.
<InlineFilter filters={["flutter"]}>
```dart
Future<void> verifyTotpSetup(String totpCode) async {
try {
await Amplify.Auth.verifyTotpSetup(totpCode);
} on AuthException catch (e) {
safePrint('An error occurred verifying TOTP: $e');
}
}
```
</InlineFilter>

<InlineFilter filters={['javascript', "angular", "nextjs", "react", "vue"]}>

After TOTP setup is complete, call `updateMFAPreference` to record the MFA type as enabled for the user and optionally set it as preferred so that subsequent logins default to using this MFA type.

```ts
import { updateMFAPreference } from 'aws-amplify/auth';

Expand Down Expand Up @@ -532,6 +672,18 @@ async function handleFetchMFAPreference() {

</InlineFilter>

<InlineFilter filters={["flutter"]}>
```dart
Future<void> getCurrentMfaPreference() async {
final cognitoPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey);

final currentPreference = await cognitoPlugin.fetchMfaPreference();
safePrint('Enabled MFA types for user: ${currentPreference.enabled}');
safePrint('Preferred MFA type for user: ${currentPreference.preferred}');
}
```
</InlineFilter>

### Update the current user's MFA preferences

Invoke the following API to update the MFA preference for the current user.
Expand All @@ -558,7 +710,31 @@ async function handleUpdateMFAPreference() {

</InlineFilter>

<InlineFilter filters={["flutter"]}>
```dart
Future<void> updateMfaPreferences() async {
final cognitoPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey);

await cognitoPlugin.updateMfaPreference(
sms: MfaPreference.enabled,
totp: MfaPreference.preferred,
);
}
```
</InlineFilter>

<InlineFilter filters={["javascript", "angular", "nextjs", "react", "vue"]}>
If multiple MFA methods are enabled for the user, the `signIn` API will return `CONTINUE_SIGN_IN_WITH_MFA_SELECTION` as the next step in the auth flow. During this scenario, the user should be prompted to select the MFA method they want to use to sign in and their preference should be passed to `confirmSignIn`.
</InlineFilter>

<InlineFilter filters={["flutter"]}>
If multiple MFA methods are enabled for the user, the signIn API will return continueSignInWithMFASelection as the next step in the auth flow. During this scenario, the user should be prompted to select the MFA method they want to use to sign in and their preference should be passed to confirmSignIn.

The MFA types which are currently supported by Amplify Auth are:

- `MfaType.sms`
- `MfaType.totp`
</InlineFilter>

<InlineFilter filters={['javascript', "angular", "nextjs", "react", "vue"]}>

Expand Down Expand Up @@ -645,3 +821,33 @@ async function handleMFASelection(mfaType) {
</BlockSwitcher>

</InlineFilter>

<InlineFilter filters={["flutter"]}>
```dart
Future<void> _handleSignInResult(SignInResult result) async {
switch (result.nextStep.signInStep) {
// ···
case AuthSignInStep.continueSignInWithMfaSelection:
final allowedMfaTypes = result.nextStep.allowedMfaTypes!;
final selection = await _promptUserPreference(allowedMfaTypes);
return _handleMfaSelection(selection);
// ···
}
}

Future<MfaType> _promptUserPreference(Set<MfaType> allowedTypes) async {
// ···
}

Future<void> _handleMfaSelection(MfaType selection) async {
try {
final result = await Amplify.Auth.confirmSignIn(
confirmationValue: selection.confirmationValue,
);
return _handleSignInResult(result);
} on AuthException catch (e) {
safePrint('Error resending code: ${e.message}');
}
}
```
</InlineFilter>
Original file line number Diff line number Diff line change
@@ -1,21 +1,3 @@
```dart
Future<void> fetchAuthSession() async {
try {
final result = await Amplify.Auth.fetchAuthSession();
safePrint('User is signed in: ${result.isSignedIn}');
} on AuthException catch (e) {
safePrint('Error retrieving auth session: ${e.message}');
}
}
```

### Retrieving AWS credentials

Sometimes it can be helpful to retrieve the instance of the underlying plugin
which has more specific typing. In the case of Cognito, calling `fetchAuthSession`
on the Cognito plugin returns AWS-specific values such as the identity ID,
AWS credentials, and Cognito User Pool tokens.

```dart
Future<void> fetchCognitoAuthSession() async {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
The Amplify Auth category persists authentication-related information to make it available to other Amplify categories and to your application.

Amplify Flutter securely manages credentials and user identity information. You do not need to store, refresh, or delete credentials yourself. Amplify Flutter stores auth data on the device using platform capabilities such as [Keychain Services](https://developer.apple.com/documentation/security/keychain_services/) on iOS and macOS and [EncryptedSharedPreferences](https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences) on Android.

<Callout info={true} warning={false}>
Expand Down
6 changes: 6 additions & 0 deletions src/fragments/lib-v1/auth/ios/signin/20_confirmSignUp.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,9 @@ func confirmSignUp(for username: String, with confirmationCode: String) -> AnyCa
</Block>

</BlockSwitcher>

You will know the sign up flow is complete if you see the following in your console window:

```console
Confirm signUp succeeded
```
6 changes: 6 additions & 0 deletions src/fragments/lib-v1/auth/ios/signin/30_signIn.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,9 @@ func signIn(username: String, password: String) -> AnyCancellable {
</Block>

</BlockSwitcher>

You will know the sign in flow is complete if you see the following in your console window:

```console
Sign in succeeded
```
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ An intentional decision with Amplify Auth was to avoid any public methods exposi

With Auth, you simply sign in and it handles everything else needed to keep the credentials up to date and vend them to the other categories.

However, if you need to access them in relation to working with an API outside Amplify or want access to AWS specific identifying information (e.g. IdentityId), you can access these implementation details by casting the result of fetchAuthSession as follows:
However, if you need to access them in relation to working with an API outside Amplify or want access to AWS specific identifying information (e.g. IdentityId), you can access these implementation by following the example below:

import android0 from '/src/fragments/lib-v1/auth/android/access_credentials/10_fetchAuthSession.mdx';

Expand Down
12 changes: 0 additions & 12 deletions src/fragments/lib-v1/auth/native_common/signin/common.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,6 @@ import flutter8 from '/src/fragments/lib-v1/auth/flutter/signin/20_confirmSignUp

<Fragments fragments={{ flutter: flutter8 }} />

You will know the sign up flow is complete if you see the following in your console window:

```console
Confirm signUp succeeded
```

## Sign in a user

Implement a UI to get the username and password from the user. After the user enters the username and password you can start the sign in flow by calling the following method:
Expand All @@ -66,12 +60,6 @@ import flutter11 from '/src/fragments/lib-v1/auth/flutter/signin/30_signIn.mdx';

<Fragments fragments={{ flutter: flutter11 }} />

You will know the sign in flow is complete if you see the following in your console window:

```console
Sign in succeeded
```

You have now successfully registered a user and authenticated with that user's username and password with Amplify. The Authentication category supports other mechanisms for authentication such as web UI based sign in, sign in using other providers etc that you can explore in the other sections.

import flutter12 from '/src/fragments/lib-v1/auth/flutter/signin/60_runtime_auth_flow.mdx';
Expand Down
Loading
Loading