@shipfox/api-auth 11.0.0 → 12.2.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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +37 -0
- package/README.md +19 -19
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/job-lease-token.d.ts +1 -1
- package/dist/core/job-lease-token.d.ts.map +1 -1
- package/dist/core/job-lease-token.js +1 -1
- package/dist/core/job-lease-token.js.map +1 -1
- package/dist/core/jwt.d.ts +2 -2
- package/dist/core/rate-limit.d.ts.map +1 -1
- package/dist/core/runner-session-token.d.ts +1 -1
- package/dist/core/runner-session-token.d.ts.map +1 -1
- package/dist/core/runner-session-token.js +1 -1
- package/dist/core/runner-session-token.js.map +1 -1
- package/dist/db/admin-user-moderation.d.ts.map +1 -1
- package/dist/db/db.d.ts +16 -16
- package/dist/db/rate-limits.d.ts +6 -15
- package/dist/db/rate-limits.d.ts.map +1 -1
- package/dist/db/rate-limits.js +17 -18
- package/dist/db/rate-limits.js.map +1 -1
- package/dist/db/refresh-tokens.d.ts +3 -3
- package/dist/db/refresh-tokens.js +3 -3
- package/dist/db/refresh-tokens.js.map +1 -1
- package/dist/db/schema/admin-command-results.d.ts +1 -1
- package/dist/db/schema/admin-grants.d.ts +1 -1
- package/dist/db/schema/outbox.d.ts +1 -1
- package/dist/db/schema/password-resets.d.ts +1 -1
- package/dist/db/schema/rate-limits.d.ts +1 -1
- package/dist/db/schema/refresh-tokens.d.ts +1 -1
- package/dist/db/schema/users.d.ts +2 -2
- package/dist/presentation/auth/lease-token-auth.d.ts +1 -1
- package/dist/presentation/auth/lease-token-auth.js +1 -1
- package/dist/presentation/auth/lease-token-auth.js.map +1 -1
- package/dist/presentation/routes/rate-limit.d.ts.map +1 -1
- package/dist/presentation/routes/rate-limit.js +32 -53
- package/dist/presentation/routes/rate-limit.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +15 -15
- package/src/core/job-lease-token.ts +1 -1
- package/src/core/runner-session-token.ts +1 -1
- package/src/db/rate-limits.ts +28 -42
- package/src/db/refresh-tokens.ts +3 -3
- package/src/presentation/auth/lease-token-auth.ts +1 -1
- package/src/presentation/routes/rate-limit.ts +31 -64
- package/test/routes.ts +6 -2
- package/tsconfig.build.tsbuildinfo +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
$ shipfox-swc
|
|
2
|
-
Successfully compiled: 67 files with swc (
|
|
2
|
+
Successfully compiled: 67 files with swc (574.33ms)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# @shipfox/api-auth
|
|
2
2
|
|
|
3
|
+
## 12.2.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @shipfox/node-opentelemetry@0.6.4
|
|
8
|
+
- @shipfox/api-email-challenges@1.1.8
|
|
9
|
+
- @shipfox/node-fastify@0.4.2
|
|
10
|
+
- @shipfox/node-mailer@0.2.5
|
|
11
|
+
- @shipfox/node-module@1.0.6
|
|
12
|
+
- @shipfox/api-auth-context@12.2.0
|
|
13
|
+
|
|
14
|
+
## 12.0.0
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- f78740d: Remove Unicode dash punctuation from package prose and source comments.
|
|
19
|
+
- 9ebc5b4: Add an authenticated, rate-limited workspace slug availability endpoint.
|
|
20
|
+
- Updated dependencies [f78740d]
|
|
21
|
+
- Updated dependencies [94aba88]
|
|
22
|
+
- Updated dependencies [f13e8bb]
|
|
23
|
+
- Updated dependencies [9ebc5b4]
|
|
24
|
+
- Updated dependencies [34a5639]
|
|
25
|
+
- Updated dependencies [e1efaee]
|
|
26
|
+
- @shipfox/api-auth-dto@12.0.0
|
|
27
|
+
- @shipfox/api-common-dto@12.0.0
|
|
28
|
+
- @shipfox/inter-module@0.2.3
|
|
29
|
+
- @shipfox/node-fastify@0.4.1
|
|
30
|
+
- @shipfox/node-module@1.0.5
|
|
31
|
+
- @shipfox/api-workspaces-dto@12.0.0
|
|
32
|
+
- @shipfox/node-jwt@0.4.0
|
|
33
|
+
- @shipfox/node-postgres@0.5.0
|
|
34
|
+
- @shipfox/node-rate-limit@0.4.0
|
|
35
|
+
- @shipfox/api-auth-context@12.0.0
|
|
36
|
+
- @shipfox/api-email-challenges@1.1.7
|
|
37
|
+
- @shipfox/node-drizzle@0.3.5
|
|
38
|
+
- @shipfox/node-outbox@0.2.6
|
|
39
|
+
|
|
3
40
|
## 11.0.0
|
|
4
41
|
|
|
5
42
|
### Major Changes
|
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ Required environment:
|
|
|
60
60
|
| `AUTH_JOB_LEASE_TOKEN_EXPIRES_IN` | `90m` | Job lease token lifetime. |
|
|
61
61
|
| `AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN` | `1h` | Runner session token lifetime. |
|
|
62
62
|
| `AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS` | `14` | Refresh token and cookie lifetime. |
|
|
63
|
-
| `AUTH_REFRESH_ROTATION_GRACE_SECONDS` | `30` | Grace window for accepting a
|
|
63
|
+
| `AUTH_REFRESH_ROTATION_GRACE_SECONDS` | `30` | Grace window for accepting a recently rotated refresh token during concurrent refreshes. |
|
|
64
64
|
| `AUTH_REFRESH_COOKIE_NAME` | `shipfox_refresh_token` | HTTP cookie name for refresh sessions. |
|
|
65
65
|
| `AUTH_PASSWORD_ENABLED` | `true` | Enables password and email-verification routes. Set to `false` when another module provides login. Server construction fails if no login method is available. |
|
|
66
66
|
| `AUTH_SIGNUP_GATE_ENABLED` | `false` | Restricts new account creation to the configured signup email allowlist. Requires `AUTH_SIGNUP_ALLOWED_EMAIL_DOMAINS` or `AUTH_SIGNUP_ALLOWED_EMAILS` to be set. |
|
|
@@ -116,16 +116,16 @@ itself.
|
|
|
116
116
|
- **Audience:** signed-in users on first-party clients (web app, CLI).
|
|
117
117
|
- **Grants:** the user's own account routes and, downstream, any route that
|
|
118
118
|
authorizes against the membership claims. Its scope is "this user, with these
|
|
119
|
-
memberships"
|
|
119
|
+
memberships", never a single resource.
|
|
120
120
|
- **Lifecycle:**
|
|
121
|
-
- *Emit
|
|
122
|
-
- *Exchange
|
|
121
|
+
- *Emit*: issued on login, signup verification, and password reset.
|
|
122
|
+
- *Exchange*: sent on each request; a longer-lived refresh session mints a
|
|
123
123
|
fresh token when it expires.
|
|
124
|
-
- *Store
|
|
124
|
+
- *Store*: held in client memory only, never persisted to disk or local
|
|
125
125
|
storage. Refresh sessions are stored server-side as hashes, never in raw form.
|
|
126
|
-
- *Discard
|
|
126
|
+
- *Discard*: expires at the configured `exp` time. Revoking the refresh
|
|
127
127
|
session blocks renewal but does not invalidate the issued access token.
|
|
128
|
-
- **Tradeoff
|
|
128
|
+
- **Tradeoff: stale memberships:** because memberships ride in the claims, a
|
|
129
129
|
membership or workspace-status change only takes effect on the next token
|
|
130
130
|
issuance or refresh. Accepted because the recommended lifetime is 15 minutes,
|
|
131
131
|
so the staleness window is bounded and a per-request membership read is avoided.
|
|
@@ -137,7 +137,7 @@ The runner exchanges its long-lived registration token at startup, then uses the
|
|
|
137
137
|
short-lived session token to claim jobs. Heartbeat and step/report/log operations
|
|
138
138
|
use the per-job lease token instead.
|
|
139
139
|
|
|
140
|
-
- **Scope
|
|
140
|
+
- **Scope: one workspace's runner data plane.** The token names one runner
|
|
141
141
|
session, one workspace, the fixed `workspace` scope, and the session's immutable
|
|
142
142
|
label set. It can claim jobs for that workspace. It is not a user identity and
|
|
143
143
|
does not authorize dashboard management routes.
|
|
@@ -148,7 +148,7 @@ use the per-job lease token instead.
|
|
|
148
148
|
- **Mechanics.** Signed with HMAC-SHA256 using the derived runner-session key
|
|
149
149
|
and the `runner-session` audience. The default lifetime is `1h`, short enough
|
|
150
150
|
to bound the residual claim window when a registration token is revoked.
|
|
151
|
-
- **Tradeoff
|
|
151
|
+
- **Tradeoff: no per-session revocation in v1.** Claim stays stateless and does
|
|
152
152
|
not check the runner session row on each poll. Revoking the registration token
|
|
153
153
|
blocks new sessions, but an existing session can keep claiming until
|
|
154
154
|
`AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN` elapses. A job execution claimed before
|
|
@@ -163,7 +163,7 @@ A single-job **capability** token, not a session. It is the means by which a
|
|
|
163
163
|
runner proves it is the legitimate holder of one specific job while it reports
|
|
164
164
|
progress and drives that job to completion.
|
|
165
165
|
|
|
166
|
-
- **Scope
|
|
166
|
+
- **Scope: exactly one job execution.** The token authorizes action on the
|
|
167
167
|
single job execution it names, for the runner holding it. It is **not** a runner
|
|
168
168
|
identity, it is **not** workspace-wide, and it is **not** a substitute for the
|
|
169
169
|
long-lived runner credential used to claim work in the first place. The
|
|
@@ -175,9 +175,9 @@ progress and drives that job to completion.
|
|
|
175
175
|
- **Trust boundary.** There is exactly one issuer: the scheduling side mints a
|
|
176
176
|
lease when a runner claims a job execution, next-step re-scopes it to the
|
|
177
177
|
dispatched step attempt, and heartbeat re-mints the same narrow capability
|
|
178
|
-
after server state accepts the heartbeat. Everything
|
|
179
|
-
|
|
180
|
-
|
|
178
|
+
after server state accepts the heartbeat. Everything downstream only *verifies*
|
|
179
|
+
the token with an in-process signature check. It makes no callback to the
|
|
180
|
+
issuer. The runner, and the untrusted agent workload it hosts, never mints
|
|
181
181
|
or modifies a token; it only presents the one it was handed.
|
|
182
182
|
- **Mechanics.** Signed with HMAC-SHA256 using the job-lease key derived from
|
|
183
183
|
`AUTH_ROOT_KEY`. Its claims name the job, job execution, and surrounding
|
|
@@ -187,13 +187,13 @@ progress and drives that job to completion.
|
|
|
187
187
|
execution. The root key is supplied through configuration, never embedded in
|
|
188
188
|
code or committed. The raw token must **never** be written to logs, traces, or
|
|
189
189
|
error payloads. There is no automatic redaction to fall back on.
|
|
190
|
-
- **Defense in depth
|
|
190
|
+
- **Defense in depth: server state is the final authority.** A valid token is
|
|
191
191
|
never sufficient on its own to advance work. On the lease's own request path the
|
|
192
192
|
gate is server-side step and progression state: a report against a step that has
|
|
193
193
|
already reached a terminal state (finished, failed, or cancelled) is ignored, so
|
|
194
194
|
a still-valid token cannot re-drive work that is already done. Job-level
|
|
195
195
|
finalization is enforced outside the lease path. Cancellation flows the other way
|
|
196
|
-
as well
|
|
196
|
+
as well: the server can ask the runner to stop at any point, and that request
|
|
197
197
|
rides on the response to each heartbeat rather than depending on the token.
|
|
198
198
|
- **Log append scope.** A step-scoped lease is a signed membership and attempt
|
|
199
199
|
check for append-log authorization. It is not an active-step proof: until the
|
|
@@ -207,7 +207,7 @@ progress and drives that job to completion.
|
|
|
207
207
|
other workspaces. If `AUTH_ROOT_KEY` leaks, rotate it. This invalidates every
|
|
208
208
|
live token and email challenge derived from the old root, including every live
|
|
209
209
|
lease, and forces fresh claims.
|
|
210
|
-
- **Tradeoff
|
|
210
|
+
- **Tradeoff: no per-token revocation.** A single outstanding lease cannot be
|
|
211
211
|
revoked on its own by token ID. The claiming runner's credential is not
|
|
212
212
|
re-checked on each request, so a lease for already-claimed work can continue to
|
|
213
213
|
be renewed by heartbeat until server state cancels, completes, times out, or
|
|
@@ -217,7 +217,7 @@ progress and drives that job to completion.
|
|
|
217
217
|
- **Guidelines for future changes.** Keep the authority narrow: do not add claims
|
|
218
218
|
that grant access beyond the single job, keep the lifetime bounded, keep a single
|
|
219
219
|
issuer, and keep every other side verify-only. If the no-revocation window ever
|
|
220
|
-
proves too wide, the right fix is to bind the lease to live runner or job state
|
|
220
|
+
proves too wide, the right fix is to bind the lease to live runner or job state,
|
|
221
221
|
not to broaden what the token itself can authorize.
|
|
222
222
|
|
|
223
223
|
## Routes
|
|
@@ -433,7 +433,7 @@ unauthorized` error when the verified request context is missing, inconsistent,
|
|
|
433
433
|
or has no refresh-session ID. It exposes neither refresh-token material nor
|
|
434
434
|
storage details.
|
|
435
435
|
|
|
436
|
-
## Data
|
|
436
|
+
## Data model
|
|
437
437
|
|
|
438
438
|
The module creates tables with the `auth_` prefix:
|
|
439
439
|
|
|
@@ -446,7 +446,7 @@ The module creates tables with the `auth_` prefix:
|
|
|
446
446
|
|
|
447
447
|
Passwords use Argon2id. Password reset tokens and refresh tokens are opaque tokens stored as hashes. Email verification uses the shared email-challenges module.
|
|
448
448
|
|
|
449
|
-
## Behavior
|
|
449
|
+
## Behavior notes
|
|
450
450
|
|
|
451
451
|
- Signup sends an eight-digit verification code and returns the new user with its challenge ID.
|
|
452
452
|
- Signup gating blocks account creation only. Existing users keep signing in, even when their address is not on the current allowlist.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,sBAAuB,SAAQ,KAAK;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,YAAY,WAAW,EAAE,MAAM,EAG9B;CACF;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,EAAE,EAAE,MAAM,EAGrB;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,YAAY,EAAE,EAAE,MAAM,EAGrB;CACF;AAED,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,cAGC;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,YAAY,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAG9C;CACF;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,SAAS,EAAE,MAAM,EAG5B;CACF;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,YAAY,KAAK,EAAE,MAAM,EAGxB;CACF;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,cAGC;CACF;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,cAGC;CACF;AAED,qBAAa,8BAA+B,SAAQ,KAAK;IACvD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,SAAkB,KAAK,EAAE,OAAO,CAAC;IAEjC,YAAY,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAK7C;CACF;AAED,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,WAAW,EAAE,OAAO,uBAAuB,EAAE,SAAS,CAAC;IAEhE,YAAY,WAAW,EAAE,OAAO,uBAAuB,EAAE,SAAS,EAIjE;CACF;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,cAGC;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,cAGC;CACF;AAED,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,cAGC;CACF;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,cAGC;CACF;AAED,qBAAa,+BAAgC,SAAQ,KAAK;IACxD,cAGC;CACF;AAED,qBAAa,6BAA8B,SAAQ,KAAK;IACtD,cAGC;CACF;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,MAAM,CAAC,EAAE,MAAM,EAG1B;CACF;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,cAGC;CACF;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,cAGC;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,YAAY,WAAW,EAAE,MAAM,EAG9B;CACF;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,YAAY,WAAW,EAAE,MAAM,EAG9B;CACF;AAED,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,cAGC;CACF;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,YAAY,KAAK,EAAE,MAAM,EAGxB;CACF"}
|
|
@@ -6,7 +6,7 @@ export declare function jobLeaseParamsFrom(source: JobLeaseParamSource, stepScop
|
|
|
6
6
|
currentStepAttempt: number;
|
|
7
7
|
}): IssueJobLeaseTokenParams;
|
|
8
8
|
export declare function issueJobLeaseToken(claims: IssueJobLeaseTokenParams): Promise<string>;
|
|
9
|
-
/** Returns the claims on success, or `null` for any invalid input
|
|
9
|
+
/** Returns the claims on success, or `null` for any invalid input. Never throws. */
|
|
10
10
|
export declare function verifyJobLeaseToken(token: string): Promise<JobLeaseTokenClaims | null>;
|
|
11
11
|
export {};
|
|
12
12
|
//# sourceMappingURL=job-lease-token.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job-lease-token.d.ts","sourceRoot":"","sources":["../../src/core/job-lease-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,uBAAuB,CAAC;AAO/B,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,mBAAmB,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC,CAAC;AAExF,KAAK,mBAAmB,GAAG,IAAI,CAC7B,mBAAmB,EACjB,eAAe,GACf,sBAAsB,GACtB,OAAO,GACP,gBAAgB,GAChB,WAAW,GACX,aAAa,GACb,iBAAiB,CACpB,CAAC;AAEF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,mBAAmB,EAC3B,SAAS,CAAC,EAAE;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAC,GAC9D,wBAAwB,CAW1B;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0B1F;AAED,
|
|
1
|
+
{"version":3,"file":"job-lease-token.d.ts","sourceRoot":"","sources":["../../src/core/job-lease-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,uBAAuB,CAAC;AAO/B,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,mBAAmB,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC,CAAC;AAExF,KAAK,mBAAmB,GAAG,IAAI,CAC7B,mBAAmB,EACjB,eAAe,GACf,sBAAsB,GACtB,OAAO,GACP,gBAAgB,GAChB,WAAW,GACX,aAAa,GACb,iBAAiB,CACpB,CAAC;AAEF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,mBAAmB,EAC3B,SAAS,CAAC,EAAE;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAC,GAC9D,wBAAwB,CAW1B;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0B1F;AAED,oFAAoF;AACpF,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAc5F"}
|
|
@@ -40,7 +40,7 @@ export async function issueJobLeaseToken(claims) {
|
|
|
40
40
|
recordTokenIssued('job_lease');
|
|
41
41
|
return token;
|
|
42
42
|
}
|
|
43
|
-
/** Returns the claims on success, or `null` for any invalid input
|
|
43
|
+
/** Returns the claims on success, or `null` for any invalid input. Never throws. */ export async function verifyJobLeaseToken(token) {
|
|
44
44
|
try {
|
|
45
45
|
const claims = await verifyHs256({
|
|
46
46
|
token,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/job-lease-token.ts"],"sourcesContent":["import {\n JOB_LEASE_TOKEN_AUDIENCE,\n type JobLeaseTokenClaims,\n jobLeaseTokenClaimsSchema,\n} from '@shipfox/api-auth-dto';\nimport {jobLeaseTokenKey} from '@shipfox/node-auth-root-key';\nimport {signHs256, verifyHs256} from '@shipfox/node-jwt';\nimport {config} from '#config.js';\nimport {recordTokenIssued, recordTokenVerified} from '#metrics/index.js';\n\n// `aud`, `iat` and `exp` are set by the codec (jose); callers supply the business ids only.\nexport type IssueJobLeaseTokenParams = Omit<JobLeaseTokenClaims, 'aud' | 'iat' | 'exp'>;\n\ntype JobLeaseParamSource = Pick<\n JobLeaseTokenClaims,\n | 'workflowRunId'\n | 'workflowRunAttemptId'\n | 'jobId'\n | 'jobExecutionId'\n | 'projectId'\n | 'workspaceId'\n | 'runnerSessionId'\n>;\n\nexport function jobLeaseParamsFrom(\n source: JobLeaseParamSource,\n stepScope?: {currentStepId: string; currentStepAttempt: number},\n): IssueJobLeaseTokenParams {\n return {\n workflowRunId: source.workflowRunId,\n workflowRunAttemptId: source.workflowRunAttemptId,\n jobId: source.jobId,\n jobExecutionId: source.jobExecutionId,\n projectId: source.projectId,\n workspaceId: source.workspaceId,\n runnerSessionId: source.runnerSessionId,\n ...(stepScope ? stepScope : {}),\n };\n}\n\nexport async function issueJobLeaseToken(claims: IssueJobLeaseTokenParams): Promise<string> {\n const token = await signHs256({\n payload: {\n workflowRunId: claims.workflowRunId,\n ...(claims.workflowRunAttempt === undefined\n ? {}\n : {workflowRunAttempt: claims.workflowRunAttempt}),\n workflowRunAttemptId: claims.workflowRunAttemptId,\n jobId: claims.jobId,\n jobExecutionId: claims.jobExecutionId,\n projectId: claims.projectId,\n workspaceId: claims.workspaceId,\n runnerSessionId: claims.runnerSessionId,\n ...(claims.currentStepId && claims.currentStepAttempt !== undefined\n ? {\n currentStepId: claims.currentStepId,\n currentStepAttempt: claims.currentStepAttempt,\n }\n : {}),\n },\n secret: jobLeaseTokenKey(),\n expiresIn: config.AUTH_JOB_LEASE_TOKEN_EXPIRES_IN,\n audience: JOB_LEASE_TOKEN_AUDIENCE,\n });\n recordTokenIssued('job_lease');\n return token;\n}\n\n/** Returns the claims on success, or `null` for any invalid input
|
|
1
|
+
{"version":3,"sources":["../../src/core/job-lease-token.ts"],"sourcesContent":["import {\n JOB_LEASE_TOKEN_AUDIENCE,\n type JobLeaseTokenClaims,\n jobLeaseTokenClaimsSchema,\n} from '@shipfox/api-auth-dto';\nimport {jobLeaseTokenKey} from '@shipfox/node-auth-root-key';\nimport {signHs256, verifyHs256} from '@shipfox/node-jwt';\nimport {config} from '#config.js';\nimport {recordTokenIssued, recordTokenVerified} from '#metrics/index.js';\n\n// `aud`, `iat` and `exp` are set by the codec (jose); callers supply the business ids only.\nexport type IssueJobLeaseTokenParams = Omit<JobLeaseTokenClaims, 'aud' | 'iat' | 'exp'>;\n\ntype JobLeaseParamSource = Pick<\n JobLeaseTokenClaims,\n | 'workflowRunId'\n | 'workflowRunAttemptId'\n | 'jobId'\n | 'jobExecutionId'\n | 'projectId'\n | 'workspaceId'\n | 'runnerSessionId'\n>;\n\nexport function jobLeaseParamsFrom(\n source: JobLeaseParamSource,\n stepScope?: {currentStepId: string; currentStepAttempt: number},\n): IssueJobLeaseTokenParams {\n return {\n workflowRunId: source.workflowRunId,\n workflowRunAttemptId: source.workflowRunAttemptId,\n jobId: source.jobId,\n jobExecutionId: source.jobExecutionId,\n projectId: source.projectId,\n workspaceId: source.workspaceId,\n runnerSessionId: source.runnerSessionId,\n ...(stepScope ? stepScope : {}),\n };\n}\n\nexport async function issueJobLeaseToken(claims: IssueJobLeaseTokenParams): Promise<string> {\n const token = await signHs256({\n payload: {\n workflowRunId: claims.workflowRunId,\n ...(claims.workflowRunAttempt === undefined\n ? {}\n : {workflowRunAttempt: claims.workflowRunAttempt}),\n workflowRunAttemptId: claims.workflowRunAttemptId,\n jobId: claims.jobId,\n jobExecutionId: claims.jobExecutionId,\n projectId: claims.projectId,\n workspaceId: claims.workspaceId,\n runnerSessionId: claims.runnerSessionId,\n ...(claims.currentStepId && claims.currentStepAttempt !== undefined\n ? {\n currentStepId: claims.currentStepId,\n currentStepAttempt: claims.currentStepAttempt,\n }\n : {}),\n },\n secret: jobLeaseTokenKey(),\n expiresIn: config.AUTH_JOB_LEASE_TOKEN_EXPIRES_IN,\n audience: JOB_LEASE_TOKEN_AUDIENCE,\n });\n recordTokenIssued('job_lease');\n return token;\n}\n\n/** Returns the claims on success, or `null` for any invalid input. Never throws. */\nexport async function verifyJobLeaseToken(token: string): Promise<JobLeaseTokenClaims | null> {\n try {\n const claims = await verifyHs256({\n token,\n secret: jobLeaseTokenKey(),\n schema: jobLeaseTokenClaimsSchema,\n audience: JOB_LEASE_TOKEN_AUDIENCE,\n });\n recordTokenVerified('job_lease', 'ok');\n return claims;\n } catch {\n recordTokenVerified('job_lease', 'rejected');\n return null;\n }\n}\n"],"names":["JOB_LEASE_TOKEN_AUDIENCE","jobLeaseTokenClaimsSchema","jobLeaseTokenKey","signHs256","verifyHs256","config","recordTokenIssued","recordTokenVerified","jobLeaseParamsFrom","source","stepScope","workflowRunId","workflowRunAttemptId","jobId","jobExecutionId","projectId","workspaceId","runnerSessionId","issueJobLeaseToken","claims","token","payload","workflowRunAttempt","undefined","currentStepId","currentStepAttempt","secret","expiresIn","AUTH_JOB_LEASE_TOKEN_EXPIRES_IN","audience","verifyJobLeaseToken","schema"],"mappings":"AAAA,SACEA,wBAAwB,EAExBC,yBAAyB,QACpB,wBAAwB;AAC/B,SAAQC,gBAAgB,QAAO,8BAA8B;AAC7D,SAAQC,SAAS,EAAEC,WAAW,QAAO,oBAAoB;AACzD,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,iBAAiB,EAAEC,mBAAmB,QAAO,oBAAoB;AAgBzE,OAAO,SAASC,mBACdC,MAA2B,EAC3BC,SAA+D;IAE/D,OAAO;QACLC,eAAeF,OAAOE,aAAa;QACnCC,sBAAsBH,OAAOG,oBAAoB;QACjDC,OAAOJ,OAAOI,KAAK;QACnBC,gBAAgBL,OAAOK,cAAc;QACrCC,WAAWN,OAAOM,SAAS;QAC3BC,aAAaP,OAAOO,WAAW;QAC/BC,iBAAiBR,OAAOQ,eAAe;QACvC,GAAIP,YAAYA,YAAY,CAAC,CAAC;IAChC;AACF;AAEA,OAAO,eAAeQ,mBAAmBC,MAAgC;IACvE,MAAMC,QAAQ,MAAMjB,UAAU;QAC5BkB,SAAS;YACPV,eAAeQ,OAAOR,aAAa;YACnC,GAAIQ,OAAOG,kBAAkB,KAAKC,YAC9B,CAAC,IACD;gBAACD,oBAAoBH,OAAOG,kBAAkB;YAAA,CAAC;YACnDV,sBAAsBO,OAAOP,oBAAoB;YACjDC,OAAOM,OAAON,KAAK;YACnBC,gBAAgBK,OAAOL,cAAc;YACrCC,WAAWI,OAAOJ,SAAS;YAC3BC,aAAaG,OAAOH,WAAW;YAC/BC,iBAAiBE,OAAOF,eAAe;YACvC,GAAIE,OAAOK,aAAa,IAAIL,OAAOM,kBAAkB,KAAKF,YACtD;gBACEC,eAAeL,OAAOK,aAAa;gBACnCC,oBAAoBN,OAAOM,kBAAkB;YAC/C,IACA,CAAC,CAAC;QACR;QACAC,QAAQxB;QACRyB,WAAWtB,OAAOuB,+BAA+B;QACjDC,UAAU7B;IACZ;IACAM,kBAAkB;IAClB,OAAOc;AACT;AAEA,kFAAkF,GAClF,OAAO,eAAeU,oBAAoBV,KAAa;IACrD,IAAI;QACF,MAAMD,SAAS,MAAMf,YAAY;YAC/BgB;YACAM,QAAQxB;YACR6B,QAAQ9B;YACR4B,UAAU7B;QACZ;QACAO,oBAAoB,aAAa;QACjC,OAAOY;IACT,EAAE,OAAM;QACNZ,oBAAoB,aAAa;QACjC,OAAO;IACT;AACF"}
|
package/dist/core/jwt.d.ts
CHANGED
|
@@ -6,8 +6,8 @@ export declare const tokenMembershipSchema: z.ZodObject<{
|
|
|
6
6
|
}>;
|
|
7
7
|
workspaceStatus: z.ZodDefault<z.ZodEnum<{
|
|
8
8
|
active: "active";
|
|
9
|
-
suspended: "suspended";
|
|
10
9
|
deleted: "deleted";
|
|
10
|
+
suspended: "suspended";
|
|
11
11
|
}>>;
|
|
12
12
|
}, z.core.$strip>;
|
|
13
13
|
export type TokenMembership = z.infer<typeof tokenMembershipSchema>;
|
|
@@ -23,8 +23,8 @@ export declare const userTokenClaimsSchema: z.ZodObject<{
|
|
|
23
23
|
}>;
|
|
24
24
|
workspaceStatus: z.ZodDefault<z.ZodEnum<{
|
|
25
25
|
active: "active";
|
|
26
|
-
suspended: "suspended";
|
|
27
26
|
deleted: "deleted";
|
|
27
|
+
suspended: "suspended";
|
|
28
28
|
}>>;
|
|
29
29
|
}, z.core.$strip>>;
|
|
30
30
|
iat: z.ZodNumber;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/core/rate-limit.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,sBAAsB,EACtB,KAAK,eAAe,EACpB,yBAAyB,EAC1B,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EAGxB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,mBAAmB,GAAG,eAAe,CAAC;AAElD,MAAM,WAAW,wBAAyB,SAAQ,mBAAmB;IACnE,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,qBAAa,0BAA2B,SAAQ,sBAAsB,CACpE,mBAAmB,EACnB,kBAAkB,CACnB;
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/core/rate-limit.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,sBAAsB,EACtB,KAAK,eAAe,EACpB,yBAAyB,EAC1B,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EAGxB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,mBAAmB,GAAG,eAAe,CAAC;AAElD,MAAM,WAAW,wBAAyB,SAAQ,mBAAmB;IACnE,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,qBAAa,0BAA2B,SAAQ,sBAAsB,CACpE,mBAAmB,EACnB,kBAAkB,CACnB;IACC,YAAY,MAAM,EAAE;QAClB,MAAM,EAAE,mBAAmB,CAAC;QAC5B,KAAK,EAAE,kBAAkB,CAAC;QAC1B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,oBAAoB,EAAE,MAAM,CAAC;KAC9B,EAIA;CACF;AAED,qBAAa,6BAA8B,SAAQ,yBAAyB,CAC1E,mBAAmB,EACnB,kBAAkB,CACnB;IACC,YAAY,MAAM,EAAE;QAClB,MAAM,EAAE,mBAAmB,CAAC;QAC5B,KAAK,EAAE,kBAAkB,CAAC;QAC1B,oBAAoB,EAAE,MAAM,CAAC;QAC7B,KAAK,EAAE,OAAO,CAAC;KAChB,EAIA;CACF;AASD,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAClD,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,MAAM,CAQT;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoCxF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type RunnerSessionTokenClaims } from '@shipfox/api-auth-dto';
|
|
2
2
|
export type IssueRunnerSessionTokenParams = Omit<RunnerSessionTokenClaims, 'aud' | 'iat' | 'exp'>;
|
|
3
3
|
export declare function issueRunnerSessionToken(claims: IssueRunnerSessionTokenParams): Promise<string>;
|
|
4
|
-
/** Returns the claims on success, or `null` for any invalid input
|
|
4
|
+
/** Returns the claims on success, or `null` for any invalid input. Never throws. */
|
|
5
5
|
export declare function verifyRunnerSessionToken(token: string): Promise<RunnerSessionTokenClaims | null>;
|
|
6
6
|
//# sourceMappingURL=runner-session-token.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner-session-token.d.ts","sourceRoot":"","sources":["../../src/core/runner-session-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,wBAAwB,EAE9B,MAAM,uBAAuB,CAAC;AAO/B,MAAM,MAAM,6BAA6B,GAAG,IAAI,CAAC,wBAAwB,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC,CAAC;AAElG,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,MAAM,CAAC,CAejB;AAED,
|
|
1
|
+
{"version":3,"file":"runner-session-token.d.ts","sourceRoot":"","sources":["../../src/core/runner-session-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,wBAAwB,EAE9B,MAAM,uBAAuB,CAAC;AAO/B,MAAM,MAAM,6BAA6B,GAAG,IAAI,CAAC,wBAAwB,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC,CAAC;AAElG,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,MAAM,CAAC,CAejB;AAED,oFAAoF;AACpF,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC,CAc1C"}
|
|
@@ -19,7 +19,7 @@ export async function issueRunnerSessionToken(claims) {
|
|
|
19
19
|
recordTokenIssued('runner_session');
|
|
20
20
|
return token;
|
|
21
21
|
}
|
|
22
|
-
/** Returns the claims on success, or `null` for any invalid input
|
|
22
|
+
/** Returns the claims on success, or `null` for any invalid input. Never throws. */ export async function verifyRunnerSessionToken(token) {
|
|
23
23
|
try {
|
|
24
24
|
const claims = await verifyHs256({
|
|
25
25
|
token,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/runner-session-token.ts"],"sourcesContent":["import {\n RUNNER_SESSION_TOKEN_AUDIENCE,\n type RunnerSessionTokenClaims,\n runnerSessionTokenClaimsSchema,\n} from '@shipfox/api-auth-dto';\nimport {runnerSessionTokenKey} from '@shipfox/node-auth-root-key';\nimport {signHs256, verifyHs256} from '@shipfox/node-jwt';\nimport {config} from '#config.js';\nimport {recordTokenIssued, recordTokenVerified} from '#metrics/index.js';\n\n// `aud`, `iat` and `exp` are set by the codec (jose); callers supply the business ids only.\nexport type IssueRunnerSessionTokenParams = Omit<RunnerSessionTokenClaims, 'aud' | 'iat' | 'exp'>;\n\nexport async function issueRunnerSessionToken(\n claims: IssueRunnerSessionTokenParams,\n): Promise<string> {\n const token = await signHs256({\n payload: {\n runnerSessionId: claims.runnerSessionId,\n workspaceId: claims.workspaceId,\n scope: claims.scope,\n labels: claims.labels,\n maxClaims: claims.maxClaims,\n },\n secret: runnerSessionTokenKey(),\n expiresIn: config.AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN,\n audience: RUNNER_SESSION_TOKEN_AUDIENCE,\n });\n recordTokenIssued('runner_session');\n return token;\n}\n\n/** Returns the claims on success, or `null` for any invalid input
|
|
1
|
+
{"version":3,"sources":["../../src/core/runner-session-token.ts"],"sourcesContent":["import {\n RUNNER_SESSION_TOKEN_AUDIENCE,\n type RunnerSessionTokenClaims,\n runnerSessionTokenClaimsSchema,\n} from '@shipfox/api-auth-dto';\nimport {runnerSessionTokenKey} from '@shipfox/node-auth-root-key';\nimport {signHs256, verifyHs256} from '@shipfox/node-jwt';\nimport {config} from '#config.js';\nimport {recordTokenIssued, recordTokenVerified} from '#metrics/index.js';\n\n// `aud`, `iat` and `exp` are set by the codec (jose); callers supply the business ids only.\nexport type IssueRunnerSessionTokenParams = Omit<RunnerSessionTokenClaims, 'aud' | 'iat' | 'exp'>;\n\nexport async function issueRunnerSessionToken(\n claims: IssueRunnerSessionTokenParams,\n): Promise<string> {\n const token = await signHs256({\n payload: {\n runnerSessionId: claims.runnerSessionId,\n workspaceId: claims.workspaceId,\n scope: claims.scope,\n labels: claims.labels,\n maxClaims: claims.maxClaims,\n },\n secret: runnerSessionTokenKey(),\n expiresIn: config.AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN,\n audience: RUNNER_SESSION_TOKEN_AUDIENCE,\n });\n recordTokenIssued('runner_session');\n return token;\n}\n\n/** Returns the claims on success, or `null` for any invalid input. Never throws. */\nexport async function verifyRunnerSessionToken(\n token: string,\n): Promise<RunnerSessionTokenClaims | null> {\n try {\n const claims = await verifyHs256({\n token,\n secret: runnerSessionTokenKey(),\n schema: runnerSessionTokenClaimsSchema,\n audience: RUNNER_SESSION_TOKEN_AUDIENCE,\n });\n recordTokenVerified('runner_session', 'ok');\n return claims;\n } catch {\n recordTokenVerified('runner_session', 'rejected');\n return null;\n }\n}\n"],"names":["RUNNER_SESSION_TOKEN_AUDIENCE","runnerSessionTokenClaimsSchema","runnerSessionTokenKey","signHs256","verifyHs256","config","recordTokenIssued","recordTokenVerified","issueRunnerSessionToken","claims","token","payload","runnerSessionId","workspaceId","scope","labels","maxClaims","secret","expiresIn","AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN","audience","verifyRunnerSessionToken","schema"],"mappings":"AAAA,SACEA,6BAA6B,EAE7BC,8BAA8B,QACzB,wBAAwB;AAC/B,SAAQC,qBAAqB,QAAO,8BAA8B;AAClE,SAAQC,SAAS,EAAEC,WAAW,QAAO,oBAAoB;AACzD,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,iBAAiB,EAAEC,mBAAmB,QAAO,oBAAoB;AAKzE,OAAO,eAAeC,wBACpBC,MAAqC;IAErC,MAAMC,QAAQ,MAAMP,UAAU;QAC5BQ,SAAS;YACPC,iBAAiBH,OAAOG,eAAe;YACvCC,aAAaJ,OAAOI,WAAW;YAC/BC,OAAOL,OAAOK,KAAK;YACnBC,QAAQN,OAAOM,MAAM;YACrBC,WAAWP,OAAOO,SAAS;QAC7B;QACAC,QAAQf;QACRgB,WAAWb,OAAOc,oCAAoC;QACtDC,UAAUpB;IACZ;IACAM,kBAAkB;IAClB,OAAOI;AACT;AAEA,kFAAkF,GAClF,OAAO,eAAeW,yBACpBX,KAAa;IAEb,IAAI;QACF,MAAMD,SAAS,MAAML,YAAY;YAC/BM;YACAO,QAAQf;YACRoB,QAAQrB;YACRmB,UAAUpB;QACZ;QACAO,oBAAoB,kBAAkB;QACtC,OAAOE;IACT,EAAE,OAAM;QACNF,oBAAoB,kBAAkB;QACtC,OAAO;IACT;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-user-moderation.d.ts","sourceRoot":"","sources":["../../src/db/admin-user-moderation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,yBAAyB,CAAC;AAwBvE,UAAU,2BAA2B;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,yBAAyB,EAAE,MAAM,CAAC;IAClC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,yBAAyB,CAAC;CAClC;AAyGD,iBAAe,4BAA4B,CACzC,MAAM,EAAE,2BAA2B,GAAG;IACpC,SAAS,EAAE,SAAS,GAAG,YAAY,GAAG,iBAAiB,CAAC;CACzD
|
|
1
|
+
{"version":3,"file":"admin-user-moderation.d.ts","sourceRoot":"","sources":["../../src/db/admin-user-moderation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,yBAAyB,CAAC;AAwBvE,UAAU,2BAA2B;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,yBAAyB,EAAE,MAAM,CAAC;IAClC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,yBAAyB,CAAC;CAClC;AAyGD,iBAAe,4BAA4B,CACzC,MAAM,EAAE,2BAA2B,GAAG;IACpC,SAAS,EAAE,SAAS,GAAG,YAAY,GAAG,iBAAiB,CAAC;CACzD;;QArFG,EAAE;QACF,KAAK;QACL,IAAI;QACJ,eAAe;QACf,MAAM;QACN,SAAS;QACT,SAAS;;;;GAsJd;AAED,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,4BAA4B,CAAC,CAAC,CAAC;AAE5F,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAE/B"}
|
package/dist/db/db.d.ts
CHANGED
|
@@ -107,7 +107,7 @@ export declare const schema: {
|
|
|
107
107
|
generated: undefined;
|
|
108
108
|
}, {}, {}>;
|
|
109
109
|
};
|
|
110
|
-
dialect:
|
|
110
|
+
dialect: 'pg';
|
|
111
111
|
}>;
|
|
112
112
|
adminCommandResults: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
113
113
|
name: "admin_command_results";
|
|
@@ -235,7 +235,7 @@ export declare const schema: {
|
|
|
235
235
|
generated: undefined;
|
|
236
236
|
}, {}, {}>;
|
|
237
237
|
};
|
|
238
|
-
dialect:
|
|
238
|
+
dialect: 'pg';
|
|
239
239
|
}>;
|
|
240
240
|
users: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
241
241
|
name: "users";
|
|
@@ -331,7 +331,7 @@ export declare const schema: {
|
|
|
331
331
|
tableName: "users";
|
|
332
332
|
dataType: "string";
|
|
333
333
|
columnType: "PgEnumColumn";
|
|
334
|
-
data: "active" | "
|
|
334
|
+
data: "active" | "deleted" | "suspended";
|
|
335
335
|
driverParam: string;
|
|
336
336
|
notNull: true;
|
|
337
337
|
hasDefault: true;
|
|
@@ -378,7 +378,7 @@ export declare const schema: {
|
|
|
378
378
|
generated: undefined;
|
|
379
379
|
}, {}, {}>;
|
|
380
380
|
};
|
|
381
|
-
dialect:
|
|
381
|
+
dialect: 'pg';
|
|
382
382
|
}>;
|
|
383
383
|
passwordResets: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
384
384
|
name: "password_resets";
|
|
@@ -487,7 +487,7 @@ export declare const schema: {
|
|
|
487
487
|
generated: undefined;
|
|
488
488
|
}, {}, {}>;
|
|
489
489
|
};
|
|
490
|
-
dialect:
|
|
490
|
+
dialect: 'pg';
|
|
491
491
|
}>;
|
|
492
492
|
refreshTokens: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
493
493
|
name: "refresh_tokens";
|
|
@@ -664,7 +664,7 @@ export declare const schema: {
|
|
|
664
664
|
generated: undefined;
|
|
665
665
|
}, {}, {}>;
|
|
666
666
|
};
|
|
667
|
-
dialect:
|
|
667
|
+
dialect: 'pg';
|
|
668
668
|
}>;
|
|
669
669
|
authOutbox: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
670
670
|
name: "outbox";
|
|
@@ -858,7 +858,7 @@ export declare const schema: {
|
|
|
858
858
|
generated: undefined;
|
|
859
859
|
}, {}, {}>;
|
|
860
860
|
};
|
|
861
|
-
dialect:
|
|
861
|
+
dialect: 'pg';
|
|
862
862
|
}>;
|
|
863
863
|
authRateLimits: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
864
864
|
name: "rate_limits";
|
|
@@ -1001,7 +1001,7 @@ export declare const schema: {
|
|
|
1001
1001
|
generated: undefined;
|
|
1002
1002
|
}, {}, {}>;
|
|
1003
1003
|
};
|
|
1004
|
-
dialect:
|
|
1004
|
+
dialect: 'pg';
|
|
1005
1005
|
}>;
|
|
1006
1006
|
};
|
|
1007
1007
|
export declare function db(): NodePgDatabase<{
|
|
@@ -1112,7 +1112,7 @@ export declare function db(): NodePgDatabase<{
|
|
|
1112
1112
|
generated: undefined;
|
|
1113
1113
|
}, {}, {}>;
|
|
1114
1114
|
};
|
|
1115
|
-
dialect:
|
|
1115
|
+
dialect: 'pg';
|
|
1116
1116
|
}>;
|
|
1117
1117
|
adminCommandResults: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
1118
1118
|
name: "admin_command_results";
|
|
@@ -1240,7 +1240,7 @@ export declare function db(): NodePgDatabase<{
|
|
|
1240
1240
|
generated: undefined;
|
|
1241
1241
|
}, {}, {}>;
|
|
1242
1242
|
};
|
|
1243
|
-
dialect:
|
|
1243
|
+
dialect: 'pg';
|
|
1244
1244
|
}>;
|
|
1245
1245
|
users: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
1246
1246
|
name: "users";
|
|
@@ -1336,7 +1336,7 @@ export declare function db(): NodePgDatabase<{
|
|
|
1336
1336
|
tableName: "users";
|
|
1337
1337
|
dataType: "string";
|
|
1338
1338
|
columnType: "PgEnumColumn";
|
|
1339
|
-
data: "active" | "
|
|
1339
|
+
data: "active" | "deleted" | "suspended";
|
|
1340
1340
|
driverParam: string;
|
|
1341
1341
|
notNull: true;
|
|
1342
1342
|
hasDefault: true;
|
|
@@ -1383,7 +1383,7 @@ export declare function db(): NodePgDatabase<{
|
|
|
1383
1383
|
generated: undefined;
|
|
1384
1384
|
}, {}, {}>;
|
|
1385
1385
|
};
|
|
1386
|
-
dialect:
|
|
1386
|
+
dialect: 'pg';
|
|
1387
1387
|
}>;
|
|
1388
1388
|
passwordResets: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
1389
1389
|
name: "password_resets";
|
|
@@ -1492,7 +1492,7 @@ export declare function db(): NodePgDatabase<{
|
|
|
1492
1492
|
generated: undefined;
|
|
1493
1493
|
}, {}, {}>;
|
|
1494
1494
|
};
|
|
1495
|
-
dialect:
|
|
1495
|
+
dialect: 'pg';
|
|
1496
1496
|
}>;
|
|
1497
1497
|
refreshTokens: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
1498
1498
|
name: "refresh_tokens";
|
|
@@ -1669,7 +1669,7 @@ export declare function db(): NodePgDatabase<{
|
|
|
1669
1669
|
generated: undefined;
|
|
1670
1670
|
}, {}, {}>;
|
|
1671
1671
|
};
|
|
1672
|
-
dialect:
|
|
1672
|
+
dialect: 'pg';
|
|
1673
1673
|
}>;
|
|
1674
1674
|
authOutbox: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
1675
1675
|
name: "outbox";
|
|
@@ -1863,7 +1863,7 @@ export declare function db(): NodePgDatabase<{
|
|
|
1863
1863
|
generated: undefined;
|
|
1864
1864
|
}, {}, {}>;
|
|
1865
1865
|
};
|
|
1866
|
-
dialect:
|
|
1866
|
+
dialect: 'pg';
|
|
1867
1867
|
}>;
|
|
1868
1868
|
authRateLimits: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
1869
1869
|
name: "rate_limits";
|
|
@@ -2006,7 +2006,7 @@ export declare function db(): NodePgDatabase<{
|
|
|
2006
2006
|
generated: undefined;
|
|
2007
2007
|
}, {}, {}>;
|
|
2008
2008
|
};
|
|
2009
|
-
dialect:
|
|
2009
|
+
dialect: 'pg';
|
|
2010
2010
|
}>;
|
|
2011
2011
|
}>;
|
|
2012
2012
|
export declare function closeDb(): void;
|
package/dist/db/rate-limits.d.ts
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
expiresAt: Date;
|
|
7
|
-
timeoutMs: number;
|
|
8
|
-
}
|
|
9
|
-
export interface ConsumeAuthRateLimitResult {
|
|
10
|
-
count: number;
|
|
11
|
-
expiresAt: Date;
|
|
12
|
-
}
|
|
13
|
-
export declare function consumeAuthRateLimit(params: ConsumeAuthRateLimitParams): Promise<ConsumeAuthRateLimitResult>;
|
|
14
|
-
export declare function pruneExpiredAuthRateLimits(params?: {
|
|
1
|
+
import { type ConsumeRateLimitParams, type ConsumeRateLimitResult } from '@shipfox/node-rate-limit';
|
|
2
|
+
export type ConsumeAuthRateLimitParams = ConsumeRateLimitParams<string, string>;
|
|
3
|
+
export type ConsumeAuthRateLimitResult = ConsumeRateLimitResult;
|
|
4
|
+
export declare const consumeAuthRateLimit: (params: ConsumeRateLimitParams<string, string>) => Promise<ConsumeRateLimitResult>;
|
|
5
|
+
export declare const pruneExpiredAuthRateLimits: (params?: {
|
|
15
6
|
now?: Date | undefined;
|
|
16
7
|
minIntervalMs?: number | undefined;
|
|
17
|
-
})
|
|
8
|
+
}) => Promise<number | undefined>;
|
|
18
9
|
//# sourceMappingURL=rate-limits.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limits.d.ts","sourceRoot":"","sources":["../../src/db/rate-limits.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rate-limits.d.ts","sourceRoot":"","sources":["../../src/db/rate-limits.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAE5B,MAAM,0BAA0B,CAAC;AA+ClC,MAAM,MAAM,0BAA0B,GAAG,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAChF,MAAM,MAAM,0BAA0B,GAAG,sBAAsB,CAAC;AAChE,eAAO,MAAM,oBAAoB,qFAAsB,CAAC;AACxD,eAAO,MAAM,0BAA0B;;;iCAAoB,CAAC"}
|
package/dist/db/rate-limits.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import { createRateLimitPersistence } from '@shipfox/node-rate-limit';
|
|
1
2
|
import { lt, sql } from 'drizzle-orm';
|
|
2
3
|
import { db } from './db.js';
|
|
3
4
|
import { authRateLimits } from './schema/rate-limits.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
const persistence = createRateLimitPersistence({
|
|
6
|
+
transaction: (callback)=>db().transaction(callback),
|
|
7
|
+
setStatementTimeout: async (transaction, timeoutMs)=>{
|
|
8
|
+
await transaction.execute(sql`select set_config('statement_timeout', ${`${timeoutMs}ms`}, true)`);
|
|
9
|
+
},
|
|
10
|
+
consume: async (transaction, params)=>{
|
|
11
|
+
const rows = await transaction.insert(authRateLimits).values({
|
|
8
12
|
action: params.action,
|
|
9
13
|
scope: params.scope,
|
|
10
14
|
identifierHmac: params.identifierHmac,
|
|
@@ -26,19 +30,14 @@ export async function consumeAuthRateLimit(params) {
|
|
|
26
30
|
count: authRateLimits.count,
|
|
27
31
|
expiresAt: authRateLimits.expiresAt
|
|
28
32
|
});
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (minIntervalMs > 0 && now.getTime() < nextPruneAt) return undefined;
|
|
39
|
-
nextPruneAt = now.getTime() + minIntervalMs;
|
|
40
|
-
const result = await db().delete(authRateLimits).where(lt(authRateLimits.expiresAt, now));
|
|
41
|
-
return result.rowCount ?? 0;
|
|
42
|
-
}
|
|
33
|
+
return rows[0];
|
|
34
|
+
},
|
|
35
|
+
prune: async (now)=>{
|
|
36
|
+
const result = await db().delete(authRateLimits).where(lt(authRateLimits.expiresAt, now));
|
|
37
|
+
return result.rowCount ?? 0;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
export const consumeAuthRateLimit = persistence.consume;
|
|
41
|
+
export const pruneExpiredAuthRateLimits = persistence.prune;
|
|
43
42
|
|
|
44
43
|
//# sourceMappingURL=rate-limits.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/rate-limits.ts"],"sourcesContent":["import {lt, sql} from 'drizzle-orm';\nimport {db} from './db.js';\nimport {authRateLimits} from './schema/rate-limits.js';\n\
|
|
1
|
+
{"version":3,"sources":["../../src/db/rate-limits.ts"],"sourcesContent":["import {\n type ConsumeRateLimitParams,\n type ConsumeRateLimitResult,\n createRateLimitPersistence,\n} from '@shipfox/node-rate-limit';\nimport {lt, sql} from 'drizzle-orm';\nimport {db} from './db.js';\nimport {authRateLimits} from './schema/rate-limits.js';\n\ntype AuthRateLimitTransaction = Parameters<Parameters<ReturnType<typeof db>['transaction']>[0]>[0];\n\nconst persistence = createRateLimitPersistence<AuthRateLimitTransaction>({\n transaction: (callback) => db().transaction(callback),\n setStatementTimeout: async (transaction, timeoutMs) => {\n await transaction.execute(\n sql`select set_config('statement_timeout', ${`${timeoutMs}ms`}, true)`,\n );\n },\n consume: async (transaction, params) => {\n const rows = await transaction\n .insert(authRateLimits)\n .values({\n action: params.action,\n scope: params.scope,\n identifierHmac: params.identifierHmac,\n windowStart: params.windowStart,\n count: 1,\n expiresAt: params.expiresAt,\n })\n .onConflictDoUpdate({\n target: [\n authRateLimits.action,\n authRateLimits.scope,\n authRateLimits.identifierHmac,\n authRateLimits.windowStart,\n ],\n set: {\n count: sql`${authRateLimits.count} + 1`,\n updatedAt: sql`now()`,\n },\n })\n .returning({count: authRateLimits.count, expiresAt: authRateLimits.expiresAt});\n\n return rows[0];\n },\n prune: async (now) => {\n const result = await db().delete(authRateLimits).where(lt(authRateLimits.expiresAt, now));\n return result.rowCount ?? 0;\n },\n});\n\nexport type ConsumeAuthRateLimitParams = ConsumeRateLimitParams<string, string>;\nexport type ConsumeAuthRateLimitResult = ConsumeRateLimitResult;\nexport const consumeAuthRateLimit = persistence.consume;\nexport const pruneExpiredAuthRateLimits = persistence.prune;\n"],"names":["createRateLimitPersistence","lt","sql","db","authRateLimits","persistence","transaction","callback","setStatementTimeout","timeoutMs","execute","consume","params","rows","insert","values","action","scope","identifierHmac","windowStart","count","expiresAt","onConflictDoUpdate","target","set","updatedAt","returning","prune","now","result","delete","where","rowCount","consumeAuthRateLimit","pruneExpiredAuthRateLimits"],"mappings":"AAAA,SAGEA,0BAA0B,QACrB,2BAA2B;AAClC,SAAQC,EAAE,EAAEC,GAAG,QAAO,cAAc;AACpC,SAAQC,EAAE,QAAO,UAAU;AAC3B,SAAQC,cAAc,QAAO,0BAA0B;AAIvD,MAAMC,cAAcL,2BAAqD;IACvEM,aAAa,CAACC,WAAaJ,KAAKG,WAAW,CAACC;IAC5CC,qBAAqB,OAAOF,aAAaG;QACvC,MAAMH,YAAYI,OAAO,CACvBR,GAAG,CAAC,uCAAuC,EAAE,GAAGO,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC;IAE1E;IACAE,SAAS,OAAOL,aAAaM;QAC3B,MAAMC,OAAO,MAAMP,YAChBQ,MAAM,CAACV,gBACPW,MAAM,CAAC;YACNC,QAAQJ,OAAOI,MAAM;YACrBC,OAAOL,OAAOK,KAAK;YACnBC,gBAAgBN,OAAOM,cAAc;YACrCC,aAAaP,OAAOO,WAAW;YAC/BC,OAAO;YACPC,WAAWT,OAAOS,SAAS;QAC7B,GACCC,kBAAkB,CAAC;YAClBC,QAAQ;gBACNnB,eAAeY,MAAM;gBACrBZ,eAAea,KAAK;gBACpBb,eAAec,cAAc;gBAC7Bd,eAAee,WAAW;aAC3B;YACDK,KAAK;gBACHJ,OAAOlB,GAAG,CAAC,EAAEE,eAAegB,KAAK,CAAC,IAAI,CAAC;gBACvCK,WAAWvB,GAAG,CAAC,KAAK,CAAC;YACvB;QACF,GACCwB,SAAS,CAAC;YAACN,OAAOhB,eAAegB,KAAK;YAAEC,WAAWjB,eAAeiB,SAAS;QAAA;QAE9E,OAAOR,IAAI,CAAC,EAAE;IAChB;IACAc,OAAO,OAAOC;QACZ,MAAMC,SAAS,MAAM1B,KAAK2B,MAAM,CAAC1B,gBAAgB2B,KAAK,CAAC9B,GAAGG,eAAeiB,SAAS,EAAEO;QACpF,OAAOC,OAAOG,QAAQ,IAAI;IAC5B;AACF;AAIA,OAAO,MAAMC,uBAAuB5B,YAAYM,OAAO,CAAC;AACxD,OAAO,MAAMuB,6BAA6B7B,YAAYsB,KAAK,CAAC"}
|
|
@@ -11,7 +11,7 @@ export interface CreateRefreshTokenParams {
|
|
|
11
11
|
export declare function createRefreshToken(params: CreateRefreshTokenParams): Promise<RefreshToken>;
|
|
12
12
|
export declare function createRefreshTokenForActiveUser(params: CreateRefreshTokenParams): Promise<RefreshToken | undefined>;
|
|
13
13
|
/**
|
|
14
|
-
* Looks up the live session token by hash
|
|
14
|
+
* Looks up the live session token by hash. It can still authenticate as
|
|
15
15
|
* the current session. Returns `undefined` for revoked, expired, or already
|
|
16
16
|
* rotated tokens; rotated rows survive in the table only for the grace window
|
|
17
17
|
* and are not "active".
|
|
@@ -22,8 +22,8 @@ export declare function findActiveRefreshTokenByHash(params: {
|
|
|
22
22
|
/**
|
|
23
23
|
* Looks up a non-revoked, non-expired token by hash, including ones already
|
|
24
24
|
* rotated. Rotation reads {@link RefreshToken.rotatedAt} to decide whether to
|
|
25
|
-
* rotate, honour the grace window, or reject reuse, so
|
|
26
|
-
* {@link findActiveRefreshTokenByHash}
|
|
25
|
+
* rotate, honour the grace window, or reject reuse, so, unlike
|
|
26
|
+
* {@link findActiveRefreshTokenByHash}, it must still see rotated rows.
|
|
27
27
|
*/
|
|
28
28
|
export declare function findRefreshTokenByHash(params: {
|
|
29
29
|
hashedToken: string;
|
|
@@ -35,7 +35,7 @@ export async function createRefreshTokenForActiveUser(params) {
|
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
|
-
* Looks up the live session token by hash
|
|
38
|
+
* Looks up the live session token by hash. It can still authenticate as
|
|
39
39
|
* the current session. Returns `undefined` for revoked, expired, or already
|
|
40
40
|
* rotated tokens; rotated rows survive in the table only for the grace window
|
|
41
41
|
* and are not "active".
|
|
@@ -48,8 +48,8 @@ export async function createRefreshTokenForActiveUser(params) {
|
|
|
48
48
|
/**
|
|
49
49
|
* Looks up a non-revoked, non-expired token by hash, including ones already
|
|
50
50
|
* rotated. Rotation reads {@link RefreshToken.rotatedAt} to decide whether to
|
|
51
|
-
* rotate, honour the grace window, or reject reuse, so
|
|
52
|
-
* {@link findActiveRefreshTokenByHash}
|
|
51
|
+
* rotate, honour the grace window, or reject reuse, so, unlike
|
|
52
|
+
* {@link findActiveRefreshTokenByHash}, it must still see rotated rows.
|
|
53
53
|
*/ export async function findRefreshTokenByHash(params) {
|
|
54
54
|
const rows = await db().select().from(refreshTokens).where(and(eq(refreshTokens.hashedToken, params.hashedToken), isNull(refreshTokens.revokedAt), gt(refreshTokens.expiresAt, sql`now()`))).limit(1);
|
|
55
55
|
const row = rows[0];
|