-
Notifications
You must be signed in to change notification settings - Fork 0
feat: a contract declares which procedures need an authenticated caller #73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
38 commits
Select commit
Hold shift + click to select a range
ee3c028
feat(contract): a contract-level marker for an authenticated procedure
btravers 92b18e3
chore: the ninth package joins the fixed group, the docs build and th…
btravers 7727c3d
feat(http): a marked contract types its handler's principal
btravers 58ac55d
feat(http): resolve a marked procedure's principal from an Authentica…
btravers b516d10
fix(http): a marked contract root installs the auth middleware, and a…
btravers 9c0e4e7
feat(http): the authenticator a marked contract needs is an HttpModul…
btravers ca8864f
docs(http): the auth type test's two gates are different gates, at di…
btravers 1b8039b
feat(http): forward oRPC handler plugins from http() and HttpModule
btravers a59371f
feat(http): security headers on the listener, covering the runtime's …
btravers 6b2d18c
feat(example): the orders fragment declares an authenticated principal
btravers cd9a90a
feat(example): order-api authenticates its callers through the starte…
btravers 782c2ea
docs(example): the prose says what the marked contract actually does
btravers aeef138
docs: the auth marker, the authenticator, plugins and security headers
btravers e8236b2
docs: the cross-cutting-concerns and transaction decisions, and the c…
btravers 28c8557
fix(http): a rejected caller's reason stays in the process
btravers 1f280ea
docs(http): plugins is an escape hatch, and the README states the non…
btravers 1b35a15
docs(http): the non-goals bullet agrees with the plugins bullet
btravers 81f226f
fix(http): the keyed router form composes a root-marked contract
btravers 6861480
fix(contract): one marker registry per install, not per copy
btravers 40699f5
fix(example): a marked fragment names no tenant on its input
btravers 8ec3bb6
test(http): a custom securityHeaders record is applied verbatim
btravers 49a8343
docs: the http spec count the branch left stale
btravers a792fe8
fix(http): hoist the security-header entries out of the request path
btravers 69230c3
refactor(example): the contract declares the minimum, the authenticat…
btravers 3ddad2b
feat(http): httpAuth mints controller, router and authenticator on on…
btravers 7ff784a
test(http): the factory's identity, in the types and through a real r…
btravers 393556a
refactor(example): the order API mints its controllers from httpAuth
btravers 418890e
docs(http): the identity factory, and the rule it states
btravers 3a1b490
feat(contract)!: the marker says whether, not who
btravers 29fcc9a
feat(http)!: the gate pairs the router's identity with the authentica…
btravers 77c2299
refactor(example): the contract marks the route, auth.ts names the id…
btravers 3f5d015
docs: the contract says whether, the factory says what
btravers f6e6d06
docs: the site and the changesets on the collapsed marker
btravers 7573776
chore: untrack the SQLite shadow database committed by accident
btravers 76d607f
refactor: cut what the marker and the factory did not need
btravers dfe5014
fix(docs): the samples compile, and the README stops admitting empty …
btravers a031e9f
test(http): the authenticator shape every adopter writes
btravers 6eea429
test(contract): the registry assertion names which half failed
btravers File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| --- | ||
| "@btravstack/contract": minor | ||
| "@btravstack/http": minor | ||
| --- | ||
|
|
||
| Let a contract declare that a procedure requires an authenticated caller, and | ||
| give `@btravstack/http` what it needs to satisfy that declaration. | ||
|
|
||
| **The contract says whether a route is protected; the application's | ||
| `httpAuth<Identity>()` says what the principal is.** | ||
|
|
||
| `@btravstack/contract` is a new zero-dependency package holding the marker | ||
| itself: `authenticated(node)`, one export with no factory and no type | ||
| parameter, applied to a finished procedure or to a whole record of them. It | ||
| names no identity type at all, so nothing about a server's view of a caller | ||
| reaches a client. It returns the node unchanged — the marker lives in a | ||
| `WeakSet` and a phantom type key set to `true` — so a client can import a | ||
| marked contract without pulling in anything that implements it. `IsMarked<T>` | ||
| answers the yes/no at the type level, `isAuthenticated(node)` at runtime. An | ||
| unmarked procedure is public; the marker makes the requirement legible in the | ||
| contract rather than detecting one that was forgotten. | ||
|
|
||
| `@btravstack/http` resolves the principal through a new `Authenticator` port — | ||
| `HttpAuthenticator<P>()([deps], { sync })`, an ordinary di provider, wired on | ||
| `HttpModule`'s `authenticator` option. A contract that marks nothing needs no | ||
| authenticator; a marked router whose root provides none is di's existing | ||
| `UNSATISFIED DEPENDENCIES` gate, and an authenticator minted on a different | ||
| identity than the router is refused at `HttpModule`. A marked procedure whose | ||
| authenticator declines is answered `UNAUTHORIZED` before dispatch, with the | ||
| handler never running and no reason reaching the caller — `Unauthenticated` | ||
| carries none, so an authenticator logs why before returning. | ||
|
|
||
| `http()` and `HttpModule` also gain `plugins`, forwarding oRPC handler plugins | ||
| (CORS, body limits, compression, CSRF) straight to `RPCHandler`, and | ||
| `securityHeaders`, applied on the node listener rather than as a plugin so the | ||
| runtime's own `404` is covered too. `plugins` is an honest escape hatch rather | ||
| than a keyhole — an oRPC plugin's `init` can reach the handler's interceptors — | ||
| but the ordinary path is configuration visible at the composition root, not a | ||
| middleware slot for application logic. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| --- | ||
| "@btravstack/http": minor | ||
| --- | ||
|
|
||
| Let a deployment state what its principal actually is, server-side, with | ||
| `httpAuth<Identity>()`. | ||
|
|
||
| The contract says **whether** a route is protected and names no identity type | ||
| at all. `httpAuth<Identity>()` is what says **what** the principal is: it mints | ||
| `HttpController`, `HttpRouter` and `HttpAuthenticator` together, all fixed to | ||
| that identity. Written once per application, because a handler's parameter | ||
| types are fixed where the arrow is written and a composition root cannot | ||
| re-type a `sync` callback living in another module; every slice then imports | ||
| `HttpController` from that one file and its marked handlers see `Identity` on | ||
| `context.principal` with no annotation of their own. The authenticator and the | ||
| controllers cannot disagree, since both come from the same call, and it is | ||
| handed back already applied (`HttpAuthenticator([deps], { sync })`). | ||
|
|
||
| It is also the only way a handler gets a readable principal: `HttpController` | ||
| and `HttpRouter` imported from the package itself name no identity, so a marked | ||
| fragment reached through them types `principal: never` and every read is a | ||
| compile error — the signal to use the factory, not a fallback. The contract | ||
| still decides _whether_: an unmarked procedure's context carries no principal, | ||
| factory or not. | ||
|
|
||
| `HttpModule`'s gate compares the **router's** identity against the | ||
| **authenticator's** — `AuthIdentity extends RouterIdentity`, so an | ||
| authenticator resolving more than the handlers read discharges it, while one | ||
| minted by a different `httpAuth` call does not. `ContractPrincipal` is replaced | ||
| by `HasMark<C>`, exactly `true` or `false`, which is all the conditional | ||
| authenticator dependency ever needed. | ||
|
|
||
| Also exported: `HttpAuth<Identity>` and the three `HttpControllerOf` / | ||
| `HttpRouterOf` / `HttpAuthenticatorOf` aliases, which a file exporting what the | ||
| factory returns needs to annotate with. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.