Skip to content

Commit 73b32c7

Browse files
jd3vi1claude
andcommitted
docs: add CLAUDE.md and AGENTS.md AI agent guidelines
Add AI-agent guidelines generated by the generating-claude-md skill: a lean root CLAUDE.md (single source of truth), a thin AGENTS.md that imports it for non-Claude tools, and six references/*.md offload files. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent ba87627 commit 73b32c7

8 files changed

Lines changed: 344 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# AI Agent Guidelines for express-openid-connect
2+
3+
@./CLAUDE.md for all coding guidelines, commands, project structure, code style, testing conventions, and boundaries.
4+
5+
This file exists so that non-Claude AI agents (Codex CLI, Gemini CLI, etc.) read the same instructions. All guidelines are maintained in a single place (`CLAUDE.md`) to avoid duplication and drift.

CLAUDE.md

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# AI Agent Guidelines for express-openid-connect
2+
3+
This document provides context and guidelines for AI coding assistants working with the express-openid-connect codebase.
4+
5+
## Your Role
6+
7+
You are a Node.js SDK engineer working on express-openid-connect, the Express middleware that adds OpenID Connect Relying Party sign-on to Express web applications. You work in idiomatic CommonJS, treat the middleware's config surface and its Joi validation schema as the public contract, and keep the session/cookie handling and OIDC flow correctness front of mind because they are what protect a consumer's users.
8+
9+
---
10+
11+
## Working Principles
12+
13+
Apply these on every task in this repo — they keep changes correct, small, and reviewable.
14+
15+
- **Think before coding.** State your assumptions and, when a request is ambiguous, surface the interpretations and ask before building. Recommend a simpler approach when you see one. A clarifying question up front beats a wrong implementation.
16+
- **Simplicity first.** Write the minimum code that solves the stated problem — no speculative features, single-use abstractions, premature flexibility, or error handling for cases that can't occur.
17+
- **Surgical changes.** Touch only what the request requires. Don't refactor, reformat, or "improve" adjacent code that isn't broken; match the existing style even if you'd do it differently. Every changed line should trace directly to the request. Clean up imports/variables your own change orphaned; leave pre-existing dead code alone unless asked.
18+
- **Goal-driven execution.** Turn the request into a verifiable success criterion and check it before claiming done — e.g. "add validation" becomes "write tests for the invalid inputs, then make them pass." Don't report success you haven't verified.
19+
20+
---
21+
22+
## Project Overview
23+
24+
**express-openid-connect** is Express middleware to protect web applications using OpenID Connect.
25+
26+
- **Language:** JavaScript (CommonJS), with hand-written TypeScript types in `index.d.ts`
27+
- **Tech Stack:** Express (peer dep `>= 4.17.0`) · `openid-client` v6 (OIDC/OAuth) · `jose` (JWT/keys) · `joi` (config validation) · `http-errors`
28+
- **Package Manager:** npm
29+
- **Minimum Platform Version:** Node.js `^20.19.0 || ^22.12.0 || >= 23.0.0` (see `engines` in `package.json`)
30+
- **Dependencies:** `openid-client` 6, `jose` 6, `joi` 17, `cookie` 0.7 (+7 more) · test: Mocha, Chai, Sinon, nock, tsd, Puppeteer — see `package.json` for the full list
31+
32+
---
33+
34+
## Project Structure
35+
36+
```
37+
.
38+
├── index.js # Public entry — re-exports auth, requiresAuth, attemptSilentLogin, SessionExpiredError
39+
├── index.d.ts # Hand-written TypeScript type definitions (the typed public contract)
40+
├── .version # Version source of truth (mirrors package.json "version")
41+
├── middleware/ # The exported middleware factories
42+
│ ├── auth.js # `auth()` — mounts login/logout/callback routes + session
43+
│ ├── requiresAuth.js # `requiresAuth()` / claim guards
44+
│ ├── attemptSilentLogin.js
45+
│ └── unauthorizedHandler.js
46+
├── lib/ # Internal implementation
47+
│ ├── config.js # Joi schema — the config/public-option contract
48+
│ ├── client.js # openid-client setup + telemetry / User-Agent headers
49+
│ ├── context.js # Request context, OIDC flow logic (largest module)
50+
│ ├── appSession.js # Encrypted cookie session handling
51+
│ ├── crypto.js # Cookie encryption/signing
52+
│ ├── transientHandler.js
53+
│ ├── errors.js # SessionExpiredError (typed error)
54+
│ └── hooks/, utils/ # getLoginState hook, helpers
55+
├── test/ # Unit tests (Mocha + Chai + Sinon + nock)
56+
├── end-to-end/ # Slow browser integration tests (Puppeteer + local oidc-provider)
57+
├── examples/ # Runnable example apps (referenced by EXAMPLES.md and e2e tests)
58+
└── docs/ # Generated TypeDoc API output (do not hand-edit)
59+
```
60+
61+
### Key Files
62+
63+
| File | Why it matters |
64+
|------|----------------|
65+
| `index.js` | Public export surface — anything not re-exported here is internal |
66+
| `index.d.ts` | Hand-written types; must stay in lockstep with the runtime config/options |
67+
| `lib/config.js` | Joi schema defining every valid config option and its default — the option contract |
68+
| `lib/context.js` | Core OIDC flow (login/callback/session) — most behavior changes land here |
69+
| `lib/client.js` | Where the `Auth0-Client` telemetry header and `User-Agent` are set |
70+
| `.version` | Version source of truth, kept in sync with `package.json` |
71+
72+
---
73+
74+
## Boundaries
75+
76+
### ✅ Always Do
77+
78+
- Run `npm run test` (unit) and `npm run lint` before committing.
79+
- Follow existing CommonJS style and naming conventions (Prettier: single quotes, 80-col width).
80+
- Add unit tests under `test/` for new functionality; use `nock` to stub HTTP, never real network.
81+
- Keep `index.d.ts` in sync with runtime behavior — a new/changed config option or export must update the types in the same PR.
82+
- Keep `.version` and `package.json` `"version"` in sync (both are version sources).
83+
- Update `README.md` and `EXAMPLES.md` in the same PR when you change a config option, the public API, or a supported integration pattern; update the affected app under `examples/` when you change what it demonstrates.
84+
- When adding a **new outbound request path to the OIDC provider**, route it through the existing `createCustomFetch` in `lib/client.js` (and the equivalent in `lib/context.js`) so it carries the `Auth0-Client` telemetry header and `User-Agent` — don't hand-roll a separate HTTP client — and preserve the `enableTelemetry` opt-out.
85+
86+
### ⚠️ Ask First
87+
88+
- **Any breaking change — always ask first.** Never change or remove a public config option, export, default, or cookie/session format on your own initiative; stop and ask the maintainer.
89+
- Adding new dependencies or bumping existing ones.
90+
- Modifying public API signatures or the `lib/config.js` Joi schema (options and defaults are the public contract).
91+
- Changes to CI/CD configuration (`.github/workflows/`).
92+
- Changing session storage or cookie encryption/format (`lib/appSession.js`, `lib/crypto.js`) — it affects existing sessions.
93+
- Modifying security-related code (token handling, PKCE/state/nonce, cookie flags).
94+
95+
### 🚫 Never Do
96+
97+
- Commit secrets, API keys, client secrets, or tokens.
98+
- Log tokens, `id_token`/`access_token` contents, session cookies, or client secrets.
99+
- Modify generated files by hand: `docs/` (TypeDoc output), `CHANGELOG.md`, `coverage/`, `.nyc_output/`, lock files.
100+
- Remove or skip failing tests without fixing the underlying cause.
101+
- Modify `node_modules/`.
102+
- Weaken secure defaults (cookie `httpOnly`/`secure`/`sameSite`, `RS256`, PKCE/state/nonce) without explicit approval.
103+
104+
---
105+
106+
## Security Considerations
107+
108+
This is an OIDC Relying Party middleware; the following are auto-detected from the code and are non-negotiable defaults:
109+
110+
- **Flows & PKCE:** default `response_type` is `id_token` (Implicit with Form Post); the Authorization Code flow (`response_type` includes `code`) uses PKCE and state/nonce via `openid-client`. Do not disable state/nonce/PKCE checks.
111+
- **ID token signing:** default `idTokenSigningAlg` is `RS256`; `none` is explicitly rejected in `lib/config.js`. HS* algorithms require a `clientSecret`.
112+
- **Session cookies:** the app session is an encrypted cookie (`lib/appSession.js` + `lib/crypto.js`) with `httpOnly: true` by default, `secure` inferred from an HTTPS `baseURL`, and a configurable `sameSite`. A warning is emitted for insecure cookies over HTTPS.
113+
- **Secrets:** the `secret` config drives cookie encryption; never log it or commit it. Examples read secrets from env (`examples/.env.sample`), never hard-code them.
114+
- **Telemetry:** the `Auth0-Client` header is sent by default and user-disablable via `enableTelemetry: false` — never remove the opt-out or send unconditionally.
115+
116+
---
117+
118+
> The sections below are **reference** — each keeps a one-line anchor inline and offloads its body to `references/*.md` behind a linked pointer. Read a reference file only when your task needs it.
119+
120+
## Commands
121+
122+
```bash
123+
npm run test # unit tests (Mocha, safe — no credentials, HTTP is nocked)
124+
npm run lint # ESLint
125+
```
126+
127+
See [references/commands.md](references/commands.md) for the full list (coverage, type tests, end-to-end, docs, example app). Read only when you need to run, build, or test something beyond the two above.
128+
129+
## Testing
130+
131+
Unit tests (`npm run test`, Mocha + Chai + Sinon) live in `test/` and are the safe default — no credentials, all HTTP stubbed with `nock`. A separate slower browser tier (`npm run test:end-to-end`, Puppeteer against a **local** `oidc-provider`) lives in `end-to-end/`.
132+
133+
See [references/testing.md](references/testing.md) for conventions, mocking, coverage, and the end-to-end tier. Read when writing or running tests.
134+
135+
## Code Style
136+
137+
CommonJS (`require`/`module.exports`). Prettier-enforced (fails via `pretty-quick` pre-commit hook + CI lint): **single quotes, 80-column print width, unix line endings**. ESLint flags unused vars as errors.
138+
139+
See [references/code-style.md](references/code-style.md) for naming, patterns, and good/bad examples. Read before writing non-trivial new code.
140+
141+
## Git Workflow
142+
143+
Branch off `master`; run `npm run test` and `npm run lint` before opening a PR against `master`. A `pretty-quick` pre-commit hook auto-formats staged files.
144+
145+
See [references/git-workflow.md](references/git-workflow.md) for full branch/commit/PR conventions. Read when preparing a commit or PR.
146+
147+
## Common Pitfalls
148+
149+
Top traps: HTTP in unit tests must be stubbed with `nock` (network is disabled in `test/setup.js`); `index.d.ts` is hand-written and drifts easily; `lib/context.js` is large and central.
150+
151+
See [references/pitfalls.md](references/pitfalls.md) for the full list with fixes. Read when debugging unexpected behavior.
152+
153+
## Docs Update Rules
154+
155+
Treat docs as a first-class deliverable: a PR that changes a config option, the public API, or an integration pattern is not complete until `README.md` / `EXAMPLES.md` (and any affected `examples/` app) are updated in the same PR.
156+
157+
See [references/docs-update.md](references/docs-update.md) for the tracked-docs inventory and the code-to-docs mapping. Read when changing public API, config, or examples.

references/code-style.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Code Style
2+
3+
## Enforced tooling
4+
5+
- **Prettier** (`.prettierrc`): `singleQuote: true`, `printWidth: 80`. Enforced by a `pretty-quick --staged` pre-commit hook (husky) and by CI lint.
6+
- **ESLint** (`eslint.config.js`, flat config on `@eslint/js` recommended): `no-unused-vars` is an **error** (args checked after-used, rest siblings ignored), `linebreak-style` is unix (error), `no-useless-escape` is a warning, `no-console` is off. `CHANGELOG.md`, `docs/`, `coverage/`, `.nyc_output/`, `node_modules/` are ignored.
7+
8+
## Naming & module conventions
9+
10+
- **CommonJS everywhere:** `const x = require('...')` and `module.exports = { ... }`. No ESM `import`/`export` in the library source.
11+
- Files are camelCase matching their primary export (`appSession.js`, `transientHandler.js`, `requiresAuth.js`).
12+
- `camelCase` for functions/variables, `PascalCase` for classes/error types (`SessionExpiredError`).
13+
- Middleware factories are functions that return an Express `(req, res, next)` handler.
14+
15+
## Patterns used here
16+
17+
- **Config-schema-as-contract:** all public options are declared and defaulted in a `joi` schema (`lib/config.js`). Add new options there with a validation rule and a sensible secure default — don't read raw config elsewhere.
18+
- **Typed errors:** throw the project's error types (e.g. `SessionExpiredError` in `lib/errors.js`, which carries `code`/`status`), and `http-errors` for HTTP responses, rather than bare `Error`.
19+
- **Custom fetch wrapper:** outbound OIDC HTTP goes through `createCustomFetch` (`lib/client.js`), which injects the `User-Agent` and (opt-in) `Auth0-Client` headers.
20+
21+
## Examples
22+
23+
**✅ Good** — CommonJS, single quotes, typed error, schema-driven option:
24+
25+
```javascript
26+
const createHttpError = require('http-errors');
27+
28+
function requiresAuth(req, res, next) {
29+
if (!req.oidc.isAuthenticated()) {
30+
return next(createHttpError(401, 'Authentication required'));
31+
}
32+
next();
33+
}
34+
35+
module.exports = requiresAuth;
36+
```
37+
38+
**❌ Bad** — ESM, double quotes, bare `Error`, magic config read:
39+
40+
```javascript
41+
export function requiresAuth(req, res, next) { // no ESM in this repo
42+
if (!req.oidc.isAuthenticated()) {
43+
throw new Error("Authentication required"); // use http-errors / typed errors; double quotes fail Prettier
44+
}
45+
const ttl = process.env.SESSION_TTL || 86400; // options belong in the lib/config.js Joi schema
46+
next();
47+
}
48+
```

references/commands.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Commands
2+
3+
Full command reference for express-openid-connect. All commands are `npm run <script>` unless noted, and map to scripts in `package.json` and jobs in `.github/workflows/test.yml`.
4+
5+
## Everyday
6+
7+
```bash
8+
npm install # install dependencies (NODE_ENV=development in CI build step)
9+
npm run test # unit tests — Mocha with --max-http-header-size=16384
10+
npm run lint # ESLint over the repo (eslint .)
11+
```
12+
13+
## Testing tiers
14+
15+
```bash
16+
npm run test:ci # unit tests with coverage: nyc --reporter=lcov npm test
17+
npm run test:types # type-definition tests: tsd . (validates index.d.ts)
18+
npm run test:end-to-end # browser integration: mocha end-to-end --timeout 30000 (Puppeteer + local oidc-provider)
19+
```
20+
21+
## Docs & examples
22+
23+
```bash
24+
npm run docs # generate TypeDoc API docs into docs/ (typedoc --options typedoc.js index.d.ts)
25+
npm run start:example # run the local example app (node ./examples/run_example.js), serves http://localhost:3000
26+
```
27+
28+
## What CI runs
29+
30+
`.github/workflows/test.yml` runs, gated on a build job:
31+
32+
- **unit**`npm run test:ci` on Node 20.x / 22.x / 24.x, then uploads coverage to Codecov
33+
- **types**`npm run test:types`
34+
- **mocha**`npm run test:end-to-end`
35+
- **lint**`npm run lint`
36+
37+
Other workflows: `codeql.yml`, `snyk.yml`, `rl-secure.yml` (security scans), `release.yml` / `npm-release.yml` (release — cut by the release process, not by an agent).
38+
39+
> There is no separate build/compile step — this is plain CommonJS. The "Build Package" CI job just installs dependencies and caches them.

references/docs-update.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Docs Update Rules
2+
3+
Treat documentation as a first-class deliverable. A PR that adds or changes public API, config options, or integration patterns is **not complete** until the relevant docs are updated in the same PR.
4+
5+
## Tracked docs
6+
7+
| Doc | Covers | Status |
8+
|-----|--------|--------|
9+
| `README.md` | Install, requirements, getting started, configuration, API reference links | present |
10+
| `EXAMPLES.md` | Runnable code samples and integration patterns (proxy, PAR, backchannel logout, custom stores, etc.) | present |
11+
| `examples/` | Standalone runnable example apps exercised by the end-to-end tests | present |
12+
13+
> Not tracked here: `CHANGELOG.md` (cut by the release process, not agent-edited), and the migration guides (`V2_MIGRATION_GUIDE.md`, `V3_MIGRATION_GUIDE.md`) — their filenames are version-specific and the right one is inferred from the branch when a breaking change is made. `FAQ.md` / `TROUBLESHOOTING.md` / `ARCHITECTURE.md` exist for context but aren't part of the code-to-docs contract.
14+
15+
## When you change code, update these docs
16+
17+
This is a library/SDK; the public surface is the exported functions (`index.js`) and the config options (`lib/config.js` Joi schema).
18+
19+
| When this changes | Update these docs |
20+
|-------------------|-------------------|
21+
| A config option added, removed, renamed, or its default changed (`lib/config.js`) | `README.md` (configuration), `EXAMPLES.md` (affected samples), `index.d.ts` (types) |
22+
| Public export added/removed/renamed (`index.js`: `auth`, `requiresAuth`, `attemptSilentLogin`, `SessionExpiredError`) | `README.md` (API reference / usage), `EXAMPLES.md`, `index.d.ts` |
23+
| Authentication / session / logout flow behavior | `README.md` (getting started), `EXAMPLES.md` (auth/logout examples) |
24+
| Install requirements / supported Node versions (`engines`) | `README.md` (requirements / install) |
25+
| A new integration pattern supported | `EXAMPLES.md` (add an example) + an app under `examples/` if it warrants a runnable demo |
26+
| An `examples/` app's demonstrated API changes | the matching `examples/*.js` app and its `end-to-end/*.test.js` |
27+
28+
> When you touch code that maps to a doc above, update that doc **in the same PR** — do not defer.

references/git-workflow.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Git Workflow
2+
3+
## Branches
4+
5+
- Default/base branch is `master`. Branch off `master` for changes.
6+
- No branch-name linting is enforced; use a short descriptive name (e.g. `fix/session-cookie-samesite`).
7+
8+
## Before you commit
9+
10+
- A husky `pre-commit` hook runs `pretty-quick --staged`, auto-formatting staged files with Prettier.
11+
- Run the checks CI enforces: `npm run test` (unit), `npm run lint`, and — for type or example changes — `npm run test:types` / `npm run test:end-to-end`.
12+
13+
## Commits & PRs
14+
15+
- No commitlint/Conventional-Commits enforcement is configured; write clear, imperative commit subjects. (The maintained `CHANGELOG.md` is produced by the release process — don't hand-edit it in feature PRs.)
16+
- There is no PR template; see `CONTRIBUTING.md` and Auth0's [general contributing guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md).
17+
- Open PRs against `master`. CI (`.github/workflows/test.yml`) must pass: build, unit tests on Node 20/22/24, type tests, end-to-end, and lint. Security workflows (CodeQL, Snyk, rl-secure) also run.
18+
- `CODEOWNERS` governs required reviewers.

0 commit comments

Comments
 (0)