@authrim/server 0.1.0

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.
Files changed (60) hide show
  1. package/README.md +610 -0
  2. package/dist/adapters/express.cjs +3 -0
  3. package/dist/adapters/express.cjs.map +1 -0
  4. package/dist/adapters/express.d.cts +75 -0
  5. package/dist/adapters/express.d.ts +75 -0
  6. package/dist/adapters/express.js +3 -0
  7. package/dist/adapters/express.js.map +1 -0
  8. package/dist/adapters/fastify.cjs +3 -0
  9. package/dist/adapters/fastify.cjs.map +1 -0
  10. package/dist/adapters/fastify.d.cts +101 -0
  11. package/dist/adapters/fastify.d.ts +101 -0
  12. package/dist/adapters/fastify.js +3 -0
  13. package/dist/adapters/fastify.js.map +1 -0
  14. package/dist/adapters/hono.cjs +2 -0
  15. package/dist/adapters/hono.cjs.map +1 -0
  16. package/dist/adapters/hono.d.cts +85 -0
  17. package/dist/adapters/hono.d.ts +85 -0
  18. package/dist/adapters/hono.js +2 -0
  19. package/dist/adapters/hono.js.map +1 -0
  20. package/dist/adapters/koa.cjs +3 -0
  21. package/dist/adapters/koa.cjs.map +1 -0
  22. package/dist/adapters/koa.d.cts +75 -0
  23. package/dist/adapters/koa.d.ts +75 -0
  24. package/dist/adapters/koa.js +3 -0
  25. package/dist/adapters/koa.js.map +1 -0
  26. package/dist/adapters/nestjs.cjs +3 -0
  27. package/dist/adapters/nestjs.cjs.map +1 -0
  28. package/dist/adapters/nestjs.d.cts +126 -0
  29. package/dist/adapters/nestjs.d.ts +126 -0
  30. package/dist/adapters/nestjs.js +3 -0
  31. package/dist/adapters/nestjs.js.map +1 -0
  32. package/dist/chunk-7POGA5LZ.cjs +3 -0
  33. package/dist/chunk-7POGA5LZ.cjs.map +1 -0
  34. package/dist/chunk-N3ONRO35.js +2 -0
  35. package/dist/chunk-N3ONRO35.js.map +1 -0
  36. package/dist/chunk-O2ALCNXB.cjs +2 -0
  37. package/dist/chunk-O2ALCNXB.cjs.map +1 -0
  38. package/dist/chunk-OS567YCE.js +3 -0
  39. package/dist/chunk-OS567YCE.js.map +1 -0
  40. package/dist/chunk-TPROSFE7.cjs +2 -0
  41. package/dist/chunk-TPROSFE7.cjs.map +1 -0
  42. package/dist/chunk-XOFM2JHF.js +2 -0
  43. package/dist/chunk-XOFM2JHF.js.map +1 -0
  44. package/dist/config-I0GIVJA_.d.cts +364 -0
  45. package/dist/config-I0GIVJA_.d.ts +364 -0
  46. package/dist/index.cjs +3 -0
  47. package/dist/index.cjs.map +1 -0
  48. package/dist/index.d.cts +791 -0
  49. package/dist/index.d.ts +791 -0
  50. package/dist/index.js +3 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/providers/index.cjs +2 -0
  53. package/dist/providers/index.cjs.map +1 -0
  54. package/dist/providers/index.d.cts +79 -0
  55. package/dist/providers/index.d.ts +79 -0
  56. package/dist/providers/index.js +2 -0
  57. package/dist/providers/index.js.map +1 -0
  58. package/dist/types-CzpMdWFR.d.cts +435 -0
  59. package/dist/types-D7gjcvs9.d.ts +435 -0
  60. package/package.json +119 -0
