Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ DEFAULT_ROLES=user,betaUser
AVAILABLE_ROLES=user,admin,betaUser,team

# Login methods administrators allow after /login creates a pre-auth session.
# Supported values: passkey,magic_link,email_otp,phone_otp
# Supported values: passkey,magic_link,email_otp,phone_otp,oauth
LOGIN_METHODS=passkey,magic_link
# When true, magic_link/email_otp/phone_otp can appear alongside passkey when allowed.
# When false, passkey-capable sessions continue with passkey only.
Expand All @@ -42,6 +42,9 @@ ACCESS_TOKEN_TTL=30m
REFRESH_TOKEN_TTL=1h
RATE_LIMIT=100
DELAY_AFTER=50
# JSON. Failed login attempts for an identified user inside windowSeconds lock the account
# for lockoutSeconds. Set enabled=false only when an upstream policy handles this.
LOCKOUT_POLICY={"enabled":true,"maxFailures":10,"windowSeconds":900,"lockoutSeconds":900}

# SERVICE TOKENS
# Required for trusted server adapters and internal bearer validation.
Expand All @@ -50,11 +53,19 @@ API_SERVICE_TOKEN=32-byte-hex-string
# If unset, the server falls back to API_SERVICE_TOKEN, and in development only
# it will use a derived local secret.
REFRESH_TOKEN_LOOKUP_SECRET=
# 32-byte base64url/base64/hex key for encrypted TOTP secrets.
# If unset, the API falls back to API_SERVICE_TOKEN, and production requires one of these values.
TOTP_SECRET_ENCRYPTION_KEY=

# WEBAUTHN
RPID=localhost
ORIGINS=http://localhost:5173,http://localhost:5174

# OAUTH
# JSON array of provider config. Provider client secrets stay in env vars referenced by
# clientSecretEnv, never in system_config or this JSON value.
OAUTH_PROVIDERS=[]

# ADMIN BOOTSTRAP
SEAMLESS_BOOTSTRAP_ENABLED=true
SEAMLESS_BOOTSTRAP_SECRET=dev-bootstrap-secret-123
Expand Down
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,9 @@ docker-data/

