@aws-blocks/bb-auth-basic

中文文档
0.2.0发布于 2026/11/02Apache-2.0TypeScriptES Module构建来源已验证近 30 天下载 6.4万

Simple username/password authentication with JWT sessions, password policy, and optional code-confirmed signup and password reset.

原始使用文档
文档目录16 节
  1. Quick Start
  2. Frontend: render the login UI and gate the app
  3. API
  4. Server-Side Methods
  5. buildApi() (deprecated)
  6. Options
  7. Password Policy
  8. Code Delivery
  9. Error Handling
  10. Branching on the setAuthState client path
  11. UI Components
  12. AuthBasicUser
  13. Re-exported types
  14. Cookies and sessions
  15. Revocation: read cost and consistency
  16. Sign-in throttling

@aws-blocks/bb-auth-basic

Simple username/password authentication with JWT sessions, password policy, and optional code-confirmed signup and password reset.

When to use: Prototyping, internal tools, MVPs, or apps that don't need OAuth/OIDC complexity.

When NOT to use: For social sign-in (Google, GitHub), use AuthOIDC. For MFA, user groups, or custom user attributes, use AuthCognito.

Design & mock parity details: DESIGN.md

Quick Start

import { Scope, ApiNamespace } from '@aws-blocks/core';
import { AuthBasic } from '@aws-blocks/bb-auth-basic';

const scope = new Scope('my-app');
const auth = new AuthBasic(scope, 'auth', {
  sessionDuration: 86400,
  passwordPolicy: { minLength: 8, requireDigits: true },
  // Optional: enable code-confirmed signup and password reset
  codeDelivery: async (username, code) => {
    await sendEmail(username, `Your verification code: ${code}`);
  },
});

export const api = new ApiNamespace(scope, 'api', (context) => ({
  async getProfile() {
    const user = await auth.requireAuth(context);
    return { username: user.username, createdAt: user.createdAt };
  },
}));

// Export the state machine API for the Authenticator component
export const authApi = auth.createApi();

The client namespace is taken from the export name you choose here (so import { authApi } from 'aws-blocks' matches); the 'auth' label inside createApi() is internal and does not affect the wire namespace.

Frontend: render the login UI and gate the app

Exporting authApi provisions the backend, but it does not render a login screen — your SPA still shows nothing until you mount an auth component. If you stop at the backend, an authenticated app opens to a blank/landing page and every signed-in route is unreachable. Mount the shipped Authenticator UI in your SPA entrypoint and gate the app shell behind it:

// src/index.ts — the SPA entrypoint
import { api, authApi } from 'aws-blocks';
import { AccountMenuBar, AuthenticatedContent } from '@aws-blocks/blocks/ui';

// A Sign In / account control that opens the Authenticator (sign-up / sign-in /
// confirm-code / reset) when clicked. No testids to wire by hand — the shipped
// components already carry them (see auth-common/CUSTOMIZING-AUTH-UI.md).
document.getElementById('menu-bar')!.appendChild(AccountMenuBar(authApi));

// Gate the app shell: the render callback runs only when a session exists;
// the fallback shows while signed out.
const signInMessage = document.createElement('p');
signInMessage.textContent = 'Sign in to get started.';

document.getElementById('app')!.appendChild(
  AuthenticatedContent(authApi, (user) => {
    const el = document.createElement('div');
    el.textContent = `Welcome, ${user.username}`;
    // ...build the authenticated app here, calling your `api` methods...
    return el;
  }, signInMessage),
);
  • AccountMenuBar(authApi) renders the entry point that pops the Authenticator. To embed the form inline instead, use Authenticator(authApi) directly.
  • AuthenticatedContent(authApi, render, fallback) subscribes to the session and swaps between render(user) and fallback on sign-in/out — this is what gates the shell.
  • Both come from @aws-blocks/blocks/ui (re-exported from @aws-blocks/auth-common/ui).
  • The sign-up/sign-in fields and buttons ship stable testids an e2e suite can target directly — authenticator-username, authenticator-password, authenticator-code, and authenticator-action-signUp / authenticator-action-signIn (one authenticator-<field> per input, one authenticator-action-<action> per action). The full selector contract is in auth-common/CUSTOMIZING-AUTH-UI.md; don't fork the component to add your own.

See the scaffolded default template's src/index.ts for a complete working app built on this seam.

API

const auth = new AuthBasic(scope, id, options?)

Server-Side Methods