package/README.md ADDED
@@ -0,0 +1,610 @@
1
+ # @authrim/server
2
+
3
+ Official Authrim Server SDK for OAuth 2.0 / OpenID Connect resource server implementation.
4
+
5
+ This SDK is part of the [Authrim](https://github.com/authrim) identity platform, providing token validation, DPoP support, and framework middleware for server-side applications.
6
+
7
+ ## Features
8
+
9
+ - **JWT Access Token Validation** - Signature verification and claims validation (RFC 7519)
10
+ - **JWKS Management** - Automatic key fetching, caching, and rotation
11
+ - **DPoP Support** - RFC 9449 compliant proof validation
12
+ - **Token Introspection** - RFC 7662 support
13
+ - **Token Revocation** - RFC 7009 support
14
+ - **Back-Channel Logout** - OIDC Back-Channel Logout 1.0 support
15
+ - **Framework Middleware** - Express, Fastify, Hono, Koa, NestJS
16
+ - **SCIM 2.0** - User and Group provisioning (RFC 7643/7644)
17
+ - **Verifiable Credentials** - OpenID4VCI and OpenID4VP support
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install @authrim/server
23
+ # or
24
+ pnpm add @authrim/server
25
+ # or
26
+ yarn add @authrim/server
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```typescript
32
+ import { AuthrimServer } from '@authrim/server';
33
+
34
+ const server = new AuthrimServer({
35
+ issuer: 'https://auth.example.com',
36
+ audience: 'https://api.example.com',
37
+ });
38
+
39
+ await server.init();
40
+
41
+ // Validate a token
42
+ const result = await server.validateToken(accessToken);
43
+ if (result.data) {
44
+ console.log('User:', result.data.claims.sub);
45
+ console.log('Token Type:', result.data.tokenType); // 'Bearer' or 'DPoP'
46
+ } else {
47
+ console.error('Error:', result.error.code, result.error.message);
48
+ }
49
+ ```
50
+
51
+ ## Runtime Support
52
+
53
+ This SDK uses Web Standard APIs (fetch, crypto.subtle) and runs on:
54
+
55
+ - Node.js 18+
56
+ - Bun
57
+ - Deno
58
+ - Cloudflare Workers
59
+ - Vercel Edge Functions
60
+
61
+ ## Configuration
62
+
63
+ ```typescript
64
+ const server = new AuthrimServer({
65
+ // Required
66
+ issuer: 'https://auth.example.com', // Expected token issuer
67
+ audience: 'https://api.example.com', // Expected audience (string or string[])
68
+
69
+ // JWKS (one of these is required)
70
+ jwksUri: 'https://auth.example.com/.well-known/jwks.json', // Explicit JWKS URI
71
+ // or omit to use OpenID Discovery (issuer + /.well-known/openid-configuration)
72
+
73
+ // Optional: Token operations
74
+ introspectionEndpoint: 'https://auth.example.com/oauth/introspect',
75
+ revocationEndpoint: 'https://auth.example.com/oauth/revoke',
76
+ clientCredentials: {
77
+ clientId: 'resource-server',
78
+ clientSecret: 'secret',
79
+ },
80
+
81
+ // Optional: Timing
82
+ clockToleranceSeconds: 60, // Clock skew tolerance (default: 60)
83
+ jwksRefreshIntervalMs: 3600000, // JWKS cache TTL (default: 1 hour)
84
+
85
+ // Optional: Security
86
+ requireHttps: true, // Require HTTPS for all endpoints (default: true)
87
+
88
+ // Optional: Custom providers (for testing/customization)
89
+ http: customHttpProvider,
90
+ crypto: customCryptoProvider,
91
+ clock: customClockProvider,
92
+ });
93
+ ```
94
+
95
+ ## Framework Middleware
96
+
97
+ ### Express
98
+
99
+ ```typescript
100
+ import express from 'express';
101
+ import { AuthrimServer } from '@authrim/server';
102
+ import { authrimMiddleware } from '@authrim/server/adapters/express';
103
+
104
+ const app = express();
105
+ const server = new AuthrimServer({
106
+ issuer: 'https://auth.example.com',
107
+ audience: 'https://api.example.com',
108
+ });
109
+
110
+ await server.init();
111
+
112
+ app.use('/api', authrimMiddleware(server));
113
+
114
+ app.get('/api/protected', (req, res) => {
115
+ res.json({ user: req.auth.claims.sub });
116
+ });
117
+ ```
118
+
119
+ ### Fastify
120
+
121
+ ```typescript
122
+ import Fastify from 'fastify';
123
+ import { AuthrimServer } from '@authrim/server';
124
+ import { authrimPreHandler } from '@authrim/server/adapters/fastify';
125
+
126
+ const fastify = Fastify();
127
+ const server = new AuthrimServer({
128
+ issuer: 'https://auth.example.com',
129
+ audience: 'https://api.example.com',
130
+ });
131
+
132
+ await server.init();
133
+
134
+ fastify.addHook('preHandler', authrimPreHandler(server));
135
+
136
+ fastify.get('/api/protected', async (request) => {
137
+ return { user: request.auth.claims.sub };
138
+ });
139
+ ```
140
+
141
+ ### Hono
142
+
143
+ ```typescript
144
+ import { Hono } from 'hono';
145
+ import { AuthrimServer } from '@authrim/server';
146
+ import { authrimMiddleware, getAuth } from '@authrim/server/adapters/hono';
147
+
148
+ const app = new Hono();
149
+ const server = new AuthrimServer({
150
+ issuer: 'https://auth.example.com',
151
+ audience: 'https://api.example.com',
152
+ });
153
+
154
+ await server.init();
155
+
156
+ app.use('/api/*', authrimMiddleware(server));
157
+
158
+ app.get('/api/protected', (c) => {
159
+ const auth = getAuth(c);
160
+ return c.json({ user: auth?.claims.sub });
161
+ });
162
+ ```
163
+
164
+ ### Koa
165
+
166
+ ```typescript
167
+ import Koa from 'koa';
168
+ import { AuthrimServer } from '@authrim/server';
169
+ import { authrimMiddleware } from '@authrim/server/adapters/koa';
170
+
171
+ const app = new Koa();
172
+ const server = new AuthrimServer({
173
+ issuer: 'https://auth.example.com',
174
+ audience: 'https://api.example.com',
175
+ });
176
+
177
+ await server.init();
178
+
179
+ app.use(authrimMiddleware(server));
180
+
181
+ app.use((ctx) => {
182
+ ctx.body = { user: ctx.state.auth?.claims.sub };
183
+ });
184
+ ```
185
+
186
+ ### NestJS
187
+
188
+ ```typescript
189
+ import { Controller, Get, UseGuards } from '@nestjs/common';
190
+ import { HttpException } from '@nestjs/common';
191
+ import { AuthrimServer } from '@authrim/server';
192
+ import { createAuthrimGuard, Auth } from '@authrim/server/adapters/nestjs';
193
+ import type { ValidatedToken } from '@authrim/server';
194
+
195
+ const server = new AuthrimServer({
196
+ issuer: 'https://auth.example.com',
197
+ audience: 'https://api.example.com',
198
+ });
199
+
200
+ const AuthrimGuard = createAuthrimGuard(server, HttpException);
201
+
202
+ @Controller('api')
203
+ export class AppController {
204
+ @Get('protected')
205
+ @UseGuards(AuthrimGuard)
206
+ getProtected(@Auth() auth: ValidatedToken) {
207
+ return { user: auth.claims.sub };
208
+ }
209
+ }
210
+ ```
211
+
212
+ ## DPoP Support
213
+
214
+ DPoP (Demonstrating Proof of Possession) binds access tokens to a specific client key pair.
215
+
216
+ ```typescript
217
+ import { AuthrimServer, DPoPValidator } from '@authrim/server';
218
+
219
+ const server = new AuthrimServer({
220
+ issuer: 'https://auth.example.com',
221
+ audience: 'https://api.example.com',
222
+ });
223
+
224
+ await server.init();
225
+
226
+ // 1. Validate the access token
227
+ const tokenResult = await server.validateToken(accessToken);
228
+ if (tokenResult.error) {
229
+ // Handle error
230
+ }
231
+
232
+ // 2. Check if token is DPoP-bound
233
+ if (tokenResult.data.tokenType === 'DPoP') {
234
+ // 3. Validate DPoP proof
235
+ const dpopResult = await server.validateDPoP(dpopProof, {
236
+ method: 'GET',
237
+ uri: 'https://api.example.com/resource',
238
+ accessToken: accessToken,
239
+ expectedThumbprint: tokenResult.data.claims.cnf?.jkt,
240
+ });
241
+
242
+ if (!dpopResult.valid) {
243
+ // DPoP proof invalid
244
+ return { error: dpopResult.errorCode };
245
+ }
246
+ }
247
+ ```
248
+
249
+ ### DPoP with Nonce
250
+
251
+ ```typescript
252
+ const dpopResult = await server.validateDPoP(dpopProof, {
253
+ method: 'POST',
254
+ uri: 'https://api.example.com/token',
255
+ expectedNonce: serverProvidedNonce,
256
+ });
257
+
258
+ if (dpopResult.errorCode === 'dpop_nonce_required') {
259
+ // Client should retry with the nonce
260
+ }
261
+ ```
262
+
263
+ ## Token Introspection
264
+
265
+ ```typescript
266
+ const server = new AuthrimServer({
267
+ issuer: 'https://auth.example.com',
268
+ audience: 'https://api.example.com',
269
+ introspectionEndpoint: 'https://auth.example.com/oauth/introspect',
270
+ clientCredentials: {
271
+ clientId: 'resource-server',
272
+ clientSecret: 'secret',
273
+ },
274
+ });
275
+
276
+ await server.init();
277
+
278
+ const result = await server.introspect(token);
279
+ if (result.active) {
280
+ console.log('Token is active');
281
+ console.log('Subject:', result.sub);
282
+ console.log('Scope:', result.scope);
283
+ }
284
+ ```
285
+
286
+ ## Token Revocation
287
+
288
+ ```typescript
289
+ const server = new AuthrimServer({
290
+ issuer: 'https://auth.example.com',
291
+ audience: 'https://api.example.com',
292
+ revocationEndpoint: 'https://auth.example.com/oauth/revoke',
293
+ clientCredentials: {
294
+ clientId: 'resource-server',
295
+ clientSecret: 'secret',
296
+ },
297
+ });
298
+
299
+ await server.init();
300
+
301
+ // Revoke access token
302
+ await server.revoke(accessToken);
303
+
304
+ // Revoke refresh token
305
+ await server.revoke(refreshToken, 'refresh_token');
306
+ ```
307
+
308
+ ## Back-Channel Logout
309
+
310
+ Handle logout tokens sent by the Authorization Server via back-channel.
311
+
312
+ ```typescript
313
+ import { BackChannelLogoutValidator } from '@authrim/server';
314
+
315
+ const validator = new BackChannelLogoutValidator();
316
+
317
+ // Endpoint to receive logout tokens
318
+ app.post('/backchannel-logout', async (req, res) => {
319
+ const logoutToken = req.body.logout_token;
320
+
321
+ // 1. Validate logout token claims (synchronous)
322
+ const result = validator.validate(logoutToken, {
323
+ issuer: 'https://auth.example.com',
324
+ audience: 'https://api.example.com',
325
+ clockToleranceSeconds: 60,
326
+ now: Math.floor(Date.now() / 1000),
327
+ });
328
+
329
+ if (!result.valid) {
330
+ return res.status(400).json({ error: result.error.code });
331
+ }
332
+
333
+ // 2. Verify signature using JWKS (application responsibility)
334
+ // 3. Check jti for replay (application responsibility)
335
+
336
+ // 4. Terminate sessions
337
+ const { sub, sid } = result.claims;
338
+ if (sid) {
339
+ await terminateSession(sid);
340
+ } else if (sub) {
341
+ await terminateAllUserSessions(sub);
342
+ }
343
+
344
+ res.status(200).end();
345
+ });
346
+ ```
347
+
348
+ ## SCIM 2.0 Provisioning
349
+
350
+ ```typescript
351
+ import { ScimClient } from '@authrim/server';
352
+
353
+ const scim = new ScimClient({
354
+ baseUrl: 'https://api.example.com/scim/v2',
355
+ accessToken: token,
356
+ });
357
+
358
+ // Create user
359
+ const user = await scim.createUser({
360
+ userName: 'john@example.com',
361
+ name: { givenName: 'John', familyName: 'Doe' },
362
+ emails: [{ value: 'john@example.com', primary: true }],
363
+ active: true,
364
+ });
365
+
366
+ // Get user
367
+ const fetchedUser = await scim.getUser(user.id);
368
+
369
+ // Update user
370
+ await scim.updateUser(user.id, {
371
+ ...user,
372
+ active: false,
373
+ });
374
+
375
+ // List users with filter
376
+ const users = await scim.listUsers({
377
+ filter: 'userName eq "john@example.com"',
378
+ startIndex: 1,
379
+ count: 10,
380
+ });
381
+
382
+ // Delete user
383
+ await scim.deleteUser(user.id);
384
+
385
+ // Group operations
386
+ const group = await scim.createGroup({
387
+ displayName: 'Admins',
388
+ members: [{ value: user.id }],
389
+ });
390
+ ```
391
+
392
+ ## Error Handling
393
+
394
+ All validation methods return a result object with `data` or `error`:
395
+
396
+ ```typescript
397
+ const result = await server.validateToken(token);
398
+
399
+ if (result.error) {
400
+ // Handle specific error codes
401
+ switch (result.error.code) {
402
+ case 'token_expired':
403
+ // Token has expired
404
+ break;
405
+ case 'invalid_issuer':
406
+ // Wrong issuer
407
+ break;
408
+ case 'invalid_audience':
409
+ // Wrong audience
410
+ break;
411
+ case 'signature_invalid':
412
+ // Signature verification failed
413
+ break;
414
+ case 'jwks_key_not_found':
415
+ // Key not found in JWKS
416
+ break;
417
+ default:
418
+ // Other error
419
+ }
420
+
421
+ // HTTP status for response
422
+ const httpStatus = result.error.httpStatus; // 401, 503, etc.
423
+ }
424
+ ```
425
+
426
+ ### Error Codes
427
+
428
+ | Code | Description | HTTP Status |
429
+ |------|-------------|-------------|
430
+ | `token_malformed` | Token is not a valid JWT | 401 |
431
+ | `token_expired` | Token has expired | 401 |
432
+ | `invalid_issuer` | Issuer doesn't match expected | 401 |
433
+ | `invalid_audience` | Audience doesn't match expected | 401 |
434
+ | `signature_invalid` | Signature verification failed | 401 |
435
+ | `jwks_key_not_found` | Signing key not in JWKS | 401 |
436
+ | `jwks_fetch_failed` | Failed to fetch JWKS | 503 |
437
+ | `dpop_proof_missing` | DPoP proof required but missing | 401 |
438
+ | `dpop_proof_invalid` | DPoP proof validation failed | 401 |
439
+ | `dpop_nonce_required` | Server nonce required | 401 |
440
+
441
+ ## Provider Injection
442
+
443
+ All dependencies are injectable for testing and customization:
444
+
445
+ ```typescript
446
+ import { AuthrimServer } from '@authrim/server';
447
+ import {
448
+ fetchHttpProvider,
449
+ webCryptoProvider,
450
+ systemClock,
451
+ memoryCache
452
+ } from '@authrim/server/providers';
453
+
454
+ const server = new AuthrimServer({
455
+ issuer: 'https://auth.example.com',
456
+ audience: 'https://api.example.com',
457
+
458
+ // Custom providers
459
+ http: fetchHttpProvider(),
460
+ crypto: webCryptoProvider(),
461
+ clock: systemClock(),
462
+ jwksCache: memoryCache({ ttlMs: 3600_000 }),
463
+ });
464
+ ```
465
+
466
+ ### Testing with Mocks
467
+
468
+ ```typescript
469
+ import { vi } from 'vitest';
470
+
471
+ const mockClock = {
472
+ nowMs: () => 1700000000000,
473
+ nowSeconds: () => 1700000000,
474
+ };
475
+
476
+ const mockCrypto = {
477
+ verifySignature: vi.fn().mockResolvedValue(true),
478
+ importJwk: vi.fn().mockResolvedValue({} as CryptoKey),
479
+ sha256: vi.fn().mockResolvedValue(new Uint8Array(32)),
480
+ calculateThumbprint: vi.fn().mockResolvedValue('thumbprint'),
481
+ };
482
+
483
+ const server = new AuthrimServer({
484
+ issuer: 'https://auth.example.com',
485
+ audience: 'https://api.example.com',
486
+ jwksUri: 'https://auth.example.com/.well-known/jwks.json',
487
+ crypto: mockCrypto,
488
+ clock: mockClock,
489
+ });
490
+ ```
491
+
492
+ ## Security Considerations
493
+
494
+ ### HTTPS Enforcement
495
+
496
+ By default, all endpoints must use HTTPS. Disable only for development:
497
+
498
+ ```typescript
499
+ const server = new AuthrimServer({
500
+ issuer: 'http://localhost:8080', // Will throw error
501
+ requireHttps: false, // Allow HTTP (development only!)
502
+ });
503
+ ```
504
+
505
+ ### Timing-Safe Comparisons
506
+
507
+ This SDK uses constant-time string comparisons for security-sensitive values:
508
+ - JWT issuer and audience validation
509
+ - DPoP thumbprint binding verification
510
+ - Back-channel logout subject and session ID validation
511
+
512
+ ### JTI Replay Protection
513
+
514
+ For DPoP proofs and logout tokens, `jti` (JWT ID) uniqueness checking is the **application's responsibility**. Implement a cache with TTL:
515
+
516
+ ```typescript
517
+ const jtiCache = new Map<string, number>();
518
+ const JTI_LIFETIME_SECONDS = 120;
519
+
520
+ function checkJtiUniqueness(jti: string): boolean {
521
+ const now = Math.floor(Date.now() / 1000);
522
+
523
+ // Clean expired entries
524
+ for (const [key, exp] of jtiCache) {
525
+ if (exp < now) jtiCache.delete(key);
526
+ }
527
+
528
+ if (jtiCache.has(jti)) {
529
+ return false; // Replay detected
530
+ }
531
+
532
+ jtiCache.set(jti, now + JTI_LIFETIME_SECONDS);
533
+ return true;
534
+ }
535
+ ```
536
+
537
+ ### JWKS Security
538
+
539
+ - Cross-origin redirects are blocked by default
540
+ - Cache-Control headers are respected (max 24 hours)
541
+ - Single-flight pattern prevents thundering herd
542
+
543
+ ## API Reference
544
+
545
+ ### AuthrimServer
546
+
547
+ | Method | Description |
548
+ |--------|-------------|
549
+ | `init()` | Initialize the server (fetch JWKS if needed) |
550
+ | `validateToken(token)` | Validate a JWT access token |
551
+ | `validateDPoP(proof, options)` | Validate a DPoP proof |
552
+ | `introspect(token)` | Introspect a token (RFC 7662) |
553
+ | `revoke(token, tokenTypeHint?)` | Revoke a token (RFC 7009) |
554
+ | `invalidateJwksCache()` | Force JWKS cache refresh |
555
+
556
+ ### BackChannelLogoutValidator
557
+
558
+ | Method | Description |
559
+ |--------|-------------|
560
+ | `validate(token, options)` | Validate logout token claims |
561
+
562
+ ### ScimClient
563
+
564
+ | Method | Description |
565
+ |--------|-------------|
566
+ | `createUser(user)` | Create a new user |
567
+ | `getUser(id)` | Get user by ID |
568
+ | `updateUser(id, user)` | Replace user |
569
+ | `patchUser(id, operations)` | Patch user |
570
+ | `deleteUser(id)` | Delete user |
571
+ | `listUsers(options?)` | List/search users |
572
+ | `createGroup(group)` | Create a new group |
573
+ | `getGroup(id)` | Get group by ID |
574
+ | `updateGroup(id, group)` | Replace group |
575
+ | `patchGroup(id, operations)` | Patch group |
576
+ | `deleteGroup(id)` | Delete group |
577
+ | `listGroups(options?)` | List/search groups |
578
+
579
+ ## RFC Compliance
580
+
581
+ | Specification | Status |
582
+ |---------------|--------|
583
+ | RFC 7519 - JWT | Implemented |
584
+ | RFC 7517 - JWK | Implemented |
585
+ | RFC 7638 - JWK Thumbprint | Implemented |
586
+ | RFC 7662 - Token Introspection | Implemented |
587
+ | RFC 7009 - Token Revocation | Implemented |
588
+ | RFC 9449 - DPoP | Implemented |
589
+ | RFC 7643/7644 - SCIM 2.0 | Implemented |
590
+ | OIDC Core 1.0 | Implemented |
591
+ | OIDC Back-Channel Logout 1.0 | Implemented |
592
+
593
+ ## Authrim SDK Family
594
+
595
+ This SDK is part of the Authrim identity platform:
596
+
597
+ | Package | Description |
598
+ |---------|-------------|
599
+ | `@authrim/core` | Client-side SDK for OAuth 2.0 / OIDC flows |
600
+ | `@authrim/server` | Server-side SDK for token validation (this package) |
601
+
602
+ ## jose Dependency
603
+
604
+ This SDK uses `jose` for type definitions. Cryptographic operations use the native Web Crypto API. Other Authrim language SDKs may use different libraries (e.g., Nimbus for Java, go-jose for Go, Microsoft.IdentityModel for .NET) as long as the same verification steps are followed.
605
+
606
+ ## License
607
+
608
+ Apache-2.0
609
+
610
+ Copyright (c) Authrim
@@ -0,0 +1,3 @@
1
+ 'use strict';var chunkO2ALCNXB_cjs=require('../chunk-O2ALCNXB.cjs'),chunkTPROSFE7_cjs=require('../chunk-TPROSFE7.cjs');function x(n,s={}){return async(e,d,o)=>{let u=e.get("host")??"localhost",i=`${e.protocol}://${u}${e.originalUrl}`,r=await chunkTPROSFE7_cjs.c(n,{headers:e.headers,method:e.method,url:i});if(r.error){let t=new chunkTPROSFE7_cjs.a(r.error.code,r.error.message),c=chunkO2ALCNXB_cjs.c(t,{realm:s.realm,scheme:"Bearer"});s.onError&&s.onError(r.error),d.status(r.error.httpStatus).set(c).json(chunkO2ALCNXB_cjs.a(t));return}e.auth=r.data.claims,e.authTokenType=r.data.tokenType,o();}}function E(n,s={}){return async(e,d,o)=>{if(!e.headers.authorization){o();return}let i=e.get("host")??"localhost",r=`${e.protocol}://${i}${e.originalUrl}`,t=await chunkTPROSFE7_cjs.c(n,{headers:e.headers,method:e.method,url:r});t.data&&(e.auth=t.data.claims,e.authTokenType=t.data.tokenType),o();}}
2
+ exports.authrimMiddleware=x;exports.authrimOptionalMiddleware=E;//# sourceMappingURL=express.cjs.map
3
+ //# sourceMappingURL=express.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/express.ts"],"names":["authrimMiddleware","server","options","req","res","next","host","url","result","authenticateRequest","error","AuthrimServerError","headers","buildErrorHeaders","buildErrorResponse","authrimOptionalMiddleware","_options","_res"],"mappings":"uHAwEO,SAASA,EACdC,CAAAA,CACAC,CAAAA,CAA6B,EAAC,CAC9B,CACA,OAAO,MACLC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,GACkB,CAElB,IAAMC,CAAAA,CAAOH,EAAI,GAAA,CAAI,MAAM,GAAK,WAAA,CAC1BI,CAAAA,CAAM,CAAA,EAAGJ,CAAAA,CAAI,QAAQ,CAAA,GAAA,EAAMG,CAAI,GAAGH,CAAAA,CAAI,WAAW,GAGjDK,CAAAA,CAAS,MAAMC,oBAAoBR,CAAAA,CAAQ,CAC/C,QAASE,CAAAA,CAAI,OAAA,CACb,OAAQA,CAAAA,CAAI,MAAA,CACZ,IAAAI,CACF,CAAC,CAAA,CAED,GAAIC,EAAO,KAAA,CAAO,CAChB,IAAME,CAAAA,CAAQ,IAAIC,oBAChBH,CAAAA,CAAO,KAAA,CAAM,IAAA,CACbA,CAAAA,CAAO,MAAM,OACf,CAAA,CAEMI,EAAUC,mBAAAA,CAAkBH,CAAAA,CAAO,CACvC,KAAA,CAAOR,CAAAA,CAAQ,KAAA,CACf,MAAA,CAAQ,QACV,CAAC,CAAA,CAEGA,EAAQ,OAAA,EACVA,CAAAA,CAAQ,QAAQM,CAAAA,CAAO,KAAK,EAG9BJ,CAAAA,CAAI,MAAA,CAAOI,EAAO,KAAA,CAAM,UAAU,EAAE,GAAA,CAAII,CAAO,EAAE,IAAA,CAAKE,mBAAAA,CAAmBJ,CAAK,CAAC,EAC/E,MACF,CAGAP,EAAI,IAAA,CAAOK,CAAAA,CAAO,KAAK,MAAA,CACvBL,CAAAA,CAAI,aAAA,CAAgBK,CAAAA,CAAO,KAAK,SAAA,CAEhCH,CAAAA,GACF,CACF,CASO,SAASU,CAAAA,CACdd,CAAAA,CACAe,CAAAA,CAA8B,GAC9B,CACA,aACEb,CAAAA,CACAc,CAAAA,CACAZ,IACkB,CAGlB,GAAI,CADeF,CAAAA,CAAI,OAAA,CAAQ,cACd,CACfE,CAAAA,GACA,MACF,CAGA,IAAMC,CAAAA,CAAOH,CAAAA,CAAI,GAAA,CAAI,MAAM,GAAK,WAAA,CAC1BI,CAAAA,CAAM,GAAGJ,CAAAA,CAAI,QAAQ,MAAMG,CAAI,CAAA,EAAGH,CAAAA,CAAI,WAAW,GAGjDK,CAAAA,CAAS,MAAMC,oBAAoBR,CAAAA,CAAQ,CAC/C,QAASE,CAAAA,CAAI,OAAA,CACb,MAAA,CAAQA,CAAAA,CAAI,OACZ,GAAA,CAAAI,CACF,CAAC,CAAA,CAEGC,CAAAA,CAAO,OACTL,CAAAA,CAAI,IAAA,CAAOK,EAAO,IAAA,CAAK,MAAA,CACvBL,EAAI,aAAA,CAAgBK,CAAAA,CAAO,KAAK,SAAA,CAAA,CAGlCH,CAAAA,GACF,CACF","file":"express.cjs","sourcesContent":["/**\r\n * Express Adapter\r\n *\r\n * Thin wrapper around authenticateRequest for Express framework.\r\n */\r\n\r\nimport type { AuthrimServer } from '../core/client.js';\r\nimport type { MiddlewareOptions } from '../middleware/types.js';\r\nimport type { ValidatedToken } from '../types/claims.js';\r\nimport { authenticateRequest } from '../middleware/authenticate.js';\r\nimport { buildErrorResponse, buildErrorHeaders } from '../utils/error-response.js';\r\nimport { AuthrimServerError } from '../types/errors.js';\r\n\r\n/**\r\n * Extended Express Request with auth property\r\n */\r\nexport interface AuthrimExpressRequest {\r\n auth?: ValidatedToken;\r\n authTokenType?: 'Bearer' | 'DPoP';\r\n}\r\n\r\n/**\r\n * Express request type (minimal interface)\r\n */\r\ninterface ExpressRequest {\r\n headers: Record<string, string | string[] | undefined>;\r\n method: string;\r\n protocol: string;\r\n get(name: string): string | undefined;\r\n originalUrl: string;\r\n}\r\n\r\n/**\r\n * Express response type (minimal interface)\r\n */\r\ninterface ExpressResponse {\r\n status(code: number): ExpressResponse;\r\n set(headers: Record<string, string>): ExpressResponse;\r\n json(body: unknown): void;\r\n}\r\n\r\n/**\r\n * Express next function\r\n */\r\ntype ExpressNextFunction = (err?: unknown) => void;\r\n\r\n/**\r\n * Create Express middleware for token validation\r\n *\r\n * @param server - AuthrimServer instance\r\n * @param options - Middleware options\r\n * @returns Express middleware function\r\n *\r\n * @example\r\n * ```typescript\r\n * import express from 'express';\r\n * import { createAuthrimServer } from '@authrim/server';\r\n * import { authrimMiddleware } from '@authrim/server/adapters/express';\r\n *\r\n * const app = express();\r\n * const server = createAuthrimServer({\r\n * issuer: 'https://auth.example.com',\r\n * audience: 'https://api.example.com',\r\n * });\r\n *\r\n * app.use('/api', authrimMiddleware(server));\r\n *\r\n * app.get('/api/protected', (req, res) => {\r\n * res.json({ user: req.auth.claims.sub });\r\n * });\r\n * ```\r\n */\r\nexport function authrimMiddleware(\r\n server: AuthrimServer,\r\n options: MiddlewareOptions = {}\r\n) {\r\n return async (\r\n req: ExpressRequest & AuthrimExpressRequest,\r\n res: ExpressResponse,\r\n next: ExpressNextFunction\r\n ): Promise<void> => {\r\n // Build URL\r\n const host = req.get('host') ?? 'localhost';\r\n const url = `${req.protocol}://${host}${req.originalUrl}`;\r\n\r\n // Authenticate\r\n const result = await authenticateRequest(server, {\r\n headers: req.headers as Record<string, string | string[] | undefined>,\r\n method: req.method,\r\n url,\r\n });\r\n\r\n if (result.error) {\r\n const error = new AuthrimServerError(\r\n result.error.code as any,\r\n result.error.message\r\n );\r\n\r\n const headers = buildErrorHeaders(error, {\r\n realm: options.realm,\r\n scheme: 'Bearer',\r\n });\r\n\r\n if (options.onError) {\r\n options.onError(result.error);\r\n }\r\n\r\n res.status(result.error.httpStatus).set(headers).json(buildErrorResponse(error));\r\n return;\r\n }\r\n\r\n // Attach auth to request\r\n req.auth = result.data.claims;\r\n req.authTokenType = result.data.tokenType;\r\n\r\n next();\r\n };\r\n}\r\n\r\n/**\r\n * Create optional auth middleware (doesn't fail if no token)\r\n *\r\n * @param server - AuthrimServer instance\r\n * @param options - Middleware options\r\n * @returns Express middleware function\r\n */\r\nexport function authrimOptionalMiddleware(\r\n server: AuthrimServer,\r\n _options: MiddlewareOptions = {}\r\n) {\r\n return async (\r\n req: ExpressRequest & AuthrimExpressRequest,\r\n _res: ExpressResponse,\r\n next: ExpressNextFunction\r\n ): Promise<void> => {\r\n // Check if Authorization header exists\r\n const authHeader = req.headers['authorization'];\r\n if (!authHeader) {\r\n next();\r\n return;\r\n }\r\n\r\n // Build URL\r\n const host = req.get('host') ?? 'localhost';\r\n const url = `${req.protocol}://${host}${req.originalUrl}`;\r\n\r\n // Authenticate\r\n const result = await authenticateRequest(server, {\r\n headers: req.headers as Record<string, string | string[] | undefined>,\r\n method: req.method,\r\n url,\r\n });\r\n\r\n if (result.data) {\r\n req.auth = result.data.claims;\r\n req.authTokenType = result.data.tokenType;\r\n }\r\n\r\n next();\r\n };\r\n}\r\n"]}
@@ -0,0 +1,75 @@
1
+ import { V as ValidatedToken, A as AuthrimServer, M as MiddlewareOptions } from '../types-CzpMdWFR.cjs';
2
+ import '../config-I0GIVJA_.cjs';
3
+
4
+ /**
5
+ * Express Adapter
6
+ *
7
+ * Thin wrapper around authenticateRequest for Express framework.
8
+ */
9
+
10
+ /**
11
+ * Extended Express Request with auth property
12
+ */
13
+ interface AuthrimExpressRequest {
14
+ auth?: ValidatedToken;
15
+ authTokenType?: 'Bearer' | 'DPoP';
16
+ }
17
+ /**
18
+ * Express request type (minimal interface)
19
+ */
20
+ interface ExpressRequest {
21
+ headers: Record<string, string | string[] | undefined>;
22
+ method: string;
23
+ protocol: string;
24
+ get(name: string): string | undefined;
25
+ originalUrl: string;
26
+ }
27
+ /**
28
+ * Express response type (minimal interface)
29
+ */
30
+ interface ExpressResponse {
31
+ status(code: number): ExpressResponse;
32
+ set(headers: Record<string, string>): ExpressResponse;
33
+ json(body: unknown): void;
34
+ }
35
+ /**
36
+ * Express next function
37
+ */
38
+ type ExpressNextFunction = (err?: unknown) => void;
39
+ /**
40
+ * Create Express middleware for token validation
41
+ *
42
+ * @param server - AuthrimServer instance
43
+ * @param options - Middleware options
44
+ * @returns Express middleware function
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * import express from 'express';
49
+ * import { createAuthrimServer } from '@authrim/server';
50
+ * import { authrimMiddleware } from '@authrim/server/adapters/express';
51
+ *
52
+ * const app = express();
53
+ * const server = createAuthrimServer({
54
+ * issuer: 'https://auth.example.com',
55
+ * audience: 'https://api.example.com',
56
+ * });
57
+ *
58
+ * app.use('/api', authrimMiddleware(server));
59
+ *
60
+ * app.get('/api/protected', (req, res) => {
61
+ * res.json({ user: req.auth.claims.sub });
62
+ * });
63
+ * ```
64
+ */
65
+ declare function authrimMiddleware(server: AuthrimServer, options?: MiddlewareOptions): (req: ExpressRequest & AuthrimExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => Promise<void>;
66
+ /**
67
+ * Create optional auth middleware (doesn't fail if no token)
68
+ *
69
+ * @param server - AuthrimServer instance
70
+ * @param options - Middleware options
71
+ * @returns Express middleware function
72
+ */
73
+ declare function authrimOptionalMiddleware(server: AuthrimServer, _options?: MiddlewareOptions): (req: ExpressRequest & AuthrimExpressRequest, _res: ExpressResponse, next: ExpressNextFunction) => Promise<void>;
74
+
75
+ export { type AuthrimExpressRequest, authrimMiddleware, authrimOptionalMiddleware };