Skip to content

Commit 5f70ece

Browse files
committed
updates examples, adds api, nits
1 parent 5c56db0 commit 5f70ece

File tree

1 file changed

+118
-40
lines changed
  • src/pages/[platform]/deploy-and-host/sandbox-environments/seed

1 file changed

+118
-40
lines changed

src/pages/[platform]/deploy-and-host/sandbox-environments/seed/index.mdx

Lines changed: 118 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ To get started, you need to create a seed script. This script will be executed w
4343
amplify/
4444
└── seed/
4545
└── seed.ts
46-
4746
```
4847

4948
## Setting up required permissions
@@ -105,9 +104,11 @@ import {
105104
getSecret,
106105
} from "@aws-amplify/seed";
107106
import { Amplify } from "aws-amplify";
108-
// @ts-expect-error this file will not exist until sandbox is created
109-
import outputs from "../../amplify_outputs.json";
107+
import { readFile } from 'node:fs/promises';
110108

109+
// this is used to get the amplify_outputs.json file as the file will not exist until sandbox is created
110+
const url = new URL("../../amplify_outputs.json", import.meta.url);
111+
const outputs = JSON.parse(await readFile(url, { encoding: "utf8" }));
111112
Amplify.configure(outputs);
112113

113114
const username = await getSecret("username");
@@ -155,9 +156,13 @@ export const auth = defineAuth({
155156
});
156157
```
157158

158-
Now to create a user with TOTP MFA enabled, you can write the following script:
159+
To create and sign in a user with TOTP MFA enabled, you can write the following script:
159160
For this example, we will use the `otpauth` library to generate TOTP codes.
160161

162+
```bash title="Terminal" showLineNumbers={false}
163+
npm install otpauth
164+
```
165+
161166
```typescript title="amplify/seed/seed.ts"
162167
import {
163168
ChallengeResponse,
@@ -167,38 +172,72 @@ import {
167172
import { Amplify } from "aws-amplify";
168173
import * as auth from "aws-amplify/auth";
169174
import * as otpauth from "otpauth";
170-
// @ts-expect-error this file will not exist until sandbox is created
171-
import outputs from "../../amplify_outputs.json";
175+
import { readFile } from 'node:fs/promises';
172176

177+
// this is used to get the amplify_outputs.json file as the file will not exist until sandbox is created
178+
const url = new URL("../../amplify_outputs.json", import.meta.url);
179+
const outputs = JSON.parse(await readFile(url, { encoding: "utf8" }));
173180
Amplify.configure(outputs);
174181

175182
const username = await getSecret("username");
176183
const password = await getSecret("password");
184+
let totpSecret: string;
185+
186+
const createTOTP = (secret: string) => {
187+
return new otpauth.TOTP({
188+
secret,
189+
algorithm: "SHA1",
190+
digits: 6,
191+
period: 30,
192+
});
193+
};
177194

178-
const setUpTOTPAndChallenge = async (
195+
const generateTOTPCode = (secret: string): string => {
196+
const totp = createTOTP(secret);
197+
return totp.generate();
198+
};
199+
200+
const challengeWithTOTP = async (
179201
totpSetup: auth.SetUpTOTPOutput
180202
): Promise<ChallengeResponse> => {
181-
// Using otpauth library to generate TOTP codes
182-
const totp = new otpauth.TOTP({ secret: totpSetup.sharedSecret });
183-
const answer = totp.generate();
203+
totpSecret = totpSetup.sharedSecret;
204+
const answer = generateTOTPCode(totpSetup.sharedSecret);
184205
return { challengeResponse: answer };
185206
};
207+
186208
const user = await createAndSignUpUser({
187-
username: username,
188-
password: password,
189-
signInAfterCreation: true,
209+
username,
210+
password,
211+
signInAfterCreation: false,
190212
signInFlow: "MFA",
191213
mfaPreference: "TOTP",
192214
totpSignUpChallenge: async (totpSetup) => {
193-
return await setUpTOTPAndChallenge(totpSetup);
215+
return await challengeWithTOTP(totpSetup);
194216
},
195217
});
196218

197219
console.log(`User ${user.username} was created`);
220+
221+
// Wait for a moment to ensure we get a fresh TOTP code
222+
await new Promise((resolve) => setTimeout(resolve, 35000));
223+
224+
const signIn = await signInUser({
225+
username,
226+
password,
227+
signInFlow: "MFA",
228+
signInChallenge: async () => {
229+
const answer = generateTOTPCode(totpSecret);
230+
return { challengeResponse: answer };
231+
},
232+
});
233+
234+
console.log(`User was signed in: ${signIn}`);
235+
198236
auth.signOut();
199237
```
200238

