Skip to content

chore(auth): add swift passwordless sign in/web authn changes #8131

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 4 commits into from
Nov 25, 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
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,79 @@ Your application's users can also sign in using passwordless methods. To learn m
</InlineFilter>
<InlineFilter filters={["swift"]}>

{/* */}
<BlockSwitcher>
<Block name="Async/Await">

```swift
// sign in with `smsOTP` as preferred factor
func signIn(username: String) async {
do {
let pluginOptions = AWSAuthSignInOptions(
authFlowType: .userAuth(preferredFirstFactor: .smsOTP))
let signInResult = try await Amplify.Auth.signIn(
username: username,
options: .init(pluginOptions: pluginOptions))
print("Sign in succeeded. Next step: \(signInResult.nextStep)")
} catch let error as AuthError {
print("Sign in failed \(error)")
} catch {
print("Unexpected error: \(error)")
}
}

// confirm sign in with the code received
func confirmSignIn() async {
do {
let signInResult = try await Amplify.Auth.confirmSignIn(challengeResponse: "<confirmation code received via SMS>")
print("Confirm sign in succeeded. Next step: \(signInResult.nextStep)")
} catch let error as AuthError {
print("Confirm sign in failed \(error)")
} catch {
print("Unexpected error: \(error)")
}
}

```

</Block>
<Block name="Combine">

```swift
// sign in with `smsOTP` as preferred factor
func signIn(username: String) -> AnyCancellable {
Amplify.Publisher.create {
let pluginOptions = AWSAuthSignInOptions(
authFlowType: .userAuth(preferredFirstFactor: .smsOTP))
try await Amplify.Auth.signIn(
username: username,
options: .init(pluginOptions: pluginOptions))
}.sink {
if case let .failure(authError) = $0 {
print("Sign in failed \(authError)")
}
}
receiveValue: { signInResult in
print("Sign in succeeded. Next step: \(signInResult.nextStep)")
}
}

// confirm sign in with the code received
func confirmSignIn() -> AnyCancellable {
Amplify.Publisher.create {
try await Amplify.Auth.confirmSignIn(challengeResponse: "<confirmation code received via SMS>")
}.sink {
if case let .failure(authError) = $0 {
print("Confirm sign in failed \(authError)")
}
}
receiveValue: { signInResult in
print("Confirm sign in succeeded. Next step: \(signInResult.nextStep)")
}
}
```

</Block>
</BlockSwitcher>

</InlineFilter>

Expand All @@ -1143,26 +1215,78 @@ Your application's users can also sign in using passwordless methods. To learn m
</InlineFilter>
<InlineFilter filters={["swift"]}>

{/* */}

</InlineFilter>

### WebAuthn Passkeys
<BlockSwitcher>
<Block name="Async/Await">

{/* blurb with supplemental information about handling sign-in, events, etc. */}
```swift
// sign in with `emailOTP` as preferred factor
func signIn(username: String) async {
do {
let pluginOptions = AWSAuthSignInOptions(
authFlowType: .userAuth(preferredFirstFactor: .emailOTP))
let signInResult = try await Amplify.Auth.signIn(
username: username,
options: .init(pluginOptions: pluginOptions))
print("Sign in succeeded. Next step: \(signInResult.nextStep)")
} catch let error as AuthError {
print("Sign in failed \(error)")
} catch {
print("Unexpected error: \(error)")
}
}

<InlineFilter filters={["angular", "javascript", "nextjs", "react", "react-native", "vue"]}>
// confirm sign in with the code received
func confirmSignIn() async {
do {
let signInResult = try await Amplify.Auth.confirmSignIn(challengeResponse: "<confirmation code received via SMS>")
print("Confirm sign in succeeded. Next step: \(signInResult.nextStep)")
} catch let error as AuthError {
print("Confirm sign in failed \(error)")
} catch {
print("Unexpected error: \(error)")
}
}

{/* */}
```

</InlineFilter>
<InlineFilter filters={["android"]}>
</Block>
<Block name="Combine">

{/* */}
```swift
// sign in with `emailOTP` as preferred factor
func signIn(username: String) -> AnyCancellable {
Amplify.Publisher.create {
let pluginOptions = AWSAuthSignInOptions(
authFlowType: .userAuth(preferredFirstFactor: .emailOTP))
try await Amplify.Auth.signIn(
username: username,
options: .init(pluginOptions: pluginOptions))
}.sink {
if case let .failure(authError) = $0 {
print("Sign in failed \(authError)")
}
}
receiveValue: { signInResult in
print("Sign in succeeded. Next step: \(signInResult.nextStep)")
}
}

</InlineFilter>
<InlineFilter filters={["swift"]}>
// confirm sign in with the code received
func confirmSignIn() -> AnyCancellable {
Amplify.Publisher.create {
try await Amplify.Auth.confirmSignIn(challengeResponse: "<confirmation code received via SMS>")
}.sink {
if case let .failure(authError) = $0 {
print("Confirm sign in failed \(authError)")
}
}
receiveValue: { signInResult in
print("Confirm sign in succeeded. Next step: \(signInResult.nextStep)")
}
}
```

