Skip to content

Extract the session layer behind an AuthProvider contract in @wasp.sh/auth-contract - #4725

Draft
FranjoMindek wants to merge 1 commit into
franjo/auth2-1-examples-initfrom
franjo/auth2-2-auth-contract
Draft

Extract the session layer behind an AuthProvider contract in @wasp.sh/auth-contract#4725
FranjoMindek wants to merge 1 commit into
franjo/auth2-1-examples-initfrom
franjo/auth2-2-auth-contract

Conversation

@FranjoMindek

Copy link
Copy Markdown
Contributor

Description

Stack 2/7. Extracts Wasp's session layer behind an AuthProvider contract, born in its final home and final shape: the @wasp.sh/auth-contract npm package (a generator lib, shipped into apps as a tarball).

The contract is small and final — nothing later in the stack rewrites it, only extends it:

  • VerifiedSession { sessionId, subjectId, claims? } — verify, don't fetch: a provider turns a credential into an identity; it is deliberately not findById.
  • AuthProvider { id, authenticate(request: Request), revokeSession } — a standard web Request; for websockets Wasp synthesizes one carrying only the bearer header.
  • SessionIssuingAuthProvider { issueSession, revokeAllSessions } + canIssueSessions() — session establishment is a capability, not part of the base interface (Clerk has no server-side login at all).

session.ts now talks only to the interface; Wasp's own Lucia-backed auth becomes its first implementation (provider/wasp.ts). Nothing else changes hands: middleware, websocket handler and logout route are untouched consumers.

Three deliberate behavior changes to scrutinize, all prerequisites for later JIT provisioning:

  1. User lookup goes through the auth relation (findFirst keyed by auth id) — same query count, the shape every provider needs.
  2. A valid session whose auth row has no linked user now reads as unauthenticated (null) instead of throwing.
  3. createSession returns { id } instead of a Lucia Session; wasp/server/auth/session (PRIVATE API) signatures change accordingly.

The adapter-package surface (ServerAdapterFactory etc.) deliberately arrives in stack 6/7 together with its consumers. There is intentionally no client-side contract: an adapter's client side ships as ordinary React exports on its own /client entry, and Wasp generates no client glue (design note for a future generated-glue iteration: a Wrapper composition slot, pull-based getCredential(), onCredentialChange for websocket re-auth, onLogout — deferred until real consumers exist).

Review guidance: the contract + session.ts + provider/wasp.ts are the substance (~600 lines); skip goldens and the @wasp.sh/auth-contract lockfile entries in example lockfiles.

Type of change

  • 🔧 Just code/docs improvement
  • 🐞 Bug fix
  • 🚀 New/improved feature
  • 💥 Breaking change

Checklist

  • I tested my change in a Wasp app to verify that it works as intended.

  • 🧪 Tests and apps:

    • I added unit tests for my change.
    • (if you fixed a bug) I added a regression test for the bug I fixed.
    • (if you added/updated a feature) I added/updated e2e tests in examples/kitchen-sink/e2e-tests.
    • (if you added/updated a feature) I updated the starter templates in waspc/data/Cli/templates, as needed.
    • (if you added/updated a feature) I updated the example apps in examples/, as needed.
      • (if you updated examples/tutorials) I updated the tutorial in the docs (and vice versa).
  • 📜 Documentation:

    • (if you added/updated a feature) I added/updated the documentation in web/docs/.
  • 🆕 Changelog: (if change is more than just code/docs improvement)

    • I updated waspc/ChangeLog.md with a user-friendly description of the change.
    • (if you did a breaking change) I added a step to the current migration guide in web/docs/migration-guides/.
    • I bumped the version in waspc/waspc.cabal to reflect the changes I introduced.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 14, 2026

Copy link
Copy Markdown

Deploying wasp-docs-on-main with  Cloudflare Pages  Cloudflare Pages

Latest commit: c06e4b2
Status: ✅  Deploy successful!
Preview URL: https://c55e83f2.wasp-docs-on-main.pages.dev
Branch Preview URL: https://franjo-auth2-2-auth-contract.wasp-docs-on-main.pages.dev

View logs

@pkg-pr-new

pkg-pr-new Bot commented Aug 14, 2026

Copy link
Copy Markdown

Open in StackBlitz

@wasp.sh/spec

npx https://pkg.pr.new/wasp-lang/wasp/@wasp.sh/spec@4725

@wasp.sh/wasp-cli

npx https://pkg.pr.new/wasp-lang/wasp/@wasp.sh/wasp-cli@4725

@wasp.sh/wasp-cli-darwin-arm64-unknown

npx https://pkg.pr.new/wasp-lang/wasp/@wasp.sh/wasp-cli-darwin-arm64-unknown@4725

@wasp.sh/wasp-cli-darwin-x64-unknown

npx https://pkg.pr.new/wasp-lang/wasp/@wasp.sh/wasp-cli-darwin-x64-unknown@4725

@wasp.sh/wasp-cli-linux-arm64-glibc

npx https://pkg.pr.new/wasp-lang/wasp/@wasp.sh/wasp-cli-linux-arm64-glibc@4725

@wasp.sh/wasp-cli-linux-x64-glibc

npx https://pkg.pr.new/wasp-lang/wasp/@wasp.sh/wasp-cli-linux-x64-glibc@4725

@wasp.sh/wasp-cli-linux-x64-musl

npx https://pkg.pr.new/wasp-lang/wasp/@wasp.sh/wasp-cli-linux-x64-musl@4725

commit: 9ff7043

@FranjoMindek
FranjoMindek force-pushed the franjo/auth2-2-auth-contract branch from e56cc25 to 6784eb5 Compare August 14, 2026 19:16
@FranjoMindek

Copy link
Copy Markdown
Contributor Author

Two contract refactors landed in this revision:

  1. authenticate() no longer returns VerifiedSession | null. It returns a tagged union, AuthenticateResult = { status: "authenticated", session } | { status: "unauthenticated" } — call sites read as prose, and future outcomes (an explicit invalid-credential state, say) become additive union members instead of signature breaks.
  2. External provider ids carry an external: prefix (external:clerk), documented on AuthProvider.id. The unprefixed namespace is reserved for Wasp's own auth methods, which record identities under the same AuthIdentity.providerName column — the prefix makes a collision impossible. Enforced at definition time in Make app.auth.provider a required discriminated union: waspAuth() or an external provider manifest #4726.

@FranjoMindek

Copy link
Copy Markdown
Contributor Author

Third contract refinement: revokeSession moved off the base AuthProvider into an optional SessionRevokingAuthProvider (which SessionIssuingAuthProvider now extends). A pure token verifier (corporate SSO relying party, an authenticating proxy) has nothing to revoke, and previously could only implement a lying no-op. Now: the base contract is id + authenticate; canRevokeSessions() narrows; logout() still works everywhere (client credential drop) and calls revokeSession only where it exists. The pairing on the issuing side is Wasp's stated policy (no login flows over sessions Wasp cannot kill), not an ontological claim. Side effect with teeth: the boot assert for the session-revocation manifest capability is now structural — it could never fire when the method was mandatory.

@FranjoMindek

Copy link
Copy Markdown
Contributor Author

Final contract shape, per review discussion: capabilities are now pure mixins, not an inheritance chain. AuthProvider { id, authenticate } is the only provider type; SupportsSessionRevocation, SupportsAllSessionsRevocation and SupportsSessionIssuance are independent ability bags (bulk revocation split from single: revoking one needs a handle you hold, revoking all needs an index by subject, and providers exist with either alone). An adapter declares what it is by intersection: AuthProvider & SupportsSessionRevocation. Wasp's policy (no login flows over sessions it cannot kill) lives in the canManageSessions() guard and the SessionManagingAuthProvider alias, not in the type lattice.

@FranjoMindek
FranjoMindek force-pushed the franjo/auth2-2-auth-contract branch from 2ced327 to c06e4b2 Compare August 15, 2026 14:43
@FranjoMindek
FranjoMindek force-pushed the franjo/auth2-2-auth-contract branch from c06e4b2 to 9ff7043 Compare August 17, 2026 08:42
@github-actions

Copy link
Copy Markdown
Contributor

This pull request is now stale.
That means that it hasn't seen any activity for a while, and needs to be updated or addressed before it can be merged.

Next steps if the PR is still relevant, and you are able to devote time to it:

  • If you received any questions or feedback, please address them.
  • Merge the latest main into your branch to ensure that you are up to date.
  • If you are still working on the changes, please leave a comment telling us so.
  • Otherwise, just leave a comment explaining why the PR is still relevant.

If no action is taken, this PR will be automatically closed in 7 days.

@github-actions github-actions Bot added the Stale label Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants