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
15 changes: 15 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"snapshot": {
"useCalculatedVersion": true,
"prereleaseTemplate": "{tag}.{datetime}"
},
"ignore": []
}
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ ORIGINS=http://localhost:5173,http://localhost:5174
# 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=[]
# Dedicated signing secret for OAuth state. If unset, production falls back to API_SERVICE_TOKEN.
OAUTH_STATE_SECRET=

# ADMIN BOOTSTRAP
SEAMLESS_BOOTSTRAP_ENABLED=true
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ jobs:
run: npm run typecheck
- name: Build
run: npm run build

- name: Verify package contents
run: npm run check-npm-build
68 changes: 68 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Release

on:
push:
branches:
- main
workflow_dispatch:

permissions:
contents: write
issues: write
pull-requests: write
id-token: write

concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

jobs:
release:
if: github.repository == 'fells-code/seamless-auth-api'
name: Version or Publish Package
runs-on: ubuntu-latest
env:
HUSKY: 0

steps:
- name: Checkout repo
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24.10.0
cache: npm
registry-url: https://registry.npmjs.org/

- name: Install dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Test
run: npm run test:run

- name: Typecheck
run: npm run typecheck

- name: Build
run: npm run build

- name: Verify package contents
run: npm run check-npm-build

- name: Version or publish package
uses: changesets/action@v1
with:
version: npm run version-packages
publish: npm run release:stable
createGithubReleases: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true
10 changes: 9 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
tests
src
coverage
logs
keys
.github
.husky
.env
.env.*
!.env.example
vitest.config.ts
tsconfig.json
tsconfig.json
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# seamless-auth-api

## 0.2.0

- Current release baseline before Changesets-managed releases.
100 changes: 81 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ The following are **intentionally out of scope** and are part of the managed Sea
- Hosted metrics, analytics, or usage dashboards
- Managed secrets storage or key rotation services
- Automated upgrades, backups, or restore tooling
- Email or SMS services to service the OTP requests
- Managed email or SMS services. The API can send directly through configured provider adapters or
return external-delivery payloads to a trusted server adapter, but SeamlessAuth Managed handles the
operated delivery service.
- Support SLAs or operational monitoring

Self-hosted users are free to implement any of the above on their own, but they are not required to use SeamlessAuth Managed Service.
Expand All @@ -61,7 +63,8 @@ This repository does **not** assume any specific cloud provider, billing system,
- Bearer/JSON auth API with opaque refresh tokens and signed access tokens
- WebAuthn / passkeys support
- Optional WebAuthn PRF support for products that need browser-local key material
- Token and JWKS support for service-to-service auth
- JWKS publication for access-token verification and separate service-token guards for trusted
server adapters
- Built for inspection, auditability, and self-hosting

This repository contains **only the auth server**. The admin portal, billing system, and hosted control plane are proprietary and offered as a managed service.
Expand All @@ -82,9 +85,21 @@ If you want hosted auth with a full control plane and operational support, use t
- SeamlessAuth server SDK (recommended)
- Direct HTTP APIs (advanced)

## Bearer Token Contract

Seamless Auth API returns JSON tokens instead of browser auth cookies.

- Pre-auth flows return an ephemeral `token`; send it as `Authorization: Bearer <token>` to routes
marked as ephemeral-authenticated, such as OTP, magic-link, and WebAuthn continuation routes.
- Completed login, registration, OAuth, TOTP, passkey, and refresh flows return an access `token`;
send it as `Authorization: Bearer <token>` to access-authenticated routes.
- Refresh uses the opaque `refreshToken` value, not the access token.
- Internal service tokens remain separate. They are used only by explicitly service-token-protected
paths or headers such as external delivery support, not as user access or ephemeral bearer tokens.

---

## Local development quickstart
## Local Development Quickstart

### Prerequisites

Expand All @@ -93,10 +108,39 @@ If you want hosted auth with a full control plane and operational support, use t

### Configuration

