From 3d1b298835a33a9badcb689ce963a20f4e46e85c Mon Sep 17 00:00:00 2001 From: Antony Rizzitelli Date: Sun, 19 Jul 2026 21:56:01 +0200 Subject: [PATCH 1/3] feat(skills): ship a consumer skill with the package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add skills/-interface/SKILL.md — a lean, source-grounded guide for agents consuming this interface (imports, minimal example, gotchas) — declared via antelopeJs.skills and published through the files array. Consumers receive it automatically: the antelopejs Claude Code plugin syncs package-shipped skills into a project's .claude/skills/, and the cms-ai chatbox loads them at runtime. Content was fact-checked against src/ and docs/ by an adversarial review pass (imports validated against the exports map, examples verified against real signatures). --- package.json | 8 +++- skills/auth-interface/SKILL.md | 83 ++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 skills/auth-interface/SKILL.md diff --git a/package.json b/package.json index d94e18d..192fe89 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "main": "dist/index.js", "types": "dist/index.d.ts", "files": [ - "dist" + "dist", + "skills" ], "exports": { ".": { @@ -58,6 +59,9 @@ "access": "public" }, "antelopeJs": { - "test": "src/antelope.test.ts" + "test": "src/antelope.test.ts", + "skills": [ + "./skills" + ] } } diff --git a/skills/auth-interface/SKILL.md b/skills/auth-interface/SKILL.md new file mode 100644 index 0000000..0af64a1 --- /dev/null +++ b/skills/auth-interface/SKILL.md @@ -0,0 +1,83 @@ +--- +name: auth-interface +description: AntelopeJS interface for token-based authentication - signing and verifying auth tokens (SignRaw, ValidateRaw, SignServerResponse) and injecting the verified payload into interface-api controllers via the @Authentication decorator or custom decorators from CreateAuthDecorator. Use when code imports "@antelopejs/interface-auth", when securing controller routes, generating or validating auth/JWT tokens, injecting authenticated user data into handler parameters, or when implementing an auth provider for internal.Verify/internal.Sign. +category: antelopejs-interface +tags: [antelopejs, auth, jwt, tokens, decorators] +--- + +# @antelopejs/interface-auth + +Token authentication for AntelopeJS modules. Two layers: + +- **Proxy crossings** (need a provider module loaded, e.g. a JWT module): `internal.Verify` and `internal.Sign`, wrapped by `SignRaw`, `ValidateRaw`, and `SignServerResponse`. Always async. +- **Consumer-side helpers** (no crossing by themselves): `Authentication` and `CreateAuthDecorator`, which build parameter providers on top of `@antelopejs/interface-api`. + +## Imports + +All symbols come from the package root (the exports map exposes no other code subpaths): + +```ts +import { + Authentication, CreateAuthDecorator, + SignRaw, ValidateRaw, SignServerResponse, + internal, // provider side only + type AuthSource, type AuthVerifier, type AuthValidator, + type SignOptions, type VerifyOptions, type CookieOptions, +} from "@antelopejs/interface-auth"; +``` + +`@antelopejs/interface-api` and `@antelopejs/interface-core` are peerDependencies — the consuming module must have them installed. + +## Consuming + +```ts +import { Controller, Get, Post } from "@antelopejs/interface-api"; +import { Authentication, SignRaw } from "@antelopejs/interface-auth"; + +interface UserSession { id: string; role: string; } + +class UserController extends Controller("/users") { + @Post("login") + async login() { + // Sign a payload into a token (proxy call to the auth provider) + return SignRaw({ id: "42", role: "admin" }, { expiresIn: "1h" }); + } + + @Get("profile") + async getProfile(@Authentication() user: UserSession) { + // Token was read from the request, verified, and injected + return { id: user.id, role: user.role }; + } +} +``` + +Custom pipelines use `CreateAuthDecorator({ source?, authenticator?, authenticatorOptions?, validator? })`; the callbacks run as `source(req, res)` → `authenticator(data, authenticatorOptions)` → `validator(data)` → injected parameter. + +## Providing + +An auth backend module implements the two proxy points: + +```ts +import { ImplementInterface } from "@antelopejs/interface-core"; +import { internal } from "@antelopejs/interface-auth"; + +ImplementInterface(internal, { + Verify: (token, options) => decodeAndVerify(token, options), // return payload, throw on invalid + Sign: (data, options) => signToken(data, options), // return token string +}); +``` + +Declare `"antelopeJs": { "implements": ["@antelopejs/interface-auth"] }` in the provider's package.json. + +## Gotchas + +- `SignRaw` / `ValidateRaw` / `SignServerResponse` are interface proxy calls: they return Promises and only resolve once a provider module is attached. Calls made earlier are queued, not failed — always `await`. +- Default token source (`internal.defaultSource`): the `x-antelopejs-auth` request header, falling back to the `ANTELOPEJS_AUTH` cookie. `SignServerResponse` sets that same cookie via `Set-Cookie`. +- `@Authentication(validator?)` accepts an optional validator at the use site; it overrides any validator configured in `CreateAuthDecorator`. +- Decorators from `CreateAuthDecorator` (including `Authentication`) apply to parameters, properties, and whole classes (class-level registers a provider for the controller) — NOT to methods; decorating a method silently registers nothing and can break other parameter decorators on that handler. +- `expiresIn` / `notBefore` (SignOptions) and `maxAge` (VerifyOptions) accept a number of seconds or a timespan string such as `"1h"`. +- Rejection is exception-based: a failed verification or validator throws, it does not return `undefined`. + +## Deeper reference + +See this package's `docs/` chapters — Introduction, Authentication Basics, Token Handling, Parameter Decoration — and the shipped `dist/index.d.ts` for full TSDoc signatures. Do not duplicate them here. From 9e49d56d3cd0f24b57ed32b00c81524f8fa939c8 Mon Sep 17 00:00:00 2001 From: Antony Rizzitelli Date: Sun, 19 Jul 2026 23:32:55 +0200 Subject: [PATCH 2/3] fix(skills): ship docs with the package and scope the proxy-queuing gotcha Queuing only holds while a provider module is loaded but not yet attached; when no loaded module provides the interface (stubbed optional dependency), the core neutralizes the async proxies and calls reject. --- package.json | 1 + skills/auth-interface/SKILL.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 192fe89..449969b 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "types": "dist/index.d.ts", "files": [ "dist", + "docs", "skills" ], "exports": { diff --git a/skills/auth-interface/SKILL.md b/skills/auth-interface/SKILL.md index 0af64a1..38206b3 100644 --- a/skills/auth-interface/SKILL.md +++ b/skills/auth-interface/SKILL.md @@ -71,7 +71,7 @@ Declare `"antelopeJs": { "implements": ["@antelopejs/interface-auth"] }` in the ## Gotchas -- `SignRaw` / `ValidateRaw` / `SignServerResponse` are interface proxy calls: they return Promises and only resolve once a provider module is attached. Calls made earlier are queued, not failed — always `await`. +- `SignRaw` / `ValidateRaw` / `SignServerResponse` are interface proxy calls: they return Promises and only resolve once a provider module is attached. While a provider module is loaded but not yet attached, earlier calls are queued, not failed — always `await`. But when no loaded module provides the interface (e.g. a stubbed `optionalDependencies` entry), the core neutralizes the proxies and those calls reject instead of queuing. - Default token source (`internal.defaultSource`): the `x-antelopejs-auth` request header, falling back to the `ANTELOPEJS_AUTH` cookie. `SignServerResponse` sets that same cookie via `Set-Cookie`. - `@Authentication(validator?)` accepts an optional validator at the use site; it overrides any validator configured in `CreateAuthDecorator`. - Decorators from `CreateAuthDecorator` (including `Authentication`) apply to parameters, properties, and whole classes (class-level registers a provider for the controller) — NOT to methods; decorating a method silently registers nothing and can break other parameter decorators on that handler. From 0f84ba52c8792a69c4b8c38bd8b15ec6d0af3aea Mon Sep 17 00:00:00 2001 From: Antony Rizzitelli Date: Mon, 20 Jul 2026 16:12:36 +0200 Subject: [PATCH 3/3] docs(skills): use fictional domains in code examples --- skills/auth-interface/SKILL.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/skills/auth-interface/SKILL.md b/skills/auth-interface/SKILL.md index 38206b3..80f0f8d 100644 --- a/skills/auth-interface/SKILL.md +++ b/skills/auth-interface/SKILL.md @@ -34,19 +34,19 @@ import { import { Controller, Get, Post } from "@antelopejs/interface-api"; import { Authentication, SignRaw } from "@antelopejs/interface-auth"; -interface UserSession { id: string; role: string; } +interface LibrarianSession { id: string; branch: string; } -class UserController extends Controller("/users") { +class LibrarianController extends Controller("/librarians") { @Post("login") async login() { // Sign a payload into a token (proxy call to the auth provider) - return SignRaw({ id: "42", role: "admin" }, { expiresIn: "1h" }); + return SignRaw({ id: "lib-7", branch: "riverside" }, { expiresIn: "1h" }); } - @Get("profile") - async getProfile(@Authentication() user: UserSession) { + @Get("desk") + async getDesk(@Authentication() librarian: LibrarianSession) { // Token was read from the request, verified, and injected - return { id: user.id, role: user.role }; + return { id: librarian.id, branch: librarian.branch }; } } ```