@rudderjs/passport 1.1.0 → 1.1.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.
Files changed (68) hide show
  1. package/README.md +96 -15
  2. package/boost/guidelines.md +190 -0
  3. package/dist/grants/authorization-code.d.ts.map +1 -1
  4. package/dist/grants/authorization-code.js +4 -17
  5. package/dist/grants/authorization-code.js.map +1 -1
  6. package/dist/grants/client-credentials.d.ts.map +1 -1
  7. package/dist/grants/client-credentials.js +4 -17
  8. package/dist/grants/client-credentials.js.map +1 -1
  9. package/dist/grants/device-code.d.ts.map +1 -1
  10. package/dist/grants/device-code.js +2 -1
  11. package/dist/grants/device-code.js.map +1 -1
  12. package/dist/grants/parse-scopes.d.ts +15 -0
  13. package/dist/grants/parse-scopes.d.ts.map +1 -0
  14. package/dist/grants/parse-scopes.js +17 -0
  15. package/dist/grants/parse-scopes.js.map +1 -0
  16. package/dist/grants/refresh-token.d.ts.map +1 -1
  17. package/dist/grants/refresh-token.js +5 -18
  18. package/dist/grants/refresh-token.js.map +1 -1
  19. package/dist/grants/verify-client.d.ts +29 -0
  20. package/dist/grants/verify-client.d.ts.map +1 -0
  21. package/dist/grants/verify-client.js +43 -0
  22. package/dist/grants/verify-client.js.map +1 -0
  23. package/dist/middleware/bearer.d.ts.map +1 -1
  24. package/dist/middleware/bearer.js +98 -103
  25. package/dist/middleware/bearer.js.map +1 -1
  26. package/dist/models/AccessToken.d.ts +3 -3
  27. package/dist/models/AuthCode.d.ts +3 -3
  28. package/dist/models/DeviceCode.d.ts +3 -3
  29. package/dist/models/RefreshToken.d.ts +3 -3
  30. package/dist/models/helpers.d.ts +27 -9
  31. package/dist/models/helpers.d.ts.map +1 -1
  32. package/dist/models/helpers.js +12 -6
  33. package/dist/models/helpers.js.map +1 -1
  34. package/dist/personal-access-tokens.d.ts.map +1 -1
  35. package/dist/personal-access-tokens.js.map +1 -1
  36. package/dist/routes/authorize.d.ts +17 -0
  37. package/dist/routes/authorize.d.ts.map +1 -0
  38. package/dist/routes/authorize.js +107 -0
  39. package/dist/routes/authorize.js.map +1 -0
  40. package/dist/routes/device.d.ts +23 -0
  41. package/dist/routes/device.d.ts.map +1 -0
  42. package/dist/routes/device.js +69 -0
  43. package/dist/routes/device.js.map +1 -0
  44. package/dist/routes/helpers.d.ts +64 -0
  45. package/dist/routes/helpers.d.ts.map +1 -0
  46. package/dist/routes/helpers.js +154 -0
  47. package/dist/routes/helpers.js.map +1 -0
  48. package/dist/routes/revoke.d.ts +16 -0
  49. package/dist/routes/revoke.d.ts.map +1 -0
  50. package/dist/routes/revoke.js +33 -0
  51. package/dist/routes/revoke.js.map +1 -0
  52. package/dist/routes/scopes.d.ts +9 -0
  53. package/dist/routes/scopes.d.ts.map +1 -0
  54. package/dist/routes/scopes.js +13 -0
  55. package/dist/routes/scopes.js.map +1 -0
  56. package/dist/routes/token.d.ts +24 -0
  57. package/dist/routes/token.d.ts.map +1 -0
  58. package/dist/routes/token.js +121 -0
  59. package/dist/routes/token.js.map +1 -0
  60. package/dist/routes/types.d.ts +132 -0
  61. package/dist/routes/types.d.ts.map +1 -0
  62. package/dist/routes/types.js +2 -0
  63. package/dist/routes/types.js.map +1 -0
  64. package/dist/routes.d.ts +2 -120
  65. package/dist/routes.d.ts.map +1 -1
  66. package/dist/routes.js +16 -411
  67. package/dist/routes.js.map +1 -1
  68. package/package.json +7 -6
@@ -0,0 +1,121 @@
1
+ import { report } from '@rudderjs/core';
2
+ import { exchangeAuthCode, clientCredentialsGrant, refreshTokenGrant, pollDeviceCode, OAuthError, } from '../grants/index.js';
3
+ import { resolveClientCredentials } from './helpers.js';
4
+ /**
5
+ * Register `POST /oauth/token` — the OAuth 2 token endpoint.
6
+ *
7
+ * Dispatches to one of the four supported grants:
8
+ * - `authorization_code` — exchanges an auth code for tokens
9
+ * - `client_credentials` — machine-to-machine, confidential clients only
10
+ * - `refresh_token` — rotates an access+refresh pair
11
+ * - `urn:ietf:params:oauth:grant-type:device_code` — polls device flow
12
+ *
13
+ * `mw` runs ahead of the handler. The token endpoint is the canonical
14
+ * brute-force target for client_secret guessing — every production app
15
+ * SHOULD pass a per-route rate limiter here. See
16
+ * `PassportRouteOptions.tokenMiddleware` jsdoc for the recommended config.
17
+ *
18
+ * RFC 6749 §5.2 — client-auth failures (HTTP 401) are signalled with a
19
+ * `WWW-Authenticate: Basic` header alongside the body. RFC 8628 §3.5 —
20
+ * device-flow polling errors (`authorization_pending`, `slow_down`,
21
+ * `expired_token`, `access_denied`) return HTTP 400; 429 is for transport-
22
+ * level rate-limiting, not the OAuth `slow_down` signal.
23
+ */
24
+ export function registerTokenRoute(router, prefix, mw) {
25
+ router.post(`${prefix}/token`, async (req, res) => {
26
+ try {
27
+ const body = req.body ?? {};
28
+ const grantType = body['grant_type'];
29
+ // RFC 6749 §2.3.1 — confidential clients MUST be able to
30
+ // authenticate via HTTP Basic; body params are an alternative.
31
+ // §2.3 forbids using both at once. Resolve credentials once for
32
+ // all grants instead of repeating the parsing in each branch.
33
+ const credentials = resolveClientCredentials(req, body);
34
+ let result;
35
+ switch (grantType) {
36
+ case 'authorization_code':
37
+ result = await exchangeAuthCode({
38
+ grantType,
39
+ code: body['code'],
40
+ ...credentials,
41
+ redirectUri: body['redirect_uri'],
42
+ codeVerifier: body['code_verifier'],
43
+ });
44
+ break;
45
+ case 'client_credentials':
46
+ // ClientCredentialsRequest requires clientSecret (the grant
47
+ // is confidential-only by spec). Surface the missing-secret
48
+ // case as invalid_request rather than letting it surface
49
+ // downstream as "Invalid client secret."
50
+ if (credentials.clientSecret === undefined) {
51
+ throw new OAuthError('invalid_request', 'client_secret is required for the client_credentials grant.', 401);
52
+ }
53
+ result = await clientCredentialsGrant({
54
+ grantType,
55
+ clientId: credentials.clientId,
56
+ clientSecret: credentials.clientSecret,
57
+ scope: body['scope'],
58
+ });
59
+ break;
60
+ case 'refresh_token':
61
+ result = await refreshTokenGrant({
62
+ grantType,
63
+ refreshToken: body['refresh_token'],
64
+ ...credentials,
65
+ scope: body['scope'],
66
+ });
67
+ break;
68
+ case 'urn:ietf:params:oauth:grant-type:device_code': {
69
+ const pollResult = await pollDeviceCode({
70
+ grantType,
71
+ deviceCode: body['device_code'],
72
+ clientId: credentials.clientId,
73
+ });
74
+ if (pollResult.status === 'authorized') {
75
+ result = pollResult.tokens;
76
+ }
77
+ else {
78
+ // RFC 8628 §3.5 — device-flow polling errors (including
79
+ // slow_down) are §5.2-shaped errors and MUST return HTTP
80
+ // 400. 429 is for transport-level rate-limiting, not the
81
+ // OAuth `slow_down` signal.
82
+ //
83
+ // On slow_down, forward the escalated `interval` so a
84
+ // well-behaved client uses the new value instead of having
85
+ // to add 5 itself. Other variants don't need it.
86
+ if (pollResult.status === 'slow_down') {
87
+ res.status(400).json({ error: 'slow_down', interval: pollResult.interval });
88
+ }
89
+ else {
90
+ res.status(400).json({ error: pollResult.status });
91
+ }
92
+ return;
93
+ }
94
+ break;
95
+ }
96
+ default:
97
+ res.status(400).json({
98
+ error: 'unsupported_grant_type',
99
+ error_description: `Grant type "${grantType}" is not supported.`,
100
+ });
101
+ return;
102
+ }
103
+ res.json(result);
104
+ }
105
+ catch (e) {
106
+ if (e instanceof OAuthError) {
107
+ // RFC 6749 §5.2 — client-auth failures at the token endpoint
108
+ // are signalled with WWW-Authenticate alongside the 401 status.
109
+ if (e.statusCode === 401 && typeof res.header === 'function') {
110
+ res.header('WWW-Authenticate', 'Basic realm="oauth"');
111
+ }
112
+ res.status(e.statusCode).json(e.toJSON());
113
+ }
114
+ else {
115
+ report(e);
116
+ res.status(500).json({ error: 'server_error', error_description: 'Internal server error.' });
117
+ }
118
+ }
119
+ }, mw);
120
+ }
121
+ //# sourceMappingURL=token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/routes/token.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,132 @@
1
+ import type { MiddlewareHandler } from '@rudderjs/contracts';
2
+ /**
3
+ * Minimal handler signature. Kept `any`-typed because passport routes are
4
+ * mounted on arbitrary Router implementations (Hono adapter, express-style,
5
+ * test fakes) — pinning a concrete request/response type here would force
6
+ * downstream casts everywhere a handler is implemented.
7
+ */
8
+ export type RouteHandler = (req: any, res: any) => Promise<any> | any;
9
+ /**
10
+ * Minimal Router contract passport's `register*Routes()` functions accept.
11
+ * The `@rudderjs/router` instance satisfies this, as do simple test fakes —
12
+ * we don't import the concrete class here to keep this package usable from
13
+ * apps that bring their own router.
14
+ */
15
+ export interface Router {
16
+ get(path: string, handler: RouteHandler, ...middleware: any[]): void;
17
+ post(path: string, handler: RouteHandler, ...middleware: any[]): void;
18
+ delete(path: string, handler: RouteHandler, ...middleware: any[]): void;
19
+ }
20
+ /** Groups of routes that can be selectively excluded. */
21
+ export type PassportRouteGroup = 'authorize' | 'token' | 'revoke' | 'scopes' | 'device';
22
+ export interface PassportRouteOptions {
23
+ /** Base path for OAuth routes (default: '/oauth') */
24
+ prefix?: string;
25
+ /** Verification URI for device auth (default: '{origin}/oauth/device') */
26
+ verificationUri?: string;
27
+ /** Route groups to skip when registering. */
28
+ except?: PassportRouteGroup[];
29
+ /**
30
+ * Middleware applied to `POST /oauth/token`. The token endpoint is the
31
+ * canonical brute-force target for client_secret guessing — every
32
+ * production app SHOULD mount a per-route rate limiter here.
33
+ *
34
+ * Recommended setup:
35
+ *
36
+ * ```ts
37
+ * import { RateLimit } from '@rudderjs/middleware'
38
+ * import { registerPassportRoutes } from '@rudderjs/passport'
39
+ *
40
+ * registerPassportRoutes(router, {
41
+ * tokenMiddleware: [
42
+ * RateLimit.perMinute(10).by((req) => `${req.ip}:${req.body?.client_id}`),
43
+ * ],
44
+ * })
45
+ * ```
46
+ *
47
+ * The composite key (`ip + client_id`) prevents one noisy client from
48
+ * exhausting the budget for legitimate co-tenants behind a shared NAT,
49
+ * and prevents a single IP from churning through every client_id in the
50
+ * registry. RateLimit also requires a cache provider to be registered —
51
+ * see `@rudderjs/cache`. Without one the middleware silently passes
52
+ * through.
53
+ *
54
+ * Accepts a single handler or an array. Empty / omitted means no
55
+ * additional middleware is applied (the same as before this option
56
+ * existed).
57
+ */
58
+ tokenMiddleware?: MiddlewareHandler | MiddlewareHandler[];
59
+ /**
60
+ * Middleware applied to the consent endpoints — `GET/POST/DELETE
61
+ * /oauth/authorize` and `DELETE /oauth/tokens/:id`. POST /oauth/authorize
62
+ * is the canonical CSRF target (an attacker page that auto-submits a
63
+ * hidden form would mint authorization codes for the victim's logged-in
64
+ * session).
65
+ *
66
+ * Most apps should NOT use this option. The recommended pattern is to
67
+ * mount CSRF on the entire web group from `bootstrap/app.ts`:
68
+ *
69
+ * ```ts
70
+ * .withMiddleware((m) => m.web(CsrfMiddleware()))
71
+ * ```
72
+ *
73
+ * which automatically covers `/oauth/authorize` along with every other
74
+ * state-changing web route. `authorizeMiddleware` is the per-route
75
+ * fallback for apps that do NOT mount CSRF at the group level:
76
+ *
77
+ * ```ts
78
+ * import { CsrfMiddleware } from '@rudderjs/middleware'
79
+ * import { registerPassportWebRoutes } from '@rudderjs/passport'
80
+ *
81
+ * registerPassportWebRoutes(router, {
82
+ * authorizeMiddleware: [CsrfMiddleware()],
83
+ * })
84
+ * ```
85
+ *
86
+ * Don't do both — CsrfMiddleware running twice on the same request
87
+ * emits duplicate `Set-Cookie`s on GETs and runs validation twice on
88
+ * POSTs.
89
+ *
90
+ * Accepts a single handler or an array. Empty / omitted means no
91
+ * additional middleware is applied — the typical case for apps that
92
+ * already CSRF-guard at the group level.
93
+ */
94
+ authorizeMiddleware?: MiddlewareHandler | MiddlewareHandler[];
95
+ /**
96
+ * Middleware applied to the device-flow endpoints — `POST /oauth/device/code`
97
+ * and `POST /oauth/device/approve`. RFC 8628 §5.2 calls for brute-force
98
+ * protection on the user_code surface (8-char alphabet → 32^8 ≈ 1.1×10^12
99
+ * keyspace; per-IP throttling makes exhaustion infeasible).
100
+ *
101
+ * Most apps should NOT need this option. The recommended pattern is to
102
+ * mount a rate limiter on the entire api group from `bootstrap/app.ts`
103
+ * (`withMiddleware((m) => m.api(RateLimit.perMinute(60)))`) — that single
104
+ * hook covers the device endpoints alongside every other api route, and
105
+ * 60/min per-IP is already enough that exhausting the user_code keyspace
106
+ * would take tens of thousands of years.
107
+ *
108
+ * `deviceMiddleware` is the per-route fallback for apps that want a
109
+ * tighter device-specific limit (e.g. `RateLimit.perMinute(5)`) on top of
110
+ * — or in place of — the group default:
111
+ *
112
+ * ```ts
113
+ * import { RateLimit } from '@rudderjs/middleware'
114
+ * import { registerPassportApiRoutes } from '@rudderjs/passport'
115
+ *
116
+ * registerPassportApiRoutes(router, {
117
+ * deviceMiddleware: [RateLimit.perMinute(5).by((req) => req.ip)],
118
+ * })
119
+ * ```
120
+ *
121
+ * Layered limits compose in sequence — group + per-route both run, with
122
+ * the tightest budget winning. Locking individual user_codes after N
123
+ * misses (the stateful half of the original RFC 8628 §5.2 guidance)
124
+ * isn't covered by RateLimit; if you need it, wrap your own middleware.
125
+ *
126
+ * Accepts a single handler or an array. Empty / omitted means no
127
+ * additional middleware is applied — the typical case for apps that
128
+ * already throttle the api group.
129
+ */
130
+ deviceMiddleware?: MiddlewareHandler | MiddlewareHandler[];
131
+ }
132
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/routes/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAE5D;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAA;AAErE;;;;;GAKG;AACH,MAAM,WAAW,MAAM;IACrB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,UAAU,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;IACpE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,UAAU,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;IACrE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,UAAU,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;CACxE;AAED,yDAAyD;AACzD,MAAM,MAAM,kBAAkB,GAC1B,WAAW,GACX,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,QAAQ,CAAA;AAEZ,MAAM,WAAW,oBAAoB;IACnC,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0EAA0E;IAC1E,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,kBAAkB,EAAE,CAAA;IAC7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,eAAe,CAAC,EAAE,iBAAiB,GAAG,iBAAiB,EAAE,CAAA;IACzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACH,mBAAmB,CAAC,EAAE,iBAAiB,GAAG,iBAAiB,EAAE,CAAA;IAC7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACH,gBAAgB,CAAC,EAAE,iBAAiB,GAAG,iBAAiB,EAAE,CAAA;CAC3D"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/routes/types.ts"],"names":[],"mappings":""}
package/dist/routes.d.ts CHANGED
@@ -1,122 +1,5 @@
1
- import type { MiddlewareHandler } from '@rudderjs/contracts';
2
- type RouteHandler = (req: any, res: any) => Promise<any> | any;
3
- interface Router {
4
- get(path: string, handler: RouteHandler, ...middleware: any[]): void;
5
- post(path: string, handler: RouteHandler, ...middleware: any[]): void;
6
- delete(path: string, handler: RouteHandler, ...middleware: any[]): void;
7
- }
8
- /** Groups of routes that can be selectively excluded. */
9
- export type PassportRouteGroup = 'authorize' | 'token' | 'revoke' | 'scopes' | 'device';
10
- export interface PassportRouteOptions {
11
- /** Base path for OAuth routes (default: '/oauth') */
12
- prefix?: string;
13
- /** Verification URI for device auth (default: '{origin}/oauth/device') */
14
- verificationUri?: string;
15
- /** Route groups to skip when registering. */
16
- except?: PassportRouteGroup[];
17
- /**
18
- * Middleware applied to `POST /oauth/token`. The token endpoint is the
19
- * canonical brute-force target for client_secret guessing — every
20
- * production app SHOULD mount a per-route rate limiter here.
21
- *
22
- * Recommended setup:
23
- *
24
- * ```ts
25
- * import { RateLimit } from '@rudderjs/middleware'
26
- * import { registerPassportRoutes } from '@rudderjs/passport'
27
- *
28
- * registerPassportRoutes(router, {
29
- * tokenMiddleware: [
30
- * RateLimit.perMinute(10).by((req) => `${req.ip}:${req.body?.client_id}`),
31
- * ],
32
- * })
33
- * ```
34
- *
35
- * The composite key (`ip + client_id`) prevents one noisy client from
36
- * exhausting the budget for legitimate co-tenants behind a shared NAT,
37
- * and prevents a single IP from churning through every client_id in the
38
- * registry. RateLimit also requires a cache provider to be registered —
39
- * see `@rudderjs/cache`. Without one the middleware silently passes
40
- * through.
41
- *
42
- * Accepts a single handler or an array. Empty / omitted means no
43
- * additional middleware is applied (the same as before this option
44
- * existed).
45
- */
46
- tokenMiddleware?: MiddlewareHandler | MiddlewareHandler[];
47
- /**
48
- * Middleware applied to the consent endpoints — `GET/POST/DELETE
49
- * /oauth/authorize` and `DELETE /oauth/tokens/:id`. POST /oauth/authorize
50
- * is the canonical CSRF target (an attacker page that auto-submits a
51
- * hidden form would mint authorization codes for the victim's logged-in
52
- * session).
53
- *
54
- * Most apps should NOT use this option. The recommended pattern is to
55
- * mount CSRF on the entire web group from `bootstrap/app.ts`:
56
- *
57
- * ```ts
58
- * .withMiddleware((m) => m.web(CsrfMiddleware()))
59
- * ```
60
- *
61
- * which automatically covers `/oauth/authorize` along with every other
62
- * state-changing web route. `authorizeMiddleware` is the per-route
63
- * fallback for apps that do NOT mount CSRF at the group level:
64
- *
65
- * ```ts
66
- * import { CsrfMiddleware } from '@rudderjs/middleware'
67
- * import { registerPassportWebRoutes } from '@rudderjs/passport'
68
- *
69
- * registerPassportWebRoutes(router, {
70
- * authorizeMiddleware: [CsrfMiddleware()],
71
- * })
72
- * ```
73
- *
74
- * Don't do both — CsrfMiddleware running twice on the same request
75
- * emits duplicate `Set-Cookie`s on GETs and runs validation twice on
76
- * POSTs.
77
- *
78
- * Accepts a single handler or an array. Empty / omitted means no
79
- * additional middleware is applied — the typical case for apps that
80
- * already CSRF-guard at the group level.
81
- */
82
- authorizeMiddleware?: MiddlewareHandler | MiddlewareHandler[];
83
- /**
84
- * Middleware applied to the device-flow endpoints — `POST /oauth/device/code`
85
- * and `POST /oauth/device/approve`. RFC 8628 §5.2 calls for brute-force
86
- * protection on the user_code surface (8-char alphabet → 32^8 ≈ 1.1×10^12
87
- * keyspace; per-IP throttling makes exhaustion infeasible).
88
- *
89
- * Most apps should NOT need this option. The recommended pattern is to
90
- * mount a rate limiter on the entire api group from `bootstrap/app.ts`
91
- * (`withMiddleware((m) => m.api(RateLimit.perMinute(60)))`) — that single
92
- * hook covers the device endpoints alongside every other api route, and
93
- * 60/min per-IP is already enough that exhausting the user_code keyspace
94
- * would take tens of thousands of years.
95
- *
96
- * `deviceMiddleware` is the per-route fallback for apps that want a
97
- * tighter device-specific limit (e.g. `RateLimit.perMinute(5)`) on top of
98
- * — or in place of — the group default:
99
- *
100
- * ```ts
101
- * import { RateLimit } from '@rudderjs/middleware'
102
- * import { registerPassportApiRoutes } from '@rudderjs/passport'
103
- *
104
- * registerPassportApiRoutes(router, {
105
- * deviceMiddleware: [RateLimit.perMinute(5).by((req) => req.ip)],
106
- * })
107
- * ```
108
- *
109
- * Layered limits compose in sequence — group + per-route both run, with
110
- * the tightest budget winning. Locking individual user_codes after N
111
- * misses (the stateful half of the original RFC 8628 §5.2 guidance)
112
- * isn't covered by RateLimit; if you need it, wrap your own middleware.
113
- *
114
- * Accepts a single handler or an array. Empty / omitted means no
115
- * additional middleware is applied — the typical case for apps that
116
- * already throttle the api group.
117
- */
118
- deviceMiddleware?: MiddlewareHandler | MiddlewareHandler[];
119
- }
1
+ import type { PassportRouteOptions, Router } from './routes/types.js';
2
+ export type { PassportRouteGroup, PassportRouteOptions } from './routes/types.js';
120
3
  /**
121
4
  * Register all Passport OAuth routes on the given router.
122
5
  *
@@ -178,5 +61,4 @@ export declare function registerPassportWebRoutes(router: Router, opts?: Passpor
178
61
  * stateful half on the web group.
179
62
  */
180
63
  export declare function registerPassportApiRoutes(router: Router, opts?: PassportRouteOptions): void;
181
- export {};
182
64
  //# sourceMappingURL=routes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAiK5D,KAAK,YAAY,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAA;AAE9D,UAAU,MAAM;IACd,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,UAAU,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;IACpE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,UAAU,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;IACrE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,UAAU,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;CACxE;AAED,yDAAyD;AACzD,MAAM,MAAM,kBAAkB,GAC1B,WAAW,GACX,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,QAAQ,CAAA;AAEZ,MAAM,WAAW,oBAAoB;IACnC,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0EAA0E;IAC1E,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,kBAAkB,EAAE,CAAA;IAC7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,eAAe,CAAC,EAAE,iBAAiB,GAAG,iBAAiB,EAAE,CAAA;IACzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACH,mBAAmB,CAAC,EAAE,iBAAiB,GAAG,iBAAiB,EAAE,CAAA;IAC7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACH,gBAAgB,CAAC,EAAE,iBAAiB,GAAG,iBAAiB,EAAE,CAAA;CAC3D;AAOD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,oBAAyB,GAAG,IAAI,CAsS5F;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,oBAAyB,GAAG,IAAI,CAG/F;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,oBAAyB,GAAG,IAAI,CAG/F"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAsB,oBAAoB,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAQzF,YAAY,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAEjF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,oBAAyB,GAAG,IAAI,CAc5F;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,oBAAyB,GAAG,IAAI,CAG/F;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,oBAAyB,GAAG,IAAI,CAG/F"}