{/* */}
</Block>
</BlockSwitcher>

</InlineFilter>
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ guard case .continueSignInWithFirstFactorSelection(let availableFactors) = signI
print("Available factors: \(availableFactors)")
```

The selection of the authentication method is done by the user. The user can choose from the available factors and proceed with the selected factor. You should call the `confirmSignIn` API with the selected factor to continue the sign-in process. Followign is an example if you want to proceed with the `emailOTP` factor selection:
The selection of the authentication method is done by the user. The user can choose from the available factors and proceed with the selected factor. You should call the `confirmSignIn` API with the selected factor to continue the sign-in process. Following is an example if you want to proceed with the `emailOTP` factor selection:

```swift
// Select emailOTP as the factor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,178 @@ export function getStaticProps() {
};
}

## Associate WebAuthN credentials

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

{/* */}

</InlineFilter>
<InlineFilter filters={["android"]}>

{/* */}

</InlineFilter>
<InlineFilter filters={["swift"]}>

<BlockSwitcher>
<Block name="Async/Await">

```swift
func associateWebAuthNCredentials() async {
do {
try await Amplify.Auth.associateWebAuthnCredential()
print("WebAuthn credential was associated")
} catch {
print("Associate WebAuthn Credential failed: \(error)")
}
}
```

</Block>
<Block name="Combine">

```swift
func associateWebAuthNCredentials() -> AnyCancellable {
Amplify.Publisher.create {
try await Amplify.Auth.associateWebAuthnCredential()
}.sink {
print("Associate WebAuthn Credential failed: \($0)")
}
receiveValue: { _ in
print("WebAuthn credential was associated")
}
}
```

</Block>
</BlockSwitcher>

</InlineFilter>

## List WebAuthN credentials

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

{/* */}

</InlineFilter>
<InlineFilter filters={["android"]}>

{/* */}

</InlineFilter>
<InlineFilter filters={["swift"]}>

<BlockSwitcher>
<Block name="Async/Await">

```swift
func listWebAuthNCredentials() async {
do {
let result = try await Amplify.Auth.listWebAuthnCredentials(
options: .init(pageSize: 5))

for credential in result.credentials {
print("Credential ID: \(credential.credentialId)")
print("Created At: \(credential.createdAt)")
print("Relying Party Id: \(credential.relyingPartyId)")
if let friendlyName = credential.friendlyName {
print("Friendly name: \(friendlyName)")
}
}

// Fetch the next page
if let nextToken = result.nextToken {
let nextResult = try await Amplify.Auth.listWebAuthnCredentials(
options: .init(
pageSize: 5,
nextToken: nextToken))
}
} catch {
print("Associate WebAuthn Credential failed: \(error)")
}
}
```

</Block>
<Block name="Combine">

```swift
func listWebAuthNCredentials() -> AnyCancellable {
Amplify.Publisher.create {
try await Amplify.Auth.listWebAuthnCredentials(
options: .init(pageSize: 5))
}.sink {
print("List WebAuthn Credential failed: \($0)")
}
receiveValue: { result in
for credential in result.credentials {
print("Credential ID: \(credential.credentialId)")
print("Created At: \(credential.createdAt)")
print("Relying Party Id: \(credential.relyingPartyId)")
if let friendlyName = credential.friendlyName {
print("Friendly name: \(friendlyName)")
}
}

if let nextToken = result.nextToken {
// Fetch the next page
}
}
}
```

</Block>
</BlockSwitcher>

</InlineFilter>

## Delete WebAuthN credentials

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

{/* */}

</InlineFilter>
<InlineFilter filters={["android"]}>

{/* */}

</InlineFilter>
<InlineFilter filters={["swift"]}>

<BlockSwitcher>
<Block name="Async/Await">

```swift
func deleteWebAuthNCredentials(credentialId: String) async {
do {
try await Amplify.Auth.deleteWebAuthnCredential(credentialId: credentialId)
print("WebAuthn credential was deleted")
} catch {
print("Delete WebAuthn Credential failed: \(error)")
}
}
```

</Block>
<Block name="Combine">

```swift
func deleteWebAuthNCredentials(credentialId: String) -> AnyCancellable {
Amplify.Publisher.create {
try await Amplify.Auth.deleteWebAuthnCredential(credentialId: credentialId)
}.sink {
print("Delete WebAuthn Credential failed: \($0)")
}
receiveValue: { _ in
print("WebAuthn credential was deleted")
}
}
```

</Block>
</BlockSwitcher>

</InlineFilter>