better-auth-cognito-native
v0.2.1
Published
Better Auth plugin for AWS Cognito native auth — email/password, passkey (WebAuthn), and password management flows
Readme
better-auth-cognito-native
A Better Auth plugin for AWS Cognito native auth — email/password, passkey (WebAuthn), and password management flows.
Features
- Email/password sign-up and sign-in via Cognito USER_PASSWORD_AUTH
- Email confirmation and resend confirmation code
- Forgot password and confirm password reset flows
- Force new password challenge (NEW_PASSWORD_REQUIRED)
- Passkey (WebAuthn) sign-in and registration via Cognito native WebAuthn
- User attribute get/update
- Global sign-out (invalidates Cognito refresh token)
- Passkey configuration and password policy helpers
- Client-side password validation against the Cognito policy
Requirements
- Node.js 18+
better-auth>= 1.5.0jose>= 5.0.0zod>= 4.0.0- AWS Cognito User Pool with USER_PASSWORD_AUTH (and optionally ALLOW_USER_AUTH for passkeys) enabled on the app client
Installation
pnpm add better-auth-cognito-nativeSetup
Server
// lib/auth.ts
import { betterAuth } from "better-auth";
import { cognitoPlugin } from "better-auth-cognito-native";
export const auth = betterAuth({
plugins: [
cognitoPlugin({
region: process.env.COGNITO_REGION!,
userPoolId: process.env.COGNITO_USER_POOL_ID!,
clientId: process.env.COGNITO_APP_CLIENT_ID!,
}),
],
});Client
// lib/auth-client.ts
import { createAuthClient } from "better-auth/react";
import { cognitoClientPlugin } from "better-auth-cognito-native/client";
export const authClient = createAuthClient({
plugins: [cognitoClientPlugin()],
});API endpoints
All endpoints are mounted under /api/auth (or your configured Better Auth base path) with the prefix /cognito.
| Method | Path | Description |
|--------|------|-------------|
| POST | /cognito/sign-up | Register a new user |
| POST | /cognito/confirm-sign-up | Confirm email with verification code |
| POST | /cognito/resend-confirmation | Resend confirmation code |
| POST | /cognito/sign-in | Sign in with email and password |
| POST | /cognito/new-password | Complete NEW_PASSWORD_REQUIRED challenge |
| POST | /cognito/forgot-password | Initiate password reset |
| POST | /cognito/confirm-forgot-password | Complete password reset with code |
| POST | /cognito/sign-out | Sign out and invalidate session |
| GET | /cognito/tokens | Get raw Cognito tokens for the current session |
| GET | /cognito/user-attributes | Get Cognito user attributes |
| POST | /cognito/user-attributes | Update Cognito user attributes |
| POST | /cognito/passkey/sign-in/start | Start passkey sign-in ceremony |
| POST | /cognito/passkey/sign-in/complete | Complete passkey sign-in ceremony |
| POST | /cognito/passkey/register/start | Start passkey registration ceremony |
| POST | /cognito/passkey/register/complete | Complete passkey registration ceremony |
The core plugin uses only public Cognito APIs (
InitiateAuth,SignUp,ConfirmSignUp, etc.) which are authorized by the app client ID — no IAM permissions required.
Client usage
The client plugin exposes typed actions via the auth client:
// Sign up
await authClient.cognito.signUp({ email, password });
// Confirm email
await authClient.cognito.confirmSignUp({ email, code });
// Sign in
await authClient.cognito.signIn({ email, password });
// Forgot password
await authClient.cognito.forgotPassword({ email });
await authClient.cognito.confirmForgotPassword({ email, code, newPassword });
// Sign out
await authClient.cognito.signOut();
// User attributes
const { data } = await authClient.cognito.userAttributes.get();
await authClient.cognito.userAttributes.update({ attributes: [{ Name: "name", Value: "Alice" }] });
// Passkeys (browser only)
const result = await authClient.cognito.signInWithPasskey(email);
const result = await authClient.cognito.registerPasskey();Passkeys (WebAuthn)
Passkey support requires the Cognito User Pool to have WebAuthn configured and ALLOW_USER_AUTH enabled on the app client.
The standalone browser helpers signInWithPasskey and registerPasskey from better-auth-cognito-native/client can also be used independently of the auth client plugin.
Config helpers
The better-auth-cognito-native/config export provides server-side helpers for reading Cognito pool configuration. These are optional utility functions — use them at startup to drive application behavior (e.g. whether to show the passkey UI, or enforce the pool's password rules on the client).
Unlike the core plugin, these helpers call admin-level Cognito APIs and require IAM permissions on the execution role.
getPasskeyConfig
Checks whether WebAuthn is configured on the User Pool and whether ALLOW_USER_AUTH is enabled on the app client.
import { getPasskeyConfig } from "better-auth-cognito-native/config";
const { mode } = await getPasskeyConfig({
region: process.env.COGNITO_REGION!,
userPoolId: process.env.COGNITO_USER_POOL_ID!,
clientId: process.env.COGNITO_APP_CLIENT_ID!,
});
// mode: "optional" — WebAuthn configured, passkey offered but not enforced
// mode: "disabled" — WebAuthn not configured or ALLOW_USER_AUTH not enabledRequired IAM permissions:
{
"Effect": "Allow",
"Action": [
"cognito-idp:GetUserPoolMfaConfig",
"cognito-idp:DescribeUserPoolClient"
],
"Resource": "arn:aws:cognito-idp:<region>:<account>:userpool/<user-pool-id>"
}Falls back to { mode: "optional" } on any error (missing permissions, network, etc.) so the passkey UI is shown rather than silently hidden.
getPasswordPolicy
Fetches the password policy configured on the User Pool.
import { getPasswordPolicy, validatePassword } from "better-auth-cognito-native/config";
const policy = await getPasswordPolicy(
{ region, userPoolId, clientId },
// optional fallback if the call fails:
{ MinimumLength: 8, RequireUppercase: true },
);
// Validate client-side before submitting a form
const { valid, errors } = validatePassword(password, policy);Required IAM permissions:
{
"Effect": "Allow",
"Action": "cognito-idp:DescribeUserPool",
"Resource": "arn:aws:cognito-idp:<region>:<account>:userpool/<user-pool-id>"
}If no fallback is provided and the call fails, getPasswordPolicy throws so the caller can decide how to handle it. validatePassword runs entirely client-side with no network call.
Exports
| Import path | Contents |
|-------------|----------|
| better-auth-cognito-native | cognitoPlugin, cognitoGlobalSignOut, COGNITO_ERROR_CODES, CognitoPluginOptions |
| better-auth-cognito-native/client | cognitoClientPlugin, signInWithPasskey, registerPasskey |
| better-auth-cognito-native/config | getPasskeyConfig, getPasswordPolicy, validatePassword, PasswordPolicyType |
License
MIT
