Subchapter 130.2
references/auth-backend.mdMarkdown9 KBView on GitHub
Prerequisites: Backend defined in
amplify/backend.tswithdefineBackend({ auth, data }).
Define authentication in amplify/auth/resource.ts:
import { defineAuth } from '@aws-amplify/backend';
export const auth = defineAuth({
loginWith: {
email: true,
// phone: true, // SMS-based login
},
userAttributes: {
preferredUsername: { required: false },
},
});Import into amplify/backend.ts:
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
defineBackend({ auth });export const auth = defineAuth({
loginWith: { email: true },
multifactor: {
mode: 'REQUIRED', // or 'OPTIONAL'
totp: true,
sms: true,
email: true,
},
});Set mode: 'REQUIRED' to enforce MFA for all users. 'OPTIONAL' lets
users enable it themselves.
Frontend impact: When MFA is enabled, the Authenticator component handles all MFA steps automatically. For custom UI, see auth-web.md for signInStep handling.
Passwordless login methods can coexist with traditional password-based auth.
Email OTP:
export const auth = defineAuth({
loginWith: {
email: {
otpLogin: true,
},
},
});SMS OTP:
export const auth = defineAuth({
loginWith: {
phone: {
otpLogin: true,
},
},
});WebAuthn / Passkeys:
export const auth = defineAuth({
loginWith: {
webAuthn: true,
},
});These passwordless methods can be combined with each other and with
password-based login in the same defineAuth configuration.
Use secret() for OAuth client secrets — hardcoding credentials exposes
them in source control.
import { defineAuth, secret } from '@aws-amplify/backend';
export const auth = defineAuth({
loginWith: {
email: true,
externalProviders: {
google: {
clientId: secret('GOOGLE_CLIENT_ID'),
clientSecret: secret('GOOGLE_CLIENT_SECRET'),
scopes: ['email', 'profile', 'openid'],
attributeMapping: {
email: 'email', // values are strings, NOT objects
fullname: 'name',
},
},
facebook: { clientId: secret('FB_CLIENT_ID'), clientSecret: secret('FB_CLIENT_SECRET') },
signInWithApple: {
clientId: secret('APPLE_CLIENT_ID'),
teamId: secret('APPLE_TEAM_ID'),
keyId: secret('APPLE_KEY_ID'),
privateKey: secret('APPLE_PRIVATE_KEY'),
},
loginWithAmazon: { clientId: secret('AMAZON_CLIENT_ID'), clientSecret: secret('AMAZON_CLIENT_SECRET') },
callbackUrls: ['http://localhost:3000/', 'https://myapp.com/'],
logoutUrls: ['http://localhost:3000/', 'https://myapp.com/'],
},
},
});Set secrets via CLI: echo -n "<value>" | npx ampx sandbox secret set MY_OAUTH_CLIENT_ID. (The documented approach uses an interactive prompt; piping with echo -n is a practical alternative for scripts.)
For provider-specific OAuth setup guides, consult AWS
documentation via available tools; when unavailable, use web
search or AWS CLI.
OIDC providers are configured inside loginWith.externalProviders:
import { defineAuth, secret } from '@aws-amplify/backend';
export const auth = defineAuth({
loginWith: {
email: true,
externalProviders: {
oidc: [{
name: 'MyOIDC',
clientId: secret('OIDC_CLIENT_ID'),
clientSecret: secret('OIDC_CLIENT_SECRET'),
issuerUrl: 'https://idp.example.com',
attributeMapping: { email: 'email' },
}],
callbackUrls: ['http://localhost:3000/'],
logoutUrls: ['http://localhost:3000/'],
},
},
});SAML is NOT supported in defineAuth — the ExternalProviderSpecificFactoryProps type has no saml property. The lower-level auth-construct package supports SAML, but it was never wired up to the high-level API. Use CDK escape hatches via backend.auth.resources to configure SAML providers:
// In backend.ts — SAML requires CDK-level configuration
const { cfnUserPool } = backend.auth.resources.cfnResources;
// Configure SAML identity provider via CfnUserPoolIdentityProviderConsult AWS documentation for CfnUserPoolIdentityProvider SAML configuration properties.
import { defineAuth } from '@aws-amplify/backend';
import { preSignUp } from './pre-sign-up/resource';
import { postConfirmation } from './post-confirmation/resource';
export const auth = defineAuth({
loginWith: { email: true },
triggers: {
preSignUp,
postConfirmation,
// Also: preAuthentication, postAuthentication,
// createAuthChallenge, defineAuthChallenge, verifyAuthChallengeResponse,
// preTokenGeneration, customMessage, userMigration
},
});Define each trigger with defineFunction:
// amplify/auth/pre-sign-up/resource.ts
import { defineFunction } from '@aws-amplify/backend';
export const preSignUp = defineFunction({ name: 'pre-sign-up' });Tip: Auth trigger handlers need
@types/aws-lambdafor TypeScript types.
If a trigger Lambda (e.g., postConfirmation) needs to write to a defineData table, this can create a circular dependency. Workarounds:
backend.auth.resources (avoids cycle when trigger is in auth stack):// backend.ts
const postConfirmFn = backend.auth.resources.userPool.triggers?.postConfirmation;
const table = backend.data.resources.tables['UserProfile'];
table.grantWriteData(postConfirmFn);
postConfirmFn.addEnvironment('TABLE_NAME', table.tableName);defineData) to avoid stack coupling.Guest access is enabled by default in Amplify Gen2 — the Cognito Identity Pool is created with allowUnauthenticatedIdentities: true automatically.
To use guest access in your data models, set defaultAuthorizationMode to 'iam' and add allow.guest() authorization rules:
const schema = a.schema({
Todo: a.model({
content: a.string(),
}).authorization(allow => [
allow.guest().to(['read']), // unauthenticated users can read
allow.owner(), // owners can CRUD
]),
});
export const data = defineData({
schema,
authorizationModes: {
defaultAuthorizationMode: 'iam', // required for guest access
apiKeyAuthorizationMode: { expiresInDays: 7 }, // optional alternative
},
});Security: Guest access grants unauthenticated users IAM-authorized access. For production, explicitly evaluate whether guest access is needed and prefer
allow.authenticated()as the default. If guest access is required, scope it to read-only on non-sensitive models only.
To disable guest access, use a CDK override in backend.ts:
const { cfnIdentityPool } = backend.auth.resources.cfnResources;
cfnIdentityPool.allowUnauthenticatedIdentities = false;defineFunction but NOT adding it to triggers: {} in defineAuth
causes a silent no-op — the function deploys but never fires.
Both define AND register: triggers: { preSignUp, postConfirmation }.secret() for
OAuth credentials exposes them in source control.'email', 'profile' explicitly or user attributes won’t populate.name maps to Cognito
fullname (NOT name). The attributeMapping values are plain strings,
NOT objects: { email: 'email', fullname: 'name' }.sms: true in MFA requires a phone
number attribute on the user pool — add phone_number to user attributes.
Similarly, email: true in MFA requires an email attribute on the user pool.ampx sandbox secret command only works for local sandbox environments.