.claude/skills/pikku-jose/SKILL.md
Use when setting up JWT authentication with the jose library in a Pikku app. Covers JoseJWTService constructor, secret rotation, token encoding/decoding/verification. TRIGGER when: code uses JoseJWTService, user asks about JWT setup, token signing, token verification, or @pikku/jose. DO NOT TRIGGER when: user asks about session middleware (use pikku-security) or general service setup (use pikku-services).
npx skillsauth add pikkujs/pikku pikku-joseInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
@pikku/jose provides JWT signing, verification, and decoding using the jose library. Implements the JWTService interface from @pikku/core.
yarn add @pikku/jose
JoseJWTServiceimport { JoseJWTService } from '@pikku/jose'
const jwt = new JoseJWTService(
getSecrets: () => Promise<Array<{ id: string; value: string }>>,
logger?: Logger
)
await jwt.init()
Constructor Parameters:
getSecrets — Async function returning an array of { id, value } key pairs. First key is used for signing; all keys are tried for verification (supports rotation).logger — Optional logger instance.Methods:
init(): Promise<void> — Fetch and cache secrets. Call at startup.encode<T>(expiresIn: RelativeTimeInput, payload: T): Promise<string> — Create a signed JWT.decode<T>(token: string): Promise<T> — Decode a JWT payload without verification.verify(token: string): Promise<void> — Verify a JWT signature and expiry.import { JoseJWTService } from '@pikku/jose'
const jwt = new JoseJWTService(
async () => [{ id: 'key-1', value: process.env.JWT_SECRET! }],
logger
)
await jwt.init()
Supply multiple keys. The first is used for signing; all are tried for verification:
const jwt = new JoseJWTService(async () => [
{ id: 'key-2', value: NEW_SECRET }, // signs with this
{ id: 'key-1', value: OLD_SECRET }, // still verifies tokens signed with this
])
const createSingletonServices = pikkuServices(async (config) => {
const logger = new ConsoleLogger()
const jwt = new JoseJWTService(
async () => [{ id: 'my-key', value: config.jwtSecret }],
logger
)
await jwt.init()
return { config, logger, jwt }
})
const token = await jwt.encode('1h', { userId: 'abc', role: 'admin' })
await jwt.verify(token) // throws if invalid/expired
const payload = await jwt.decode<{ userId: string; role: string }>(token)
documentation
Deprecated — use pikku-middleware instead. Tag middleware (addTagMiddleware) is now documented as a section within the pikku-middleware skill, alongside global HTTP middleware, execution order, and the service-to-service bearer auth pattern.
testing
Use when adding authorization checks to Pikku functions or routes — pikkuPermission, pikkuAuth, per-function permissions, pattern-based permissions, or understanding OR/AND permission logic. TRIGGER when: user wants to restrict who can call a function, check resource ownership, add role-based access, or understand where permission checks belong. DO NOT TRIGGER when: user asks about middleware or request interception (use pikku-middleware), authentication strategies (use pikku-security), or session management.
testing
Use when adding any middleware to a Pikku app — global HTTP middleware, tag-scoped middleware (including service-to-service bearer auth), per-route middleware, session-setting middleware, or understanding middleware execution order and priority. TRIGGER when: user wants middleware on some or all routes, machine-to-machine auth, tag-scoped cross-cutting concerns, global interceptors, or middleware priority/order questions. DO NOT TRIGGER when: user asks about permissions/authorization checks (use pikku-permissions), auth strategies like authBearer/authCookie (use pikku-security), or deployment.
documentation
Standard cleanup to run right after a Pikku template is cloned or scaffolded into a new project. TRIGGER when: a Pikku template was just cloned/scaffolded (via `pikku create`, `git clone <template>`, or the user says "I cloned the kanban template / starter / template"), or the working tree still looks like an untouched template (template README, placeholder `@project/*` name in package.json). DO NOT TRIGGER when: working in an established project mid-feature, or editing the template repo itself.