Skip to content
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

Added an example page for setting up custom-auth-flows #7947

Merged
merged 8 commits into from
Sep 12, 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
3 changes: 3 additions & 0 deletions src/directory/directory.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,9 @@ export const directory = {
},
{
path: 'src/pages/[platform]/build-a-backend/functions/examples/s3-upload-confirmation/index.mdx'
},
{
path: 'src/pages/[platform]/build-a-backend/functions/examples/custom-auth-flows/index.mdx'
}
]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,11 @@ in addition to a password in order to verify the identity of users. These challe
or dynamic challenge questions. The `CUSTOM_WITH_SRP` flow requires a password when calling `signIn`. Both of
these flows map to the `CUSTOM_AUTH` flow in Cognito.

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

To define your challenges for custom authentication flow, you need to implement three Lambda triggers for Amazon Cognito. Please visit [AWS Amplify Custom Auth Challenge example](/[platform]/build-a-backend/functions/examples/custom-auth-flows/) for set up instructions.

</Callout>

<Callout>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
import { getCustomStaticPath } from '@/utils/getCustomStaticPath';

export const meta = {
title: 'Custom Auth Challenge',
description:
'Leverage Custom Auth with and without SRP, allowing for a series of challenge and response cycles that can be customized to meet different requirements during sign in.',
platforms: [
'android',
'angular',
'flutter',
'javascript',
'nextjs',
'react',
'react-native',
'swift',
'vue'
]
};

export function getStaticPaths() {
return getCustomStaticPath(meta.platforms);
}

export function getStaticProps() {
return {
props: {
meta
}
};
}

Secure Remote Password (SRP) is a cryptographic protocol enabling password-based authentication without transmitting the password over the network. In Amazon Cognito custom authentication flows, CUSTOM_WITH_SRP incorporates SRP steps for enhanced security, while CUSTOM_WITHOUT_SRP bypasses these for a simpler process. The choice between them depends on your application's security needs and performance requirements.
This guide demonstrates how to implement both types of custom authentication flows using AWS Amplify with Lambda triggers.

You can use `defineAuth` and `defineFunction` to create an auth experience that uses `CUSTOM_WITH_SRP` and `CUSTOM_WITHOUT_SRP`. This can be accomplished by leveraging [Amazon Cognito's feature to define a custom auth challenge](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html#Custom-authentication-flow-and-challenges) and 3 triggers:
yuhengshs marked this conversation as resolved.
Show resolved Hide resolved

1. [Create auth challenge](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-create-auth-challenge.html)
2. [Define auth challenge](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-define-auth-challenge.html)
3. [Verify auth challenge response](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-verify-auth-challenge-response.html)

To get started, install the `aws-lambda` package, which is used to define the handler type.

```bash title="Terminal" showLineNumbers={false}
npm add --save-dev @types/aws-lambda
```

## Create auth challenge trigger

To get started, create the first of the three triggers, `create-auth-challenge`. This is the trigger responsible for creating the reCAPTCHA challenge after a password is verified.

```ts title="amplify/auth/create-auth-challenge/resource.ts"
import { defineFunction } from "@aws-amplify/backend"

export const createAuthChallenge = defineFunction({
name: "create-auth-challenge",
})
```

After creating the resource file, create the handler with the following contents:

```ts title="amplify/auth/create-auth-challenge/handler.ts"
import type { CreateAuthChallengeTriggerHandler } from "aws-lambda";

export const handler: CreateAuthChallengeTriggerHandler = async (event) => {
if (event.request.challengeName === "CUSTOM_CHALLENGE") {
// Generate a random code for the custom challenge
const challengeCode = "123456";

event.response.challengeMetadata = "TOKEN_CHECK";

event.response.publicChallengeParameters = {
trigger: "true",
code: challengeCode,
};

event.response.privateChallengeParameters = { trigger: "true" };
event.response.privateChallengeParameters.answer = challengeCode;
}
return event;
};
```

## Define auth challenge trigger

Next, you will want to create the trigger responsible for _defining_ the auth challenge flow, `define-auth-challenge`.

```ts title="amplify/auth/define-auth-challenge/resource.ts"
import { defineFunction } from "@aws-amplify/backend"

export const defineAuthChallenge = defineFunction({
name: "define-auth-challenge",
})
```

After creating the resource file, create the handler with the following contents if you are using `CUSTOM_WITHOUT_SRP`:

```ts title="amplify/auth/define-auth-challenge/handler.ts"
import type { DefineAuthChallengeTriggerHandler } from "aws-lambda"

export const handler: DefineAuthChallengeTriggerHandler = async (event) => {
// Check if this is the first authentication attempt
if (event.request.session.length === 0) {
// For the first attempt, we start with the custom challenge
event.response.issueTokens = false;
event.response.failAuthentication = false;
event.response.challengeName = "CUSTOM_CHALLENGE";
} else if (
event.request.session.length === 1 &&
event.request.session[0].challengeName === "CUSTOM_CHALLENGE" &&
event.request.session[0].challengeResult === true
) {
// If this is the second attempt (session length 1),
// it was a CUSTOM_CHALLENGE, and the result was successful
event.response.issueTokens = true;
event.response.failAuthentication = false;
} else {
// If we reach here, it means either:
// 1. The custom challenge failed
// 2. We've gone through more attempts than expected
// In either case, we fail the authentication
event.response.issueTokens = false;
event.response.failAuthentication = true;
}

return event;
};
```

Or if you are using `CUSTOM_WITH_SRP`:

```ts title="amplify/auth/define-auth-challenge/handler.ts"
import type { DefineAuthChallengeTriggerHandler } from "aws-lambda"

export const handler: DefineAuthChallengeTriggerHandler = async (event) => {
// First attempt: Start with SRP_A (Secure Remote Password protocol, step A)
if (event.request.session.length === 0) {
event.response.issueTokens = false;
yuhengshs marked this conversation as resolved.
Show resolved Hide resolved
event.response.failAuthentication = false;
event.response.challengeName = "SRP_A";
} else if (
event.request.session.length === 1 &&
event.request.session[0].challengeName === "SRP_A" &&
event.request.session[0].challengeResult === true
) {
// Second attempt: SRP_A was successful, move to PASSWORD_VERIFIER
event.response.issueTokens = false;
event.response.failAuthentication = false;
event.response.challengeName = "PASSWORD_VERIFIER";
} else if (
event.request.session.length === 2 &&
event.request.session[1].challengeName === "PASSWORD_VERIFIER" &&
event.request.session[1].challengeResult === true
) {
// Third attempt: PASSWORD_VERIFIER was successful, move to CUSTOM_CHALLENGE
event.response.issueTokens = false;
event.response.failAuthentication = false;
event.response.challengeName = "CUSTOM_CHALLENGE";
} else if (
event.request.session.length === 3 &&
event.request.session[2].challengeName === "CUSTOM_CHALLENGE" &&
event.request.session[2].challengeResult === true
) {
// Fourth attempt: CUSTOM_CHALLENGE was successful, authentication complete
event.response.issueTokens = true;
event.response.failAuthentication = false;
} else {
// If we reach here, it means one of the challenges failed or
// we've gone through more attempts than expected
event.response.issueTokens = false;
event.response.failAuthentication = true;
}

return event;
};
```

## Verify auth challenge response trigger

Lastly, create the trigger responsible for _verifying_ the challenge response. For the purpose of this example, the verification check will always return true.

```ts title="amplify/auth/verify-auth-challenge-response/resource.ts"
import { defineFunction, secret } from "@aws-amplify/backend"

export const verifyAuthChallengeResponse = defineFunction({
name: "verify-auth-challenge-response",
})
```

After creating the resource file, create the handler with the following contents:

```ts title="amplify/auth/verify-auth-challenge-response/handler.ts"
import type { VerifyAuthChallengeResponseTriggerHandler } from "aws-lambda"

export const handler: VerifyAuthChallengeResponseTriggerHandler = async (
event
) => {
event.response.answerCorrect = true;
return event;
};

```

## Configure auth resource

Finally, import and set the three triggers on your auth resource:

```ts title="amplify/auth/resource.ts"
import { defineAuth } from "@aws-amplify/backend"
import { createAuthChallenge } from "./create-auth-challenge/resource"
import { defineAuthChallenge } from "./define-auth-challenge/resource"
import { verifyAuthChallengeResponse } from "./verify-auth-challenge-response/resource"

/**
* Define and configure your auth resource
* @see https://docs.amplify.aws/gen2/build-a-backend/auth
*/
export const auth = defineAuth({
loginWith: {
email: true,
},
triggers: {
createAuthChallenge,
defineAuthChallenge,
verifyAuthChallengeResponse,
},
})
```

After deploying the changes, whenever a user attempts to sign in with `CUSTOM_WITH_SRP` or `CUSTOM_WITHOUT_SRP`, the Lambda challenges will be triggered.