# Lint / tooling cache
.eslintcache
docs/
docs/*
!docs/admin-operations.md
!docs/architecture.md
!docs/oauth.md
!docs/production-operations.md
!docs/webauthn-prf.md
59 changes: 52 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Never commit real secrets. Use `.env.example` for documentation.

SeamlessAuth can request PRF-capable passkeys and PRF assertions without ever receiving PRF output.
See [docs/webauthn-prf.md](./docs/webauthn-prf.md) for API usage, browser limitations, SDK
contract guidance, and Seamless Secrets consumption rules.
contract guidance, and local key-material handling rules.

### Login Method Policy

Expand Down Expand Up @@ -139,10 +139,14 @@ system config.
"userInfoUrl": "https://openidconnect.googleapis.com/v1/userinfo",
"scopes": ["openid", "email", "profile"],
"redirectUri": "https://app.example.com/oauth/callback",
"redirectUris": ["https://app.example.com/oauth/callback"],
"subjectJsonPath": "sub",
"emailJsonPath": "email",
"emailVerifiedJsonPath": "email_verified",
"nameJsonPath": "name",
"allowSignup": true
"allowSignup": true,
"accountLinking": "email",
"requireEmailVerified": true
}
]
```
Expand Down Expand Up @@ -182,11 +186,45 @@ curl -X POST http://localhost:5312/oauth/google/callback \
Security notes:

- OAuth `state` is signed and expires after a short window.
- `redirectUri` and `returnTo` must match configured `origins`.
- `redirectUri` must exactly match a provider `redirectUris` entry when configured. Providers
without a redirect allowlist fall back to trusted configured origins.
- `returnTo` must match configured `origins`.
- OIDC providers that request the `openid` scope receive a nonce bound into the signed state.
- Provider access tokens are never persisted.
- OAuth identities are stored as provider id + provider subject in `oauth_identities`.
- Existing users are linked by verified email; new users are created only when `allowSignup` is
enabled for that provider.
- Existing users are linked by email only when `accountLinking` is `email`; set
`accountLinking: "disabled"` to require an existing provider identity.
- New users are created only when `allowSignup` is enabled for that provider.
- Set `requireEmailVerified: true` for providers that expose a reliable email verification claim.

### Lockout Policy

`LOCKOUT_POLICY` configures account lockout for identified users after repeated failed login
attempts. The value is JSON and is also manageable through `system_config`:

```json
{
"enabled": true,
"maxFailures": 10,
"windowSeconds": 900,
"lockoutSeconds": 900
}
```

Lockout is checked after Seamless Auth has identified the target user. Keep route-level rate limits
enabled for unknown identifiers, OTP delivery abuse, and broad IP pressure.

### Admin-Assisted Device Replacement

Administrators with write access can prepare an account for device replacement with:

```http
POST /admin/users/:userId/recovery/device-replacement
```

The endpoint requires a fresh step-up session and can revoke active sessions, remove registered
passkeys, and disable enabled TOTP credentials. It returns counts only; it never returns secrets,
credential private material, TOTP secrets, or recovery codes.

### Sensitive Data Redaction

Expand Down Expand Up @@ -321,6 +359,9 @@ For production deployments:
- Back up your database
- Monitor authentication failures

See [docs/production-operations.md](./docs/production-operations.md) for key, secret, rotation,
lockout, and deployment guidance.

## Prefer not to self-host?

SeamlessAuth managed services provides a fully managed experience built on top of this same open-source core, including hosting, upgrades, dashboards, backups, and SLAs.
Expand All @@ -342,10 +383,14 @@ For production deployments:

See [CONTRIBUTING.md](./CONTRIBUTING.md).

## Maintainer Docs
## Public Docs

- [AGENTS.md](./AGENTS.md) for a fast codebase briefing aimed at coding agents and maintainers
- [docs/architecture.md](./docs/architecture.md) for a deeper walkthrough of runtime flow, auth modes, config, and testing
- [docs/architecture.md](./docs/architecture.md) for runtime structure and request flow
- [docs/oauth.md](./docs/oauth.md) for OAuth provider setup and security behavior
- [docs/webauthn-prf.md](./docs/webauthn-prf.md) for PRF-capable passkey usage
- [docs/admin-operations.md](./docs/admin-operations.md) for scoped admin and recovery operations
- [docs/production-operations.md](./docs/production-operations.md) for production deployment guidance

## Security

Expand Down
64 changes: 64 additions & 0 deletions docs/admin-operations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Admin Operations

Seamless Auth API includes administrative endpoints for self-hosted operators. Admin access is controlled by scoped roles and should be used from a trusted operator surface.

## Scoped Admin Roles

Admin routes are split by intent:

- Read routes accept `admin`, `admin:read`, or `admin:write`.
- Write routes accept `admin` or `admin:write`.
- `admin:write` satisfies `admin:read`.
- `admin:read` does not satisfy write checks.

The legacy `admin` role remains broad for backwards compatibility.

## Device Replacement Recovery

Administrators with write access can prepare an account for device replacement:

```http
POST /admin/users/:userId/recovery/device-replacement
```

The endpoint requires a fresh step-up session. By default it:

- revokes active sessions
- removes passkeys
- disables enabled TOTP credentials

The response returns counts only:

```json
{
"userId": "user-id",
"revokedSessions": 2,
"removedCredentials": 1,
"disabledTotpCredentials": 1
}
```

It does not return credential private material, TOTP secrets, recovery codes, refresh tokens, or PRF output.

## Session Hygiene

Administrative session endpoints can list sessions and revoke individual or all sessions for a user. Use these endpoints when responding to suspicious account activity or user-requested device cleanup.

## Lockout Policy

`lockout_policy` controls account lockout for identified users after repeated failed login attempts:

```json
{
"enabled": true,
"maxFailures": 10,
"windowSeconds": 900,
"lockoutSeconds": 900
}
```

Lockout is checked after a user has been identified. Keep route-level and destination-aware limits enabled for unknown identifiers and delivery abuse.

## Audit Events

Admin actions are recorded as auth events with redacted metadata. Do not store raw secrets, tokens, OTPs, magic-link URLs, PRF values, account keys, or provider tokens in admin metadata.
Loading
Loading