Skip to content
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
10 changes: 10 additions & 0 deletions src/directory/directory.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,11 @@ const directory = {
route: '/lib/auth/signin',
filters: ['android', 'flutter', 'ios']
},
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you please use the existing Switching authentication flows directory entry? Rather than creating another one just for iOS?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar is the case here.. If I use the existing one, Switch Auth would be the first option whereas we want it to come after sign in..

title: 'Switching authentication flows',
route: '/lib/auth/switch-auth',
filters: ['ios']
},
{
title: 'Sign in with custom flow',
route: '/lib/auth/signin_with_custom_flow',
Expand Down Expand Up @@ -363,6 +368,11 @@ const directory = {
route: '/lib/auth/escapehatch',
filters: ['android', 'ios']
},
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here for advanced workflow?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I use the existing directory entry, it messes up the order.. For example:

Advance workflows would show up before Sign in which we don't want.

title: 'Advanced workflows',
route: '/lib/auth/advanced',
filters: ['ios']
},
{
title: 'Under the hood',
route: '/lib/auth/overview',
Expand Down
93 changes: 93 additions & 0 deletions src/fragments/lib/auth/ios/advanced/advanced.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
## Subscribing Events

You can take specific actions when users sign-in or sign-out by subscribing authentication events in your app. Please see our [Hub Module Developer Guide](/lib/utilities/hub) for more information.

## Identity Pool Federation

Imagine that you are creating a mobile app that accesses AWS resources, such as a game that runs on a mobile device and stores player and score information using Amazon S3 and DynamoDB.

When you write such an app, you make requests to AWS services that must be signed with an AWS access key. However, we strongly recommend that you do not embed or distribute long-term AWS credentials with apps that a user downloads to a device, even in an encrypted store. Instead, build your app so that it requests temporary AWS security credentials dynamically when needed using web identity federation. The supplied temporary credentials map to an AWS role that has only the permissions needed to perform the tasks required by the mobile app.

With web identity federation, you don't need to create custom sign-in code or manage your own user identities. Instead, users of your app can sign in using a well-known external identity provider (IdP), such as Login with Amazon, Facebook, Google, or any other OpenID Connect (OIDC)-compatible IdP. They can receive an authentication token, and then exchange that token for temporary security credentials in AWS that map to an IAM role with permissions to use the resources in your AWS account. Using an IdP helps you keep your AWS account secure, because you don't have to embed and distribute long-term security credentials with your application.

You can use `federateToIdentityPool` to get AWS credentials directly from Cognito Federated Identities and not use User Pool federation. If you have logged in with `Auth.signIn` you **can not** call `federateToIdentityPool` as Amplify will perform this federation automatically for you in the background. In general, you should only call `Auth.federatedSignIn()` when using OAuth flows.

You can use the escape hatch API `federateToIdentityPool` with a valid token from other social providers.

```swift
func federateToIdentityPools() async throws {
guard let authCognitoPlugin = try Amplify.Auth.getPlugin(
for: "awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin else {
fatalError("Unable to get the Auth plugin")
}
do {
let result = try await authCognitoPlugin.federateToIdentityPool(
withProviderToken: "YOUR_TOKEN", for: .facebook)
print("Federation successful with result: \(result)")
} catch {
print("Failed to federate to identity pools with error: \(error)")
}
}
```

<Callout>
Note that when federated, API's such as Auth.getCurrentUser() will throw an error as the user is not authenticated with User Pools.
</Callout>

### Retrieve Session

After federated login, you can retrieve session using the `Auth.fetchAuthSession` API.

### Token Refresh

<Callout>
NOTE: Automatic authentication token refresh is NOT supported when federated.
</Callout>

By default, Amplify will **NOT** automatically refresh the tokens from the federated providers. You will need to handle the token refresh logic and provide the new token to the `federateToIdentityPool` API.

### Clear Session

You can clear the federated session using the `clearFederationToIdentityPool` API.

```swift
func clearFederationToIdentityPools() async throws {
guard let authCognitoPlugin = try Amplify.Auth.getPlugin(
for: "awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin else {
fatalError("Unable to get the Auth plugin")
}
do {
try await authCognitoPlugin.clearFederationToIdentityPool()
print("Federation cleared successful")
} catch {
print("Clear federation failed with error: \(error)")
}
}
```

<Callout>
clearFederationToIdentityPool will only clear the session from local cache, developer need to handle signing out from the federated provider.
</Callout>

### Provide Custom Identity Id

You can provide a custom identity id to the `federateToIdentityPool` API. This is useful when you want to use the same identity id across multiple devices.

```swift
func federateToIdentityPoolsUsingCustomIdentityId() async throws {
guard let authCognitoPlugin = try Amplify.Auth.getPlugin(
for: "awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin else {
fatalError("Unable to get the Auth plugin")
}
do {
let identityId = "YOUR_CUSTOM_IDENTITY_ID"
let result = try await authCognitoPlugin.federateToIdentityPool(
withProviderToken: "YOUR_TOKEN",
for: .facebook,
options: .init(developerProvidedIdentityID: identityId))
print("Federation successful with result: \(result)")
} catch {
print("Failed to federate to identity pools with error: \(error)")
}
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ The Amplify CLI can be used to generate triggers required by a custom authentica

> More information on available triggers can be found in the [Cognito documentation](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-challenge.html).

### Custom Auth Flow with SRP

Cognito User Pool allows to start the custom authentication flow with SRP as the first step. If you would like to use this flow, setup Define Auth Lambda trigger to handle SRP_A as the first challenge as shown below:

```javascript
Expand Down Expand Up @@ -78,10 +80,71 @@ exports.handler = (event, context) => {
};
```

If your lambda is setup to start with `SRP` as the first step, make sure to initiate the signIn process with `SRP` as the authentication flow:
If your lambda is setup to start with `SRP` as the first step, make sure to initiate the signIn process with `customWithSRP` as the authentication flow:

```swift
let options = AWSAuthSignInOptions(authFlowType: .customWithSRP)
let signInResult = try await Amplify.Auth.signIn(username: username,
options: .init(pluginOptions: options))
let options = AWSAuthSignInOptions(
authFlowType: .customWithSRP)
let signInResult = try await Amplify.Auth.signIn(
username: username,
password: password,
options: .init(pluginOptions: options))
```

### CAPTCHA-based authentication

Here is the sample for creating a CAPTCHA challenge with a Lambda Trigger.

The `Create Auth Challenge Lambda Trigger` creates a CAPTCHA as a challenge to the user. The URL for the CAPTCHA image and the expected answer is added to the private challenge parameters:

```javascript
export const handler = async (event) => {
if (!event.request.session || event.request.session.length === 0) {
event.response.publicChallengeParameters = {
captchaUrl: <captcha url>,
};
event.response.privateChallengeParameters = {
answer: <expected answer>,
};
event.response.challengeMetadata = "CAPTCHA_CHALLENGE";
}
return event;
};
```

This `Define Auth Challenge Lambda Trigger` defines a custom challenge:

```javascript
export const handler = async (event) => {
if (!event.request.session || event.request.session.length === 0) {
// If we don't have a session or it is empty then send a CUSTOM_CHALLENGE
event.response.challengeName = "CUSTOM_CHALLENGE";
event.response.failAuthentication = false;
event.response.issueTokens = false;
} else if (event.request.session.length === 1 && event.request.session[0].challengeResult === true) {
// If we passed the CUSTOM_CHALLENGE then issue token
event.response.failAuthentication = false;
event.response.issueTokens = true;
} else {
// Something is wrong. Fail authentication
event.response.failAuthentication = true;
event.response.issueTokens = false;
}

return event;
};
```

The `Verify Auth Challenge Response Lambda Trigger` is used to verify a challenge answer:

```javascript
export const handler = async (event, context) => {
if (event.request.privateChallengeParameters.answer === event.request.challengeAnswer) {
event.response.answerCorrect = true;
} else {
event.response.answerCorrect = false;
}

return event;
};
```
95 changes: 95 additions & 0 deletions src/fragments/lib/auth/ios/switch-auth/switch-auth.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
`AWSCognitoAuthPlugin` allows you to switch between different auth flows while initiating signIn. You can configure the flow in the `amplifyconfiguration.json` file or pass the `authFlowType` as a runtime parameter to the `signIn` api call.

For client side authentication there are four different flows that can be configured during runtime:

1. `userSRP`: The `userSRP` flow uses the <a href="https://en.wikipedia.org/wiki/Secure_Remote_Password_protocol" target="_blank">SRP protocol (Secure Remote Password)</a> where the password never leaves the client and is unknown to the server. This is the recommended flow and is used by default.

2. `userPassword`: The `userPassword` flow will send user credentials unencrypted to the back-end. If you want to migrate users to Cognito using the "Migration" trigger and avoid forcing users to reset their passwords, you will need to use this authentication type because the Lambda function invoked by the trigger needs to verify the supplied credentials.

3. `customWithSRP`: The `customWithSRP` flow is used to start with SRP authentication and then switch to custom authentication. This is useful if you want to use SRP for the initial authentication and then use custom authentication for subsequent authentication attempts.

4. `customWithoutSRP`: The `customWithoutSRP` flow is used to start authentication flow **WITHOUT** SRP and then use a series of challenge and response cycles that can be customized to meet different requirements.

`Auth` can be configured to use the different flows at runtime by calling `signIn` with `AuthSignInOptions`'s `authFlowType` as `AuthFlowType.userPassword`, `AuthFlowType.customAuthWithoutSrp` or `AuthFlowType.customAuthWithSrp`. If you do not specify the `AuthFlowType` in `AuthSignInOptions`, the default flow (`AuthFlowType.userSRP`) will be used.

```swift
public enum AuthFlowType: String {

/// Authentication flow for the Secure Remote Password (SRP) protocol
case userSRP

/// Authentication flow for custom flow which are backed by lambda triggers.
/// Note that `custom`will always begin with a SRP flow.
@available(*, deprecated, message: "Use of custom is deprecated, use customWithSrp or customWithoutSrp instead")
case custom

/// Authentication flow which start with SRP and then move to custom auth flow
case customWithSRP

/// Authentication flow which starts without SRP and directly moves to custom auth flow
case customWithoutSRP

/// Non-SRP authentication flow; user name and password are passed directly.
/// If a user migration Lambda trigger is set, this flow will invoke the user migration
/// Lambda if it doesn't find the user name in the user pool.
case userPassword
}
```

<Callout>

Runtime configuration will take precedence and will override any auth flow type configuration present in amplifyconfiguration.json

</Callout>

> For more information about authentication flows, please visit [AWS Cognito developer documentation](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html#amazon-cognito-user-pools-custom-authentication-flow)

## USER_PASSWORD_AUTH flow

A use case for the `USER_PASSWORD_AUTH` authentication flow is migrating users into Amazon Cognito

A user migration Lambda trigger helps migrate users from a legacy user management system into your user pool. If you choose the USER_PASSWORD_AUTH authentication flow, users don't have to reset their passwords during user migration. This flow sends your users' passwords to the service over an encrypted SSL connection during authentication.

When you have migrated all your users, switch flows to the more secure SRP flow. The SRP flow doesn't send any passwords over the network.

```swift
func signIn(username: String, password: String) async throws {

let option = AWSAuthSignInOptions(authFlowType: .userPassword)
do {
let result = try await Amplify.Auth.signIn(
username: username,
password: password,
options: AuthSignInRequest.Options(pluginOptions: option))
print("Sign in succeeded with result: \(result)")
} catch {
print("Failed to sign in with error: \(error)")
}
}
```

### Setup auth backend
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add a section on how this works when we have auth type in amplifyconfiguration.json

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can do that as a follow up


In order to use the authentication flow `USER_PASSWORD_AUTH`, your Cognito app client has to be configured to allow it. In the AWS Console, this is done by ticking the checkbox at General settings > App clients > Show Details (for the affected client) > Enable username-password (non-SRP) flow. If you're using the AWS CLI or CloudFormation, update your app client by adding `USER_PASSWORD_AUTH` to the list of "Explicit Auth Flows".

### Migrate users with Amazon Cognito

Amazon Cognito provides a trigger to migrate users from your existing user directory seamlessly into Cognito. You achieve this by configuring your User Pool's "Migration" trigger which invokes a Lambda function whenever a user that does not already exist in the user pool authenticates, or resets their password.

In short, the Lambda function will validate the user credentials against your existing user directory and return a response object containing the user attributes and status on success. An error message will be returned if an error occurs. There's a documentation [here](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-import-using-lambda.html) on how to set up this migration flow and more detailed instructions [here](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-migrate-user.html#cognito-user-pools-lambda-trigger-syntax-user-migration) on how the lambda should handle request and response objects.

## CUSTOM_AUTH flow

Amazon Cognito User Pools supports customizing the authentication flow to enable custom challenge types, in addition to a password in order to verify the identity of users. The custom authentication flow is a series of challenge and response cycles that can be customized to meet different requirements. These challenge types may include CAPTCHAs or dynamic challenge questions.

To define your challenges for custom authentication flow, you need to implement three Lambda triggers for Amazon Cognito.

The flow is initiated by calling `signIn` with `AuthSignInOptions` configured with `AuthFlowType.customAuthWithSrp` OR `AuthFlowType.customAuthWithoutSrp`.

Follow the instructions in [Custom Auth Sign In](/lib/auth/signin_with_custom_flow) to learn about how to integrate custom authentication flow in your application with the Auth APIs.

<Callout>

For more information about working with Lambda Triggers for custom authentication challenges, please visit [Amazon Cognito Developer Documentation](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-challenge.html).

</Callout>
4 changes: 4 additions & 0 deletions src/pages/lib/auth/advanced/q/platform/[platform].mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ export const meta = {
description: `Learn more about advanced workflows in the Amplify auth category. This includes subscribing to events, identity pool federation, auth-related Lambda triggers and working with AWS service objects.`,
};

import ios0 from "/src/fragments/lib/auth/ios/advanced/advanced.mdx";

<Fragments fragments={{ios: ios0}} />

import js0 from "/src/fragments/lib/auth/js/advanced.mdx";

<Fragments fragments={{js: js0}} />
Expand Down
4 changes: 4 additions & 0 deletions src/pages/lib/auth/switch-auth/q/platform/[platform].mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ export const meta = {
description: `Learn more about how to switch between different auth flows in Amplify.`,
};

import ios0 from "/src/fragments/lib/auth/ios/switch-auth/switch-auth.mdx";

<Fragments fragments={{ios: ios0}} />

import js0 from "/src/fragments/lib/auth/js/switch-auth.mdx";

<Fragments fragments={{js: js0}} />
Expand Down