Subchapter 130.3
references/auth-mobile.mdMarkdown14 KBView on GitHub
Initialize Amplify with the Auth plugin before using this feature:
Flutter — lib/main.dart:
await Amplify.addPlugins([AmplifyAuthCognito()]);
await Amplify.configure(amplifyConfig);Generate dart outputs:
npx ampx sandbox --outputs-format dart --outputs-out-dir lib
Swift (Apple platforms):
try Amplify.add(plugin: AWSCognitoAuthPlugin())
try Amplify.configure(with: .amplifyOutputs)Drag
amplify_outputs.jsoninto the Xcode project navigator so it is included in the app bundle.
Android:
Amplify.addPlugin(AWSCognitoAuthPlugin())
Amplify.configure(AmplifyOutputs(R.raw.amplify_outputs), applicationContext)Place
amplify_outputs.jsoninapp/src/main/res/raw/. Enable core library desugaring for API level < 26.Backend required: Auth must be defined in
amplify/auth/resource.tsusingdefineAuth— see auth-backend.md.
All three mobile platforms provide a drop-in Authenticator component that
handles sign-in, sign-up, MFA, social login, passwordless, password reset, and
all intermediate auth states automatically. Use it unless you need a fully
custom UI. Zero manual signInStep handling is required.
Passwordless: The Authenticator component handles passwordless flows (email OTP, SMS OTP, and WebAuthn/passkey) automatically when configured in
defineAuth. No custom UI code needed for passwordless authentication. Custom OTP/passkey flows require additional challenge handling.Passwordless WebAuthn:
- Swift:
.userChoice(preferredAuthFactor: .webAuthn)- Android:
AuthenticationFlow.UserChoice(preferredFirstFactor = AuthFactorType.WEB_AUTHN)
Dependencies — add to pubspec.yaml:
flutter pub add amplify_flutter amplify_auth_cognito amplify_authenticatorUsage — wrap your MaterialApp and set its builder:
import 'package:amplify_authenticator/amplify_authenticator.dart';
import 'package:flutter/material.dart';
// After Amplify is configured (see Prerequisites above):
@override
Widget build(BuildContext context) {
return Authenticator(
child: MaterialApp(
builder: Authenticator.builder(),
home: const Scaffold(
body: Center(child: Text('You are logged in!')),
),
),
);
}Dependencies — add both SPM packages in Xcode (File > Add Packages…):
| Package | URL | Libraries |
|---|---|---|
| Amplify Library for Swift | https://github.com/aws-amplify/amplify-swift | Amplify, AWSCognitoAuthPlugin |
| Amplify UI Swift Authenticator | https://github.com/aws-amplify/amplify-ui-swift-authenticator | Authenticator |
SPM versioning: For both packages, select “Up to Next Major Version” in Xcode’s dependency rule. Do NOT pin to a specific branch (e.g.,
main) — use “Up to Next Major Version” to get compatible updates automatically.
Usage — SwiftUI entry point:
import Amplify
import Authenticator
import AWSCognitoAuthPlugin
import SwiftUI
@main
struct MyApp: App {
// init() — configure Amplify (see Prerequisites above)
var body: some Scene {
WindowGroup {
Authenticator { state in
VStack {
Text("Hello, \(state.user.username)")
Button("Sign out") {
Task { await state.signOut() }
}
}
}
}
}
}Passwordless / user-choice flow:
Authenticator(authenticationFlow: .userChoice(
preferredAuthFactor: .webAuthn
)) { state in
Text("Welcome \(state.user.username)!")
}Dependencies — add to your app’s build.gradle.kts:
Core library desugaring required — see Prerequisites above.
dependencies {
implementation("com.amplifyframework.ui:authenticator:<version>")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:<version>")
}INTERNET permission is required in AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>Usage — Jetpack Compose:
import com.amplifyframework.ui.authenticator.ui.Authenticator
import com.amplifyframework.ui.authenticator.SignedInState
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Authenticator { state ->
Column {
Text("Signed in as ${state.user.username}")
val scope = rememberCoroutineScope()
Button(onClick = { scope.launch { state.signOut() } }) {
Text("Sign Out")
}
}
}
}
}
}Passwordless / user-choice flow:
val authenticatorState = rememberAuthenticatorState(
authenticationFlow = AuthenticationFlow.UserChoice(
preferredFirstFactor = AuthFactorType.WEB_AUTHN
)
)
Authenticator(state = authenticatorState) { state ->
Text("Welcome ${state.user.username}!")
}Use the low-level Auth APIs when you need full control over the UI. Each
platform returns a nextStep from signIn / signUp — switch on it and
call confirmSignIn as needed. The Authenticator handles all these steps
automatically; the list below is for reference when building custom flows.
import 'package:amplify_flutter/amplify_flutter.dart';Sign in:
final result = await Amplify.Auth.signIn(
username: username,
password: password,
);
if (result.isSignedIn) {
safePrint('Sign in complete');
} else {
// Handle result.nextStep.signInStep — e.g.:
// confirmSignInWithSmsMfaCode → prompt for SMS code, call confirmSignIn
// confirmSignInWithTotpMfaCode → prompt for TOTP code, call confirmSignIn
// confirmSignInWithNewPassword → prompt new password, call confirmSignIn
// done → authenticated
}Confirm sign-in (for MFA / challenge steps):
final result = await Amplify.Auth.confirmSignIn(
confirmationValue: codeFromUser,
);Sign up:
final result = await Amplify.Auth.signUp(
username: username,
password: password,
options: SignUpOptions(
userAttributes: {AuthUserAttributeKey.email: email},
),
);
if (result.nextStep.signUpStep == AuthSignUpStep.confirmSignUp) {
// Prompt for confirmation code
}Confirm sign-up:
await Amplify.Auth.confirmSignUp(
username: username,
confirmationCode: code,
);Uses async/await.
import AmplifySign in:
do {
let result = try await Amplify.Auth.signIn(
username: username,
password: password
)
switch result.nextStep {
case .done:
print("Sign in succeeded")
case .confirmSignInWithSMSMFACode(let details, _):
print("SMS code sent to \(details.destination)")
// Prompt user, then call confirmSignIn
case .confirmSignInWithTOTPCode:
// Prompt for TOTP code, then call confirmSignIn
default:
print("Next step: \(result.nextStep)")
}
} catch let error as AuthError {
print("Sign in failed: \(error)")
}Confirm sign-in:
let result = try await Amplify.Auth.confirmSignIn(
challengeResponse: codeFromUser
)Sign up:
let options = AuthSignUpRequest.Options(
userAttributes: [AuthUserAttribute(.email, value: email)]
)
let result = try await Amplify.Auth.signUp(
username: username,
password: password,
options: options
)
if case .confirmUser(let details, _, _) = result.nextStep {
print("Confirmation sent to \(String(describing: details))")
}Confirm sign-up:
try await Amplify.Auth.confirmSignUp(
for: username,
confirmationCode: code
)Android supports both Kotlin coroutines and callbacks. Coroutines are recommended.
import com.amplifyframework.kotlin.core.Amplify
import com.amplifyframework.auth.AuthUserAttributeKey
import com.amplifyframework.auth.options.AuthSignUpOptionsSign in (coroutines — recommended):
try {
val result = Amplify.Auth.signIn("username", "password")
if (result.isSignedIn) {
Log.i("Auth", "Sign in succeeded")
} else {
// Handle result.nextStep.signInStep — e.g.:
// CONFIRM_SIGN_IN_WITH_SMS_MFA_CODE → prompt SMS code
// CONFIRM_SIGN_IN_WITH_TOTP_CODE → prompt TOTP code
// DONE → authenticated
Log.i("Auth", "Next step: ${result.nextStep.signInStep}")
}
} catch (error: AuthException) {
Log.e("Auth", "Sign in failed", error)
}Sign in (callbacks — alternative):
import com.amplifyframework.core.Amplify // Java facade for callback style
Amplify.Auth.signIn("username", "password",
{ result -> Log.i("Auth", "Signed in: ${result.isSignedIn}") },
{ error -> Log.e("Auth", "Sign in failed", error) }
)Confirm sign-in (coroutines):
try {
val result = Amplify.Auth.confirmSignIn("code from user")
Log.i("Auth", "Confirmed: $result")
} catch (error: AuthException) {
Log.e("Auth", "Confirm failed", error)
}Sign up (coroutines):
val options = AuthSignUpOptions.builder()
.userAttributes(listOf(
AuthUserAttribute(AuthUserAttributeKey.email(), email)
))
.build()
try {
val result = Amplify.Auth.signUp("username", "password", options)
Log.i("Auth", "Sign up step: ${result.nextStep.signUpStep}")
} catch (error: AuthException) {
Log.e("Auth", "Sign up failed", error)
}Confirm sign-up (coroutines):
try {
Amplify.Auth.confirmSignUp("username", "123456")
} catch (error: AuthException) {
Log.e("Auth", "Confirm sign-up failed", error)
}Social sign-in uses an OAuth web UI redirect. Callback URLs must match the
callbackUrls configured in your defineAuth backend resource.
Flutter:
final result = await Amplify.Auth.signInWithWebUI(
provider: AuthProvider.google,
);Platform setup for Flutter OAuth:
<intent-filter> with your callback scheme to MainActivity in AndroidManifest.xml.Swift:
let result = try await Amplify.Auth.signInWithWebUI(
for: .google,
presentationAnchor: window
)Platform setup: Add callback URL scheme to Info.plist under CFBundleURLSchemes.
Android (coroutines):
try {
val result = Amplify.Auth.signInWithSocialWebUI(
AuthProvider.google(), activity
)
Log.i("Auth", "Social sign-in OK: $result")
} catch (error: AuthException) {
Log.e("Auth", "Social sign-in failed", error)
}Platform setup: Add HostedUIRedirectActivity with your callback scheme to AndroidManifest.xml:
<activity
android:name="com.amplifyframework.auth.cognito.activities.HostedUIRedirectActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" />
</intent-filter>
</activity><uses-permission android:name="android.permission.INTERNET"/> in
AndroidManifest.xml, all auth calls fail with a network error.callbackUrls in your defineAuth backend resource.
A mismatch causes a silent redirect failure.nextStep returned from signIn must be handled. Ignoring
steps like MFA confirmation causes the auth flow to stall silently. The
Authenticator component handles all steps automatically.