transit-kit 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -158,6 +158,131 @@ server.registerApiEndpoint(createUserEndpoint);
158
158
  server.start();
159
159
  ```
160
160
 
161
+ ## Authentication
162
+
163
+ Transit-kit includes a simple, composable authentication mechanism based on `Authorization` headers. You declare one or more `SecurityScheme`s on an endpoint, and the framework will:
164
+
165
+ - Apply an authentication middleware for that endpoint
166
+ - Try all declared schemes in order and pick the first successful one
167
+ - Return `401 Unauthorized` automatically when authentication fails
168
+ - Inject the authenticated `caller` into your endpoint handler
169
+
170
+ ### Basic Auth
171
+
172
+ Supports `Authorization: Basic <base64(username:password)>`.
173
+
174
+ ```typescript
175
+ import {
176
+ createApiEndpointHandler,
177
+ createServer,
178
+ } from "transit-kit/server";
179
+ import { createBasicAuthSchema } from "transit-kit/server/security/basicAuth";
180
+ import z from "zod";
181
+
182
+ // Define the shape of the authenticated caller
183
+ type Caller = { id: string; role: "admin" | "user" };
184
+
185
+ // Your validation logic (DB lookup, etc.)
186
+ const basicAuth = createBasicAuthSchema<Caller>(
187
+ "basic",
188
+ async (username, password) => {
189
+ const user = await findUserByUsername(username);
190
+ if (!user) return null;
191
+ const ok = await verifyPassword(password, user.passwordHash);
192
+ return ok ? { id: user.id, role: user.role } : null;
193
+ },
194
+ );
195
+
196
+ const getProfile = createApiEndpointHandler(
197
+ {
198
+ meta: { name: "Get Profile", description: "Returns caller profile", group: "Auth" },
199
+ method: "get",
200
+ path: "/me",
201
+ responseSchemas: {
202
+ 200: {
203
+ dataType: "application/json",
204
+ dataSchema: z.object({ id: z.string(), role: z.enum(["admin", "user"]) }),
205
+ },
206
+ 401: {},
207
+ },
208
+ // Enable authentication for this endpoint
209
+ securitySchemes: [basicAuth],
210
+ },
211
+ async (_req, { caller }) => {
212
+ // `caller` is typed from your security scheme
213
+ return { code: 200, dataType: "application/json", json: caller };
214
+ },
215
+ );
216
+
217
+ const server = createServer({ port: 3000, inDevMode: true, logger: true });
218
+ server.registerApiEndpoint(getProfile);
219
+ server.start();
220
+ ```
221
+
222
+ ### Bearer Token Auth
223
+
224
+ Supports `Authorization: Bearer <token>`.
225
+
226
+ ```typescript
227
+ import { createBearerAuthSchema } from "transit-kit/server/security/bearerAuth";
228
+
229
+ type Caller = { id: string; scopes: string[] };
230
+
231
+ const bearerAuth = createBearerAuthSchema<Caller>(
232
+ "bearer",
233
+ async (token) => {
234
+ const payload = await verifyJwt(token); // or call your token introspection service
235
+ return payload ? { id: payload.sub, scopes: payload.scopes } : null;
236
+ },
237
+ );
238
+
239
+ const getSecret = createApiEndpointHandler(
240
+ {
241
+ meta: { name: "Get Secret", description: "Protected resource", group: "Auth" },
242
+ method: "get",
243
+ path: "/secret",
244
+ responseSchemas: {
245
+ 200: { dataType: "application/json", dataSchema: z.object({ secret: z.string() }) },
246
+ 401: {},
247
+ },
248
+ securitySchemes: [bearerAuth],
249
+ },
250
+ async (_req, { caller }) => {
251
+ // Use `caller.scopes` for authorization decisions
252
+ return { code: 200, dataType: "application/json", json: { secret: "shh" } };
253
+ },
254
+ );
255
+ ```
256
+
257
+ ### Multiple Schemes per Endpoint
258
+
259
+ You can allow multiple authentication methods on the same endpoint. The framework tries them in the order you specify and picks the first successful scheme.
260
+
261
+ ```typescript
262
+ const endpoint = createApiEndpointHandler(
263
+ {
264
+ meta: { name: "Hybrid Auth", description: "Basic or Bearer", group: "Auth" },
265
+ method: "get",
266
+ path: "/hybrid",
267
+ responseSchemas: { 200: { dataType: "application/json", dataSchema: z.object({ ok: z.boolean() }) }, 401: {} },
268
+ securitySchemes: [basicAuth, bearerAuth],
269
+ },
270
+ async (_req, { caller }) => {
271
+ // `caller` resolves from whichever scheme authenticated successfully
272
+ return { code: 200, dataType: "application/json", json: { ok: true } };
273
+ },
274
+ );
275
+ ```
276
+
277
+ ### How It Works
278
+
279
+ - Authentication middleware is built automatically per-endpoint when `securitySchemes` are present.
280
+ - Internals use `authenticate()` from [src/server/security/SecuritySchema.ts](src/server/security/SecuritySchema.ts) to try each scheme.
281
+ - Basic auth reads and decodes the header in [src/server/security/basicAuth.ts](src/server/security/basicAuth.ts).
282
+ - Bearer auth reads the header in [src/server/security/bearerAuth.ts](src/server/security/bearerAuth.ts).
283
+ - On success, the authenticated `caller` is stored and passed to your handler as `extractedRequestData.caller`.
284
+ - On failure, the framework responds with `401` and a JSON `{ message: "Unauthorized" }` from [src/server/middleware/auth.ts](src/server/middleware/auth.ts).
285
+
161
286
  ## Response Types
162
287
 
163
288
  ### JSON Response
@@ -1,5 +1,6 @@
1
1
  export { createApiEndpointHandler } from "./handlers/api/createApiHandler";
2
- export { buildAuthenticationMiddleware } from "./middleware/auth";
2
+ export { createBasicAuthSchema } from "./security/basicAuth";
3
+ export { createBearerAuthSchema } from "./security/bearerAuth";
3
4
  export { createServer, type Server, type ServerConfig } from "./server";
4
5
  declare const _default: {};
5
6
  export default _default;
@@ -1,4 +1,5 @@
1
1
  export { createApiEndpointHandler } from "./handlers/api/createApiHandler";
2
- export { buildAuthenticationMiddleware } from "./middleware/auth";
2
+ export { createBasicAuthSchema } from "./security/basicAuth";
3
+ export { createBearerAuthSchema } from "./security/bearerAuth";
3
4
  export { createServer } from "./server";
4
5
  export default {};
@@ -1,10 +1,13 @@
1
1
  import expressAsyncHandler from "express-async-handler";
2
+ import { HttpStatusCodes } from "../constants/HttpStatusCodes";
2
3
  import { authenticate } from "../security/SecuritySchema";
3
4
  export function buildAuthenticationMiddleware(schemes) {
4
5
  return expressAsyncHandler(async (request, response, next) => {
5
6
  const caller = await authenticate(schemes, request);
6
7
  if (caller == null) {
7
- response.status(401).json({ message: "Unauthorized" });
8
+ response
9
+ .status(HttpStatusCodes.Unauthorized_401)
10
+ .json({ message: "Unauthorized" });
8
11
  return;
9
12
  }
10
13
  response.locals.caller = caller;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "transit-kit",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "A declarative TypeScript framework for building type-safe Express.js APIs with automatic OpenAPI generation",
5
5
  "keywords": [
6
6
  "express",