201-
This will create a user with the username and password with TOTP MFA enabled. The TOTP code is generated using the `otpauth` library.
239+
This will create a user with the username and password with TOTP MFA enabled. The TOTP code is generated using the `otpauth` library. The user will then be signed in and the TOTP code will be generated.
240+
The timeout ensures the previous TOTP code expires before generating a new code for sign-in. This prevents potential conflicts that could occur if the same TOTP code were used for both user creation and authentication.
202241

203242
Run the seed script
204243

@@ -240,10 +279,12 @@ import { getSecret, signInUser } from "@aws-amplify/seed";
240279
import { Amplify } from "aws-amplify";
241280
import * as auth from "aws-amplify/auth";
242281
import type { Schema } from "./../data/resource";
243-
244282
import { generateClient } from "aws-amplify/api";
245-
// @ts-expect-error this file will not exist until sandbox is created
246-
import outputs from "../../amplify_outputs.json";
283+
import { readFile } from 'node:fs/promises';
284+
285+
// this is used to get the amplify_outputs.json file as the file will not exist until sandbox is created
286+
const url = new URL("../../amplify_outputs.json", import.meta.url);
287+
const outputs = JSON.parse(await readFile(url, { encoding: 'utf8' }));
247288

248289
Amplify.configure(outputs);
249290

@@ -305,9 +346,11 @@ import { getSecret, signInUser } from "@aws-amplify/seed";
305346
import { Amplify } from "aws-amplify";
306347
import * as auth from "aws-amplify/auth";
307348
import * as storage from "aws-amplify/storage";
308-
// @ts-expect-error this file will not exist until sandbox is created
309-
import outputs from "../../amplify_outputs.json";
349+
import { readFile } from 'node:fs/promises';
310350

351+
// this is used to get the amplify_outputs.json file as the file will not exist until sandbox is created
352+
const url = new URL("../../amplify_outputs.json", import.meta.url);
353+
const outputs = JSON.parse(await readFile(url, { encoding: 'utf8' }));
311354
Amplify.configure(outputs);
312355

