Skip to content

Commit 8b682d1

Browse files
yuhengshsjosefaidt
andauthored
Added an example page for setting up custom-auth-flows (#7947)
* added an example page for setting up custom-auth-flows * Update src/pages/[platform]/build-a-backend/functions/examples/custom-auth-flows/index.mdx Co-authored-by: josef <josef.aidt@gmail.com> * add links in switching-authentication-flows * updated docs * fixed wording issue for CUSTOM_AUTH challenge * Update index.mdx Co-authored-by: josef <josef.aidt@gmail.com> * updated callout * added paragraph to what and why SRP --------- Co-authored-by: josef <josef.aidt@gmail.com>
1 parent 5c34f48 commit 8b682d1

File tree

3 files changed

+237
-1
lines changed
  • src
    • directory
    • pages/[platform]/build-a-backend
      • auth/connect-your-frontend/switching-authentication-flows
      • functions/examples/custom-auth-flows

3 files changed

+237
-1
lines changed

src/directory/directory.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,9 @@ export const directory = {
417417
},
418418
{
419419
path: 'src/pages/[platform]/build-a-backend/functions/examples/s3-upload-confirmation/index.mdx'
420+
},
421+
{
422+
path: 'src/pages/[platform]/build-a-backend/functions/examples/custom-auth-flows/index.mdx'
420423
}
421424
]
422425
},

src/pages/[platform]/build-a-backend/auth/connect-your-frontend/switching-authentication-flows/index.mdx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,11 @@ in addition to a password in order to verify the identity of users. These challe
160160
or dynamic challenge questions. The `CUSTOM_WITH_SRP` flow requires a password when calling `signIn`. Both of
161161
these flows map to the `CUSTOM_AUTH` flow in Cognito.
162162

163-
To define your challenges for custom authentication flow, you need to implement three Lambda triggers for Amazon Cognito.
163+
<Callout>
164+
165+
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.
166+
167+
</Callout>
164168

165169
<Callout>
166170

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
import { getCustomStaticPath } from '@/utils/getCustomStaticPath';
2+
3+
export const meta = {
4+
title: 'Custom Auth Challenge',
5+
description:
6+
'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.',
7+
platforms: [
8+
'android',
9+
'angular',
10+
'flutter',
11+
'javascript',
12+
'nextjs',
13+
'react',
14+
'react-native',
15+
'swift',
16+
'vue'
17+
]
18+
};
19+
20+
export function getStaticPaths() {
21+
return getCustomStaticPath(meta.platforms);
22+
}
23+
24+
export function getStaticProps() {
25+
return {
26+
props: {
27+
meta
28+
}
29+
};
30+
}
31+
32+
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.
33+
This guide demonstrates how to implement both types of custom authentication flows using AWS Amplify with Lambda triggers.
34+
35+
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:
36+
37+
1. [Create auth challenge](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-create-auth-challenge.html)
38+
2. [Define auth challenge](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-define-auth-challenge.html)
39+
3. [Verify auth challenge response](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-verify-auth-challenge-response.html)
40+
41+
To get started, install the `aws-lambda` package, which is used to define the handler type.
42+
43+
```bash title="Terminal" showLineNumbers={false}
44+
npm add --save-dev @types/aws-lambda
45+
```
46+
47+
## Create auth challenge trigger
48+
49+
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.
50+
51+
```ts title="amplify/auth/create-auth-challenge/resource.ts"
52+
import { defineFunction } from "@aws-amplify/backend"
53+
54+
export const createAuthChallenge = defineFunction({
55+
name: "create-auth-challenge",
56+
})
57+
```
58+
59+
After creating the resource file, create the handler with the following contents:
60+
61+
```ts title="amplify/auth/create-auth-challenge/handler.ts"
62+
import type { CreateAuthChallengeTriggerHandler } from "aws-lambda";
63+
64+
export const handler: CreateAuthChallengeTriggerHandler = async (event) => {
65+
if (event.request.challengeName === "CUSTOM_CHALLENGE") {
66+
// Generate a random code for the custom challenge
67+
const challengeCode = "123456";
68+
69+
event.response.challengeMetadata = "TOKEN_CHECK";
70+
71+
event.response.publicChallengeParameters = {
72+
trigger: "true",
73+
code: challengeCode,
74+
};
75+
76+
event.response.privateChallengeParameters = { trigger: "true" };
77+
event.response.privateChallengeParameters.answer = challengeCode;
78+
}
79+
return event;
80+
};
81+
```
82+
83+
## Define auth challenge trigger
84+
85+
Next, you will want to create the trigger responsible for _defining_ the auth challenge flow, `define-auth-challenge`.
86+
87+
```ts title="amplify/auth/define-auth-challenge/resource.ts"
88+
import { defineFunction } from "@aws-amplify/backend"
89+
90+
export const defineAuthChallenge = defineFunction({
91+
name: "define-auth-challenge",
92+
})
93+
```
94+
95+
After creating the resource file, create the handler with the following contents if you are using `CUSTOM_WITHOUT_SRP`:
96+
97+
```ts title="amplify/auth/define-auth-challenge/handler.ts"
98+
import type { DefineAuthChallengeTriggerHandler } from "aws-lambda"
99+
100+
export const handler: DefineAuthChallengeTriggerHandler = async (event) => {
101+
// Check if this is the first authentication attempt
102+
if (event.request.session.length === 0) {
103+
// For the first attempt, we start with the custom challenge
104+
event.response.issueTokens = false;
105+
event.response.failAuthentication = false;
106+
event.response.challengeName = "CUSTOM_CHALLENGE";
107+
} else if (
108+
event.request.session.length === 1 &&
109+
event.request.session[0].challengeName === "CUSTOM_CHALLENGE" &&
110+
event.request.session[0].challengeResult === true
111+
) {
112+
// If this is the second attempt (session length 1),
113+
// it was a CUSTOM_CHALLENGE, and the result was successful
114+
event.response.issueTokens = true;
115+
event.response.failAuthentication = false;
116+
} else {
117+
// If we reach here, it means either:
118+
// 1. The custom challenge failed
119+
// 2. We've gone through more attempts than expected
120+
// In either case, we fail the authentication
121+
event.response.issueTokens = false;
122+
event.response.failAuthentication = true;
123+
}
124+
125+
return event;
126+
};
127+
```
128+
129+
Or if you are using `CUSTOM_WITH_SRP`:
130+
131+
```ts title="amplify/auth/define-auth-challenge/handler.ts"
132+
import type { DefineAuthChallengeTriggerHandler } from "aws-lambda"
133+
134+
export const handler: DefineAuthChallengeTriggerHandler = async (event) => {
135+
// First attempt: Start with SRP_A (Secure Remote Password protocol, step A)
136+
if (event.request.session.length === 0) {
137+
event.response.issueTokens = false;
138+
event.response.failAuthentication = false;
139+
event.response.challengeName = "SRP_A";
140+
} else if (
141+
event.request.session.length === 1 &&
142+
event.request.session[0].challengeName === "SRP_A" &&
143+
event.request.session[0].challengeResult === true
144+
) {
145+
// Second attempt: SRP_A was successful, move to PASSWORD_VERIFIER
146+
event.response.issueTokens = false;
147+
event.response.failAuthentication = false;
148+
event.response.challengeName = "PASSWORD_VERIFIER";
149+
} else if (
150+
event.request.session.length === 2 &&
151+
event.request.session[1].challengeName === "PASSWORD_VERIFIER" &&
152+
event.request.session[1].challengeResult === true
153+
) {
154+
// Third attempt: PASSWORD_VERIFIER was successful, move to CUSTOM_CHALLENGE
155+
event.response.issueTokens = false;
156+
event.response.failAuthentication = false;
157+
event.response.challengeName = "CUSTOM_CHALLENGE";
158+
} else if (
159+
event.request.session.length === 3 &&
160+
event.request.session[2].challengeName === "CUSTOM_CHALLENGE" &&
161+
event.request.session[2].challengeResult === true
162+
) {
163+
// Fourth attempt: CUSTOM_CHALLENGE was successful, authentication complete
164+
event.response.issueTokens = true;
165+
event.response.failAuthentication = false;
166+
} else {
167+
// If we reach here, it means one of the challenges failed or
168+
// we've gone through more attempts than expected
169+
event.response.issueTokens = false;
170+
event.response.failAuthentication = true;
171+
}
172+
173+
return event;
174+
};
175+
```
176+
177+
## Verify auth challenge response trigger
178+
179+
Lastly, create the trigger responsible for _verifying_ the challenge response. For the purpose of this example, the verification check will always return true.
180+
181+
```ts title="amplify/auth/verify-auth-challenge-response/resource.ts"
182+
import { defineFunction, secret } from "@aws-amplify/backend"
183+
184+
export const verifyAuthChallengeResponse = defineFunction({
185+
name: "verify-auth-challenge-response",
186+
})
187+
```
188+
189+
After creating the resource file, create the handler with the following contents:
190+
191+
```ts title="amplify/auth/verify-auth-challenge-response/handler.ts"
192+
import type { VerifyAuthChallengeResponseTriggerHandler } from "aws-lambda"
193+
194+
export const handler: VerifyAuthChallengeResponseTriggerHandler = async (
195+
event
196+
) => {
197+
event.response.answerCorrect = true;
198+
return event;
199+
};
200+
201+
```
202+
203+
## Configure auth resource
204+
205+
Finally, import and set the three triggers on your auth resource:
206+
207+
```ts title="amplify/auth/resource.ts"
208+
import { defineAuth } from "@aws-amplify/backend"
209+
import { createAuthChallenge } from "./create-auth-challenge/resource"
210+
import { defineAuthChallenge } from "./define-auth-challenge/resource"
211+
import { verifyAuthChallengeResponse } from "./verify-auth-challenge-response/resource"
212+
213+
/**
214+
* Define and configure your auth resource
215+
* @see https://docs.amplify.aws/gen2/build-a-backend/auth
216+
*/
217+
export const auth = defineAuth({
218+
loginWith: {
219+
email: true,
220+
},
221+
triggers: {
222+
createAuthChallenge,
223+
defineAuthChallenge,
224+
verifyAuthChallengeResponse,
225+
},
226+
})
227+
```
228+
229+
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.

0 commit comments

Comments
 (0)