Copy the `.env.example` to an `.env` file and populate empty values.
Copy the `.env.example` to an `.env` file and populate values for your local environment.

Never commit real secrets. Use `.env.example` for documentation.

For a default local Postgres instance, `.env.example` expects:

```text
DB_HOST=localhost
DB_PORT=5432
DB_NAME=seamless_auth
DB_USER=myuser
DB_PASSWORD=mypassword
```

### Run Locally

```bash
npm install
npm run migrate:up
npm run dev
```

The server starts on `http://localhost:5312` by default.

Verify it:

```bash
curl http://localhost:5312/health
```

In development, OpenAPI is available at `http://localhost:5312/openapi.json` and Swagger UI is
available at `http://localhost:5312/docs`.

### WebAuthn PRF

SeamlessAuth can request PRF-capable passkeys and PRF assertions without ever receiving PRF output.
Expand Down Expand Up @@ -239,6 +283,10 @@ external delivery also requires a valid `x-seamless-service-token` from a truste
Development-only bootstrap token details require `x-seamless-auth-include-sensitive: true` and are
never enabled in production.

Admin and user endpoints use explicit minimized response schemas. They do not return WebAuthn
public keys, refresh-token hashes/lookups, challenge context, verification tokens, PRF output, TOTP
secrets, or provider tokens.

### Scoped Roles

Global roles may be plain names such as `admin` or scoped names such as `admin:read` and
Expand All @@ -255,18 +303,9 @@ Admin routes are split by intent:
- write routes accept `admin` or `admin:write`
- plain `admin` checks remain exact for backwards compatibility

### Install & run

```
npm install
npm run dev
```

The server should start on `http://localhost:5312` (or your configured port).

---

# Docker Quickstart (5 minutes)
# Docker Quickstart

This is the fastest way to run **Seamless Auth API** locally using Docker.

Expand Down Expand Up @@ -310,22 +349,40 @@ cp .env.example .env
If you do not already have Postgres running:

```bash
docker network create seamless-auth-local

docker run -d \
--name seamless-auth-postgres \
--network seamless-auth-local \
-e POSTGRES_USER=myuser \
-e POSTGRES_PASSWORD=mypassword \
-e POSTGRES_DB=seamless_auth \
-p 5432:5432 \
postgres:16
```

Update DB env values accordingly.
The one-off migration command and API container below override `DB_HOST` to the Docker network name.

## 4. Run database migrations

Run migrations before the first boot and after upgrades that include migrations:

```bash
docker run --rm \
--env-file .env \
--network seamless-auth-local \
-e DB_HOST=seamless-auth-postgres \
ghcr.io/fells-code/seamless-auth-api:latest \
npm run migrate:up
```

## 4. Run Seamless Auth Server
## 5. Run Seamless Auth Server

```bash
docker run --rm \
--env-file .env \
--network seamless-auth-local \
-e DB_HOST=seamless-auth-postgres \
-p 5312:5312 \
ghcr.io/fells-code/seamless-auth-api:latest
```
Expand All @@ -336,7 +393,7 @@ The server will:
- Start on port `5312`
- Expose health and authentication endpoints

## 5. Verify it is running
## 6. Verify it is running

```bash
curl http://localhost:5312/health
Expand All @@ -347,7 +404,8 @@ You should receive a healthy response.
## Notes for self-hosting

- Secrets are provided via environment variables
- Keys are generated or mounted at runtime as needed
- Development keys can be generated automatically; production signing keys should be generated,
rotated, and mounted or provided through environment-backed secret management
- This image contains only the open-source authentication server
- No admin portal, billing, or managed infrastructure is included

Expand All @@ -358,6 +416,7 @@ For production deployments:
- Rotate signing keys
- Back up your database
- Monitor authentication failures
- Treat `system_config` values as runtime configuration, not a secret store

See [docs/production-operations.md](./docs/production-operations.md) for key, secret, rotation,
lockout, and deployment guidance.
Expand All @@ -373,11 +432,14 @@ Authentication infrastructure is security-sensitive.
For production deployments:

- Use HTTPS end-to-end
- Keep access and refresh tokens out of browser-readable storage
- Keep access and refresh tokens out of browser-readable storage. This API does not set or read
browser auth cookies; browser-facing apps should integrate through a trusted server adapter or
backend.
- Restrict CORS origins
- Rotate signing keys and secrets regularly
- Enable database backups and test restores
- Monitor auth failures and suspicious behavior
- Treat `system_config` values as runtime configuration, not a secret store

## Contributing

Expand Down
18 changes: 16 additions & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Seamless Auth API is an Express and TypeScript authentication service backed by
## Runtime Components

- Express app: global middleware, CORS, rate limits, OpenAPI metadata, and route loading.
- Routes: thin endpoint declarations with request/response schemas.
- Routes: thin endpoint declarations with request and response schemas.
- Controllers: request handling and response shaping.
- Services: reusable auth, session, messaging, organization, OAuth, lockout, redaction, and step-up logic.
- Models: Sequelize models for users, credentials, sessions, system config, auth events, organizations, TOTP credentials, and OAuth identities.
Expand All @@ -17,7 +17,7 @@ Seamless Auth API is an Express and TypeScript authentication service backed by
2. Route declarations validate params, query, and body schemas.
3. Auth middleware validates access or ephemeral tokens where required.
4. Controllers call service/model layers.
5. Response schemas validate JSON responses when configured.
5. Response schemas validate JSON responses and strip undocumented fields from successful payloads.
6. Auth events are logged with sensitive metadata redacted.

## Token Model
Expand All @@ -30,6 +30,14 @@ Seamless Auth API uses three token states:

Access tokens are signed with configured JWKS signing keys. Refresh tokens are rotated and stored in the `sessions` table as non-raw values.

Routes that declare access or ephemeral auth validate SeamlessAuth-issued bearer JWTs. Internal
service tokens are intentionally separate and are accepted only by service-token-specific middleware
or headers.

The API does not set or read browser auth cookies. Browser-facing applications should keep token
custody in a trusted server adapter or backend and forward bearer tokens to the API from that trusted
layer.

## Authentication Methods

Supported login methods are controlled by `login_methods` system config:
Expand All @@ -48,6 +56,12 @@ Runtime configuration lives in the `system_config` table and is bootstrapped fro

Use environment variables for raw secrets. Do not store raw secrets in `system_config`.

## OpenAPI and Response Contracts

Route modules use the `schemas` option so request validation, runtime response validation, and
OpenAPI generation stay aligned. Every route should declare an explicit response schema. Admin/user
responses are intentionally minimized and should return only fields the route contract names.

## Operational Boundaries

This repository contains the auth server only. It does not include billing, hosted tenant lifecycle, managed observability, managed secret storage, or the hosted control plane. Self-hosted deployments can integrate their own infrastructure for those responsibilities.
5 changes: 5 additions & 0 deletions docs/oauth.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Provider access tokens are used only during callback handling to fetch profile d
1. Add `oauth` to `LOGIN_METHODS`.
2. Configure one or more providers in `oauth_providers` system config or the `OAUTH_PROVIDERS` environment variable.
3. Store provider client secrets in environment variables referenced by `clientSecretEnv`.
4. Set `OAUTH_STATE_SECRET` in production, or ensure `API_SERVICE_TOKEN` is stable and secret so it
can be used as the OAuth state-signing fallback.

Example provider:

Expand Down Expand Up @@ -62,3 +64,6 @@ Use `requireEmailVerified: true` for providers that expose a reliable email veri
## OIDC Notes

When provider scopes include `openid`, Seamless Auth includes a nonce bound into signed state. PKCE support depends on provider and client flow requirements; keep provider callback handling server-side and avoid exposing provider tokens to browsers.

OAuth callback responses return the normal Seamless Auth JSON token payload. Provider tokens remain
server-side and must not be forwarded to browser clients.
Loading
Loading