313356
const username = await getSecret("username");
@@ -414,10 +457,32 @@ const user3 = await createAndSignUpUser({
414457

415458
This behavior is particularly important when seeding multiple users in your application, as you'll need to carefully manage which user should be signed out at the end of your seeding process.
416459

460+
### MFA Challenge Handling
461+
462+
- For sign-up challenges, each MFA type has its specific challenge callback:
463+
- TOTP: `totpSignUpChallenge`
464+
- Email: `emailSignUpChallenge`
465+
466+
- For sign-in, there's a single `signInChallenge` callback that works for all MFA types
467+
468+
- Command line prompts work with all forms of MFA during sign-in
469+
- For sign-up, command line prompts work with EMAIL and SMS, but not with TOTP
470+
- When MFA is set to "Optional" in a user pool, users will be sent through the Password flow
471+
472+
### TOTP Considerations
473+
474+
When working with TOTP MFA, be aware of these behaviors:
475+
476+
- Using the same TOTP setup secret multiple times for different TOTP instances will result in an error
477+
- Using the same 6-digit passcode for both sign-up and sign-in (before it expires) will cause an error
478+
- When creating multiple users or performing multiple sign-ins with TOTP:
479+
- Wait for the previous passcode to expire before generating a new one
480+
- The example includes a timeout to handle this: `await new Promise((resolve) => setTimeout(resolve, 35000));`
481+
417482

418483
## Seed APIs
419484

420-
The `@aws-amplify/seed` package provides a set of APIs to help you seed your sandbox environment.
485+
The `@aws-amplify/seed` package provides a set of APIs that are compatible with the Amplify JS Auth APIs to help you seed your sandbox environment.
421486

422487
### Secret APIs
423488

@@ -437,39 +502,52 @@ Secret APIs use AWS Parameter Store and are compatible with `ampx sandbox secret
437502

438503
Auth APIs allow you to create and manage users in your sandbox environment and are compatible with Amplify JS Auth APIs.
439504

440-
- **createAndSignUpUser** - Creates a user based on the properties passed in, returns the created user's username and the sign-up flow they were created with
505+
- **createAndSignUpUser** - Creates a user based on the properties passed in
441506
```typescript
442507
const user = await createAndSignUpUser({
443508
username: 'username',
444509
password: 'password',
445510
signInAfterCreation: true,
446-
signInFlow: 'Password'
511+
signInFlow: 'Password',
512+
userAttributes?: StandardUserAttributes // Optional user attributes
447513
});
448514
```
449-
450-
**MFA Support:**
451-
- Can be used with MFA by passing a `signUpChallenge` callback function to automate the response to MFA challenges
452-
- If no `signUpChallenge` is provided, SMS and EMAIL MFA will prompt for input via command line, while TOTP will throw an error
453-
- Each MFA type has its own challenge callback (e.g., `totpSignUpChallenge` for TOTP)
454-
- The `totpSignUpChallenge` receives a `totpSetup` argument to help set up TOTP devices
455-
- When MFA is set to "Optional" in a user pool, users will be sent through the Password flow
456-
457-
- **addToUserGroup** - Adds a user to an existing user group
458-
```typescript
459-
await addToUserGroup(user, 'GroupName');
460-
```
461515

462516
- **signInUser** - Signs in a user using their username, password, and sign-in flow
463517
```typescript
464518
await signInUser({
465519
username: 'username',
466520
password: 'password',
467-
signInFlow: 'Password'
521+
signInFlow: 'Password' | 'MFA',
522+
signInChallenge?: () => Promise<ChallengeResponse> // Optional for MFA
468523
});
469524
```
470-
471-
**MFA Support:**
472-
- Can pass a `signInChallenge` callback to automate MFA responses
473-
- If no callback is provided, the user will be prompted for input via command line
474525

526+
- **addToUserGroup** - Adds a user to an existing user group
527+
```typescript
528+
await addToUserGroup({
529+
username: 'username' // User to add to group
530+
}, 'GroupName');
531+
```
532+
533+
### Additional APIs
534+
535+
The `@aws-amplify/seed` package additionally provides the following APIs:
536+
537+
- `AuthSignUp` - API for user sign-up configuration
538+
- `AuthUser` - API for user authentication information
539+
- `ChallengeResponse` - API for MFA challenge responses
540+
- `StandardUserAttributes` - API for managing user attributes during sign-up
541+
- `PasswordSignInFlow` - API for password-based authentication
542+
- `MfaSignUpFlow` - API for MFA during sign-up
543+
- `MfaSignInFlow` - API for MFA during sign-in
544+
- `MfaWithTotpSignUpFlow` - API for TOTP-specific MFA during sign-up
545+
546+
The following challenge callback APIs are available for MFA flows:
547+
- `emailSignUpChallenge` - Handles Email MFA during sign-up
548+
- `smsSignUpChallenge` - Handles SMS MFA during sign-up
549+
- `totpSignUpChallenge` - Handles TOTP MFA during sign-up
550+
- `signInChallenge` - Universal handler for all MFA types during sign-in
551+
552+
For information on using these APIs, refer to the [Amplify JS Auth API documentation](/[platform]/build-a-backend/auth/connect-your-frontend/).
475553

0 commit comments

Comments
 (0)