MethodReturnsDescription
requireAuth(context)Promise<AuthBasicUser>Require auth. Throws 401 — SessionRevokedException if the session was revoked (signed out), else SessionExpiredException if absent/expired.
checkAuth(context)Promise<boolean>Check if signed in.
getCurrentUser(context)Promise<AuthBasicUser | null>Get current user or null.
signUp(username, password)Promise<void>Register a new user. If codeDelivery is set, user is unconfirmed until code is verified.
confirmSignUp(username, code)Promise<void>Confirm signup with verification code. Only when codeDelivery is set.
signIn(username, password, context)Promise<AuthBasicUser>Authenticate and set session cookie. Rejects unconfirmed users. When lockout is enabled, throws AccountLockedException (429) while the account is locked after too many failed attempts.
signOut(context)Promise<void>Revoke the presented session server-side (a replayed cookie is rejected) and clear the session cookie. The local cookie is always cleared; if the server-side revocation write fails it throws SignOutIncompleteException (503) so the caller can retry.
resetPassword(username)Promise<void>Send a password reset code. Only when codeDelivery is set.
confirmResetPassword(username, code, newPassword)Promise<void>Complete password reset with verification code.
createApi()ApiNamespaceCreate the state machine API for the Authenticator component.

Action input shape: createApi() returns the state-machine API. setAuthState takes a single FLAT discriminated input — { action, ...fields }, never a nested { action, data: {...} }. Examples: { action: 'signIn', username, password }, { action: 'signUp', username, password }, { action: 'confirmSignUp', username, code, password }, { action: 'resetPassword', username }, { action: 'confirmResetPassword', username, code, newPassword }. See the AuthActionInput type in @aws-blocks/auth-common.

buildApi() (deprecated)

Deprecated — use createApi(). Retained for backward compatibility. Unlike createApi() (which exposes the getAuthState/setAuthState state machine), buildApi() returns an ApiNamespace with a flat RPC surface. Documented here so existing callers can find the shapes. The request context is captured by the namespace factory, so the exposed methods take no context argument:

MethodSignatureReturns
signUp(username, password)(username: string, password: string) => Promise<{ username: string }>{ username }. Also sets the session cookie immediately when codeDelivery is not configured.
signIn(username, password)(username: string, password: string) => Promise<{ username: string }>{ username }. Sets the session cookie.
signOut()() => Promise<{ success: true }>{ success: true }. Revokes the presented session server-side and clears the session cookie.
getCurrentUser()() => Promise<AuthBasicUser | null>The signed-in user, or null.

Options

OptionTypeDefaultDescription
sessionDurationnumber86400 (24h)Session duration in seconds.
passwordPolicyPasswordPolicy{}Password requirements.
codeDelivery(username, code) => Promise<void>—Code delivery callback. Enables confirmed signup and password reset.
loggerChildLogger—Optional logger for internal operations. When omitted, a default Logger at error level is created.
crossDomainbooleanfalseSet to true when the frontend and API are on different registrable domains (e.g. frontend on Vercel, API on AWS). Switches the session cookie to SameSite=None; Secure; Partitioned.
lockoutLockoutPolicy— (disabled)Sign-in throttling / account lockout. Omit to disable (default). Pass {} to enable with defaults, or override maxAttempts (5), windowSeconds (900), lockoutSeconds (900). See Sign-in throttling.

Password Policy

FieldTypeDefaultDescription
minLengthnumber8Minimum password length.
requireUppercasebooleanfalseRequire uppercase letter.
requireLowercasebooleanfalseRequire lowercase letter.
requireDigitsbooleanfalseRequire digit.
requireSpecialCharsbooleanfalseRequire special character.

Code Delivery

When codeDelivery is provided:

  • Signup creates the user in an unconfirmed state. The code is delivered via the callback. The user must call confirmSignUp before they can sign in.
  • Password reset is available as a state machine action. The code is delivered via the callback. The user enters the code + new password to complete the reset.
  • Verification codes are 6-digit numeric strings, valid for 10 minutes.
  • Codes are HMAC-hashed before storage — never stored in plain text.
  • Each code allows at most 5 failed guesses (signup confirmation and password reset alike); after the 5th wrong guess the code is invalidated and the user must request a new one. This is always on — a 6-digit code is brute-forceable within its 10-minute window without it — and independent of the opt-in sign-in lockout.

When codeDelivery is not provided:

  • Signup is immediate (no confirmation step).
  • Password reset is not available (the action does not appear in the state machine).

Error Handling

import { isBlocksError } from '@aws-blocks/core';
import { AuthBasicErrors } from '@aws-blocks/bb-auth-basic';

try {
  await auth.signIn('alice', 'wrong', context);
} catch (e) {
  if (isBlocksError(e, AuthBasicErrors.InvalidCredentials)) {
    // bad username or password
  }
}
Error ConstantNameWhen
AuthBasicErrors.InvalidCredentialsInvalidCredentialsExceptionWrong username or password, or unconfirmed user
AuthBasicErrors.UserAlreadyExistsUserAlreadyExistsExceptionDuplicate username on sign-up
AuthBasicErrors.InvalidPasswordInvalidPasswordExceptionPassword doesn't meet policy
AuthBasicErrors.SessionExpiredSessionExpiredExceptionrequireAuth with no/expired session
AuthBasicErrors.SessionRevokedSessionRevokedExceptionrequireAuth with a token that was revoked at sign-out (still unexpired, but invalidated)
AuthBasicErrors.InvalidCodeInvalidCodeExceptionWrong or expired verification code (signup or reset)
AuthBasicErrors.SignOutIncompleteSignOutIncompleteExceptionsignOut cleared the local cookie but the server-side revocation write failed (e.g. a DynamoDB throttle); status 503 — retry to revoke the token
AuthBasicErrors.AccountLockedAccountLockedExceptionsignIn rejected because the account is locked after too many failed attempts (only when lockout is enabled); status 429 — try again after the lockout window

Branching on the setAuthState client path

isBlocksError works on a thrown error. The recommended client path (createApi() → setAuthState()) does not throw — it returns an AuthState whose errorName carries the same structured name. Use hasAuthError to branch on the returned state, e.g. to fall back to sign-up for a brand-new user:

import { hasAuthError } from '@aws-blocks/core';
import { AuthBasicErrors } from '@aws-blocks/bb-auth-basic';

let next = await authApi.setAuthState({ action: 'signIn', username, password });
if (hasAuthError(next, AuthBasicErrors.InvalidCredentials)) {
  // unknown or wrong-credential user → offer sign-up instead
  next = await authApi.setAuthState({ action: 'signUp', username, password });
}

Rule of thumb: throw path → isBlocksError; returned AuthState → hasAuthError. Never match on the human-facing error string.

UI Components

This package does not include UI components. Use the provider-agnostic Authenticator from @aws-blocks/auth-common/ui:

import { Authenticator } from '@aws-blocks/auth-common/ui';
import { authApi } from 'aws-blocks';

document.body.appendChild(Authenticator(authApi));

See the auth-common README for full UI component documentation including AuthenticatedContent, onAuthChange, and broadcastAuthChange.

AuthBasicUser

Extends the common AuthUser with:

FieldTypeDescription
userIdstringSame as username
usernamestringThe username
createdAtstringISO 8601 creation timestamp

Re-exported types

These types from @aws-blocks/auth-common are re-exported by this package, so you can import them directly from @aws-blocks/bb-auth-basic without adding a dependency on auth-common:

import type {
  BlocksAuth, AuthUser, AuthState, AuthActionInput, AuthAction, AuthField,
} from '@aws-blocks/bb-auth-basic';
TypeKindShape / purpose
BlocksAuthinterfaceThe common server-side auth contract AuthBasic implements: requireAuth(context): Promise<AuthUser>, checkAuth(context): Promise<boolean>, getCurrentUser(context): Promise<AuthUser | null>.
AuthUserinterfaceBase user shape: { userId: string; username: string }. AuthBasicUser extends it (adds createdAt).
AuthStateinterfaceState returned by getAuthState() / setAuthState(): { state, user?, actions, error?, errorName?, retriable? }, where state is 'signedOut' | 'signedIn' | 'confirmingSignUp' | 'confirmingSignIn' | 'confirmingMfa' | 'confirmingPasswordReset'.
AuthActioninterfaceOne action offered by a state: { name, label, fields: AuthField[], url?, method?: 'GET' | 'POST', capability?: 'webauthn-get' | 'webauthn-create' }.
AuthFieldinterfaceA form field to render: { name, label, type: 'text' | 'password' | 'email' | 'tel' | 'number' | 'hidden', required: boolean, defaultValue? }.
AuthActionInputtypeDiscriminated union (keyed on action) passed to setAuthState. The variants AuthBasic handles are signIn, signUp, confirmSignUp, signOut, resetPassword, and confirmResetPassword (see the action-input note under API).

Cookies and sessions

The session is an HttpOnly cookie holding a signed JWT. By default the cookie is SameSite=Lax (plus Secure off localhost), which is correct for same-origin apps and the local dev proxy — the common case.

When you serve the frontend through the Hosting block, CloudFront puts the static site and the API behind one origin (the API is a path on the same distribution), so the deployed app is same-origin and the SameSite=Lax default works without any configuration — leave crossDomain at its default false.

Set crossDomain: true only when the frontend and API are served from different registrable domains in production (e.g. frontend on Vercel, API on AWS). That switches the cookie to SameSite=None; Secure; Partitioned so it survives the cross-site request:

const auth = new AuthBasic(app, 'auth', { crossDomain: true });

On plain-HTTP localhost the BB drops Secure for the Lax default and drops Partitioned for the cross-domain recipe (CHIPS requires HTTPS).

Revocation: read cost and consistency

Server-side sign-out revocation (see signOut) adds one DynamoDB GetItem per authenticated request — checkAuth, getCurrentUser, requireAuth, and the getAuthState call that runs on page load each check the token's jti against a revocation list. On an auth-gated, high-traffic app this roughly doubles read volume to the underlying table versus pure stateless JWT validation. Tokens issued before revocation shipped (no jti) skip the read and simply expire within sessionDuration.

The revocation read is an eventually-consistent GetItem (no ConsistentRead), so revocation is near-immediate, not instantaneous: for a sub-second window after signOut, a token replayed against a different Lambda instance may still authenticate before the revocation marker propagates. A signed-out token stops working everywhere within that propagation window, and always expires at its own exp regardless.

Sign-in throttling

Brute-force and credential-stuffing attacks hammer signIn with password guesses. Enable the lockout policy to lock an account after too many consecutive failures — parity with the Cognito-managed lockout you'd get from AuthCognito:

// Lock an account for 15 minutes after 5 consecutive failed sign-ins (defaults)
const auth = new AuthBasic(app, 'auth', { lockout: {} });

// Stricter: 3 attempts, 30-minute lock
const auth = new AuthBasic(app, 'auth', {
  lockout: { maxAttempts: 3, lockoutSeconds: 1800 },
});
FieldDefaultMeaning
maxAttempts5Consecutive failed sign-ins that trip the lock.
windowSeconds900 (15 min)Fixed window, measured from the first failure, over which failures are counted — once it elapses the next failure resets the running count, so slow, spaced-out guesses don't accumulate.
lockoutSeconds900 (15 min)How long the account stays locked once tripped. The lock self-expires (no manual unlock).

While locked, signIn throws AuthBasicErrors.AccountLocked (AccountLockedException, HTTP 429) — before the password is checked, so a locked account sheds the bcrypt cost under attack and the decision carries no password-dependent timing signal. A successful sign-in clears the counter. Counting is per-username (the credential-stuffing target); it does not lock by IP, so a shared NAT isn't a false-positive risk.

Throttling is opt-in — with no lockout option, sign-in is unlimited and behaves exactly as before. Counters live in the same TTL-backed store as verification codes and revocation markers, so they self-reap and never grow unbounded.

The counter is a best-effort read-modify-write, not an atomic increment, so highly concurrent failures for the same username (across many Lambda instances) can lose an increment and make the lock trip slightly later than maxAttempts suggests. The lockout window is measured from the moment the count first reaches maxAttempts, so a lock always holds for the full lockoutSeconds once tripped.

Local development: the mock KVStore is in-memory, so lockout counters (like all AuthBasic state) reset when the dev server restarts.

版本与发布记录

@aws-blocks/bb-auth-basic 共发布 16 个版本,首次发布于 2026/06/16;过去 12 个月发布了 16 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.2.0latest2026/11/025 天前
0.1.112026/10/1622 天前
0.0.0-canary.esql-canary.c93c81b.1791235863canary-esql-canary2026/10/061 个月前
0.1.102026/10/051 个月前
0.0.0-canary.esql-canary.f7f39ac.17909891272026/10/031 个月前
0.0.0-canary.ci-publish-canary-npm.f53dfdc.1790988617canary-ci-publish-canary-npm2026/10/031 个月前

关于 @aws-blocks/bb-auth-basic 的常见问题

@aws-blocks/bb-auth-basic 是什么?

Simple username/password authentication with JWT sessions, password policy, and optional code-confirmed signup and password reset.

如何安装 @aws-blocks/bb-auth-basic?

在项目目录运行 npm install @aws-blocks/bb-auth-basic,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:constructs(^10.6.0)、aws-cdk-lib(^2.257.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@aws-blocks/bb-auth-basic 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@aws-blocks/bb-auth-basic 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@aws-blocks/bb-auth-basic 可以商用吗?

@aws-blocks/bb-auth-basic 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@aws-blocks/bb-auth-basic 的下载量有多少?

根据 npm 官方下载统计,@aws-blocks/bb-auth-basic 在 2026/10/07 – 2026/11/05 这 30 天内被下载 63,948 次,日均约 2,132 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@aws-blocks/bb-auth-basic 还在维护吗?

最新版本 0.2.0 发布于 2026/11/02(5 天前);至今共发布 16 个版本,其中过去 12 个月发布了 16 个;首次发布于 2026/06/16。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。