Skip to content

test(security): independent AuthN contract suite vs frozen Security API (#243) - #249

Merged
izzywdev merged 2 commits into
masterfrom
claude/authn-contract-tests
Jul 14, 2026
Merged

test(security): independent AuthN contract suite vs frozen Security API (#243)#249
izzywdev merged 2 commits into
masterfrom
claude/authn-contract-tests

Conversation

@izzywdev

Copy link
Copy Markdown
Owner

Independent AuthN contract/verification suite vs the frozen Security API (#243)

Independent, spec-driven verification of the provider-agnostic Security layer's AuthN slice, authored by test-engineer (not the implementer). Every assertion is against the FROZEN contract packages/security/openapi.yaml (Ajv/OpenAPI-3.1 schema validation) + the internal IdentityProvider swap interface — never an implementation's internals.

What it covers (backend/security/tests/security-api/)

  • Session: password login → SessionResult union (authenticated / mfa_required), GET /session ("me"), logout (204, idempotent), POST /session/exchange opaque-code, fail-closed 401 on bad creds/unknown code.
  • Social: 302 semantics for /social/{provider}/start + /social/callback, opaque ?code= return (no token in URL), cross-origin redirectTo rejection.
  • Signup + methods: 201 LoginResponse, 409 on existing email, neutral AuthMethods descriptor.
  • MFA: full factor lifecycle (enroll/activate/remove/recovery-codes) + login step-up (challenge → verify), fail-closed on bad codes.
  • Verify: email + phone contact-ownership start/confirm/status.
  • M2M tokens: issuance + fail-closed introspection (unknown/expired → { active: false }).
  • Provider-swap proof: the full AuthN path driven through a SECOND, independent IdentityProvider (different token format/storage) — objective evidence the consumer contract has zero vendor coupling.
  • Boundary/neutrality: no response/redirect ever names auth.fuzefront.com or a vendor (authentik/permit/…).
  • Pagination gate: every AuthN endpoint asserted genuinely x-pagination: exempt with a reason; the spec's paginated AuthZ collections asserted to declare limit+cursor + the { items, page } envelope at the contract level (runtime cursor-walk is AuthZ Phase-2, flagged it.todo, not silently skipped).

Run results

Test Suites: 9 passed, 9 total
Tests:       57 passed, 1 todo, 58 total

Type-clean (tsc --noEmit, noUnusedLocals). Runs against the in-process contract-mock today; set SECURITY_BASE_URL to run the identical assertions against the real implementation once it lands (harness.ts).

Scope

Independent API/contract/event verification only. OUT OF SCOPE: backend impl, UI/browser e2e (frontend-test-engineer), deploy wiring. A failing test here against a real backend bug is a valid deliverable, not a blocker to hide.

🤖 Generated with Claude Code

fuzeone-bot Bot and others added 2 commits July 14, 2026 13:59
…[skip ci]

Contract/integration tests for /api/v1/security AuthN endpoints, schema-validated
(Ajv, OpenAPI 3.1) against packages/security/openapi.yaml: session CRUD+exchange,
social 302, signup, methods, SessionResult MFA union, MFA factor lifecycle +
step-up, email/phone verification, M2M tokens. Provider-swap proof via a second
IdentityProvider; boundary/vendor-neutrality + fail-closed; pagination gate.

Co-Authored-By: Claude claude-opus-4-8 <noreply@anthropic.com>
Claude-Session-Id: cf830721-b1ef-4fe0-a024-035ad280dcf7
…-mock server + uuid-conformant swap fixture

Root-caused 6 red tests to a harness bug (independent verification, not a
product bug): the no-arg agent() built a fresh app+MockIdentityProvider per
call, so a token minted at login was unknown to the follow-up authed request
(401). The single reference-app + one persistent in-memory provider IS the
in-process stand-in for the running server, so agent() now reuses one instance
per test file (explicit-provider callers still get a fresh app). provider-swap
reuses one AltIdentityProvider app for the same reason.

Also fixed the provider-swap fixture to emit uuid-format User.id (the contract's
User.id format) — a conformant provider must; MockIdentityProvider already did.

Suite now: 9 suites, 57 passed, 1 todo (AuthZ Phase-2 runtime pagination walk,
out of AuthN scope). Type-clean under noUnusedLocals.

Co-Authored-By: Claude claude-opus-4-8 <noreply@anthropic.com>
Claude-Session-Id: cf830721-b1ef-4fe0-a024-035ad280dcf7
@izzywdev izzywdev added the auto-merge Enable squash auto-merge once CI passes label Jul 14, 2026
@github-actions
github-actions Bot enabled auto-merge (squash) July 14, 2026 17:25
@github-actions

Copy link
Copy Markdown
Contributor

CI Fix — lock file regenerated

Root cause: This PR added ajv, ajv-formats, js-yaml, and @types/js-yaml as devDependencies to backend/security/package.json but did not regenerate package-lock.json. npm ci requires the lock file to match package.json exactly, so it failed with:

Missing: ajv@8.20.0 from lock file

in both the Lint & Test (18.x) and Identity UI + Security (unit) jobs. The Notify Team failure was downstream of those two.

Fix: Ran npm install --package-lock-only at repo root to regenerate the lock file, adding the backend/security/node_modules/ajv@8.20.0 entry and all the new devDependencies to the workspace section.

Branch with fix: claude-auto-fix-ci-claude/authn-contract-tests-29353738515 (commit d2c5143)

This branch targets claude/authn-contract-tests — please merge it into this PR's branch to unblock CI, or cherry-pick the lock file commit directly.

@github-actions

Copy link
Copy Markdown
Contributor

CI fix: regenerated package-lock.json

Root cause: The `backend/security/package.json` added four new devDependencies for the AuthN contract test suite but the root `package-lock.json` was not updated, causing `npm ci` to fail at the "Build & start backend" step with:

```
npm error Missing: @types/js-yaml@4.0.9 from lock file
npm error Missing: ajv@8.20.0 from lock file
npm error Missing: ajv-formats@3.0.1 from lock file
```

Fix: Ran `npm install --package-lock-only` to regenerate the lock file, adding proper entries for `@types/js-yaml`, `ajv@8.20.0`, `ajv-formats@3.0.1`, and `js-yaml` under the `backend/security` workspace.

Branch: `claude-auto-fix-ci-claude/authn-contract-tests-29353746278` (commit cb3b68e) — ready to merge into `claude/authn-contract-tests` once CI passes.

Note: GitHub Actions is not permitted to open a PR automatically in this repo, so the fix branch is pushed but requires a human to open/merge the PR or squash it into #249.

@github-actions

Copy link
Copy Markdown
Contributor

Automated code review (gate-code-review)

Verified the suite against the frozen spec (packages/security/openapi.yaml): the discriminated SessionResult/AuthenticatedSession/MfaRequiredChallenge unions resolve, Identity/User/MfaEnrollResult/ErrorBody required-field sets match what the mock and AltIdentityProvider return, and the pagination gate references schemas/params that exist. Those paths are correct.

One genuine wrong-condition (fail-open) defect:

  • backend/security/tests/security-api/mockIdentityProvider.ts:192brokerCallback's validity check input.code !== SEED.socialCode && !input.code.startsWith('state_') && input.code.length < 3 only rejects codes shorter than 3 chars. With a valid state, any junk code ≥3 chars (e.g. "invalid") passes and mints/links a session — a fail-open in a suite whose stated guarantee is fail-closed. (No negative-code callback test exercises this, so it passes silently rather than failing.)

Report-only — this check never blocks merge.

@github-actions

Copy link
Copy Markdown
Contributor

CI autofix: gate-frontend-build (ajv@8.20.0 missing from lock file)

Root cause: backend/security/package.json gained four new devDependencies (ajv@^8.17.1, ajv-formats@^3.0.1, js-yaml@^4.1.0, @types/js-yaml@^4.0.9) as part of the AuthN contract test suite, but package-lock.json was never regenerated. The root npm ci step failed with:

Missing: ajv@8.20.0 from lock file

Fix: Ran npm install --package-lock-only to sync the lock file. This added the missing workspace-level devDependency entries and a nested backend/security/node_modules/ajv@8.20.0 entry.

Branch with fix: claude-auto-fix-ci-claude/authn-contract-tests-29353996162 (commit 7606049)

To merge this fix into the PR branch, run:

git fetch origin claude-auto-fix-ci-claude/authn-contract-tests-29353996162
git cherry-pick 7606049

@github-actions

Copy link
Copy Markdown
Contributor

CI fix pushed — branch `claude-auto-fix-ci-claude/authn-contract-tests-29353750237`

Root cause: All three failing CI jobs (Backend tests Node 22.x/24.x and Permit.io integration tests) failed at the `npm ci` step with:

```
npm error npm ci can only install packages when your package.json and
package-lock.json are in sync.
npm error Missing: @types/js-yaml@4.0.9 from lock file
npm error Missing: ajv@8.20.0 from lock file
npm error Missing: ajv-formats@3.0.1 from lock file
```

When the AuthN contract test suite added `ajv`, `ajv-formats`, and `@types/js-yaml` to `backend/security/package.json`, the workspace root `package-lock.json` was never regenerated. Since `npm ci` failed, no `node_modules` were installed, causing all subsequent test steps to also fail with exit code 127 (jest not found).

Fix: Ran `npm install` at the workspace root to regenerate `package-lock.json` with the three missing packages. Verified `npm ci --dry-run` succeeds.

The fix is committed to `claude-auto-fix-ci-claude/authn-contract-tests-29353750237` (1 file changed: `package-lock.json`, +67/-10 lines). Please merge this branch into `claude/authn-contract-tests` to unblock CI.

@izzywdev
izzywdev merged commit 7af2c1c into master Jul 14, 2026
37 of 49 checks passed
@izzywdev
izzywdev deleted the claude/authn-contract-tests branch July 14, 2026 17:44
@izzywdev
izzywdev restored the claude/authn-contract-tests branch July 22, 2026 16:33
@izzywdev
izzywdev deleted the claude/authn-contract-tests branch July 27, 2026 18:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

auto-merge Enable squash auto-merge once CI passes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant