@shipfox/api-auth 12.2.0 → 15.0.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 +29 -0
- package/README.md +15 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -1
- package/dist/config.js.map +1 -1
- package/dist/core/auth.d.ts.map +1 -1
- package/dist/core/auth.js +1 -1
- package/dist/core/auth.js.map +1 -1
- package/dist/core/errors.d.ts +3 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +2 -2
- package/dist/core/errors.js.map +1 -1
- package/dist/core/ports.d.ts +3 -0
- package/dist/core/ports.d.ts.map +1 -1
- package/dist/core/ports.js +1 -1
- package/dist/core/ports.js.map +1 -1
- package/dist/core/signup-policy.d.ts.map +1 -1
- package/dist/core/signup-policy.js +2 -1
- package/dist/core/signup-policy.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/presentation/routes/registration/signup.d.ts +1 -1
- package/dist/presentation/routes/registration/signup.d.ts.map +1 -1
- package/dist/presentation/routes/registration/signup.js +6 -3
- package/dist/presentation/routes/registration/signup.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +12 -12
- package/src/config.test.ts +12 -0
- package/src/config.ts +11 -1
- package/src/core/auth-default-signup-policy.test.ts +2 -0
- package/src/core/auth.test.ts +1 -0
- package/src/core/auth.ts +4 -1
- package/src/core/errors.ts +6 -1
- package/src/core/ports.ts +6 -1
- package/src/core/signup-policy.test.ts +10 -2
- package/src/core/signup-policy.ts +1 -0
- package/src/index.test.ts +1 -0
- package/src/index.ts +1 -1
- package/src/presentation/routes/registration/signup.test.ts +21 -0
- package/src/presentation/routes/registration/signup.ts +3 -5
- 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 (406.61ms)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @shipfox/api-auth
|
|
2
2
|
|
|
3
|
+
## 15.0.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @shipfox/api-common-dto@15.0.0
|
|
8
|
+
- @shipfox/node-opentelemetry@0.6.5
|
|
9
|
+
- @shipfox/node-tokens@0.3.3
|
|
10
|
+
- @shipfox/api-auth-dto@15.0.0
|
|
11
|
+
- @shipfox/api-email-challenges@1.1.10
|
|
12
|
+
- @shipfox/api-workspaces-dto@15.0.0
|
|
13
|
+
- @shipfox/node-fastify@0.4.3
|
|
14
|
+
- @shipfox/node-mailer@0.2.6
|
|
15
|
+
- @shipfox/node-module@1.0.7
|
|
16
|
+
- @shipfox/api-auth-context@15.0.0
|
|
17
|
+
|
|
18
|
+
## 13.1.0
|
|
19
|
+
|
|
20
|
+
### Minor Changes
|
|
21
|
+
|
|
22
|
+
- a7fb52d: Supports server-declared Markdown in signup denial messages while keeping custom policy messages plain by default.
|
|
23
|
+
|
|
24
|
+
Existing environment-backed messages now interpret Markdown syntax. Older clients display the Markdown source as plain text.
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- Updated dependencies [6366319]
|
|
29
|
+
- @shipfox/node-email@0.3.5
|
|
30
|
+
- @shipfox/api-email-challenges@1.1.9
|
|
31
|
+
|
|
3
32
|
## 12.2.0
|
|
4
33
|
|
|
5
34
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -66,7 +66,7 @@ Required environment:
|
|
|
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. |
|
|
67
67
|
| `AUTH_SIGNUP_ALLOWED_EMAIL_DOMAINS` | none | Comma-separated email domains allowed to create accounts, such as `shipfox.io,acme.com`. |
|
|
68
68
|
| `AUTH_SIGNUP_ALLOWED_EMAILS` | none | Comma-separated exact email addresses allowed to create accounts. |
|
|
69
|
-
| `AUTH_SIGNUP_NOT_ALLOWED_MESSAGE` | none |
|
|
69
|
+
| `AUTH_SIGNUP_NOT_ALLOWED_MESSAGE` | none | Markdown message returned when the signup gate blocks an account. Accepts at most 500 characters. Defaults to "This Shipfox deployment does not accept new accounts right now." |
|
|
70
70
|
| `CLIENT_BASE_URL` | `http://localhost:5173` | Base URL used in email verification and password reset links. |
|
|
71
71
|
| `ADMIN_BOOTSTRAP_TOKEN` | none | Deployment secret accepted once to create the first administrator owner. Set it before bootstrap and remove or rotate it after successful bootstrap. |
|
|
72
72
|
|
|
@@ -90,6 +90,19 @@ The policy matches exact email addresses or exact domains. It does not match
|
|
|
90
90
|
subdomains or wildcards. `createAuthModule` applies these settings by default;
|
|
91
91
|
pass a `signupPolicy` to replace the environment-backed policy.
|
|
92
92
|
|
|
93
|
+
The Shipfox client renders `AUTH_SIGNUP_NOT_ALLOWED_MESSAGE` as sanitized
|
|
94
|
+
Markdown. The setting accepts at most 500 characters, and startup fails when it
|
|
95
|
+
is longer so the response does not cut a Markdown construct. Use an absolute
|
|
96
|
+
HTTP or HTTPS URL for a link:
|
|
97
|
+
|
|
98
|
+
```sh
|
|
99
|
+
AUTH_SIGNUP_NOT_ALLOWED_MESSAGE='New accounts require approval. [Request access](https://example.com/access).'
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The environment-backed policy declares its denial message as Markdown. A custom
|
|
103
|
+
`signupPolicy` denial message stays plain text unless its result includes
|
|
104
|
+
`format: 'markdown'`.
|
|
105
|
+
|
|
93
106
|
When `AUTH_PASSWORD_ENABLED=false`, the module does not register signup, login, password-reset, password-change, or email-verification routes. Refresh, logout, and current-session routes stay available. Another module must contribute a login method before the API server starts.
|
|
94
107
|
|
|
95
108
|
## Security model
|
|
@@ -321,7 +334,7 @@ It also exports lower-level pieces for tests and advanced integration:
|
|
|
321
334
|
- `createLeaseTokenAuthMethod()`: the Fastify auth method for job lease tokens.
|
|
322
335
|
- `issueRunnerSessionToken(claims)` / `verifyRunnerSessionToken(token)`: mint and verify runner session tokens.
|
|
323
336
|
- `issueJobLeaseToken(claims)` / `verifyJobLeaseToken(token)`: mint and verify job lease tokens.
|
|
324
|
-
- `createEnvironmentSignupPolicy()`: creates the environment-backed signup policy used by default. Pass `signupPolicy` to `createAuthModule` to replace it.
|
|
337
|
+
- `createEnvironmentSignupPolicy()`: creates the environment-backed signup policy used by default. Pass `signupPolicy` to `createAuthModule` to replace it. A denied custom policy can opt into sanitized Markdown with `format: 'markdown'`; without a format, its message stays plain text.
|
|
325
338
|
- `getClientContext(request)`: reads the authenticated user context from a Fastify request.
|
|
326
339
|
- `getAuthenticatedSessionContext(request)`: reads the user ID and required refresh-session ID from verified access-token claims. It does not check whether the refresh session is still active.
|
|
327
340
|
- `findUserByEmail({email})`: read-only lookup of the current owner of a normalized email; see below.
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,MAAM;;;;;;;;;;;;;;kDAqDjB,CAAC"}
|
package/dist/config.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { bool, createConfig, num, str } from '@shipfox/config';
|
|
2
|
+
import { SIGNUP_DENIAL_MESSAGE_MAX_LENGTH } from '#core/ports.js';
|
|
2
3
|
export const config = createConfig({
|
|
3
4
|
ADMIN_BOOTSTRAP_TOKEN: str({
|
|
4
5
|
desc: 'Deployment secret accepted once to create the first administrator owner. Set it before bootstrap and remove or rotate it after successful bootstrap.',
|
|
@@ -45,7 +46,7 @@ export const config = createConfig({
|
|
|
45
46
|
default: ''
|
|
46
47
|
}),
|
|
47
48
|
AUTH_SIGNUP_NOT_ALLOWED_MESSAGE: str({
|
|
48
|
-
desc: '
|
|
49
|
+
desc: 'Markdown message returned when the signup gate blocks an account. Use at most 500 characters. Optional. The default is This Shipfox deployment does not accept new accounts right now.',
|
|
49
50
|
default: undefined
|
|
50
51
|
}),
|
|
51
52
|
CLIENT_BASE_URL: str({
|
|
@@ -56,6 +57,9 @@ export const config = createConfig({
|
|
|
56
57
|
if (config.AUTH_SIGNUP_GATE_ENABLED && !hasSignupAllowlistEntry(config.AUTH_SIGNUP_ALLOWED_EMAIL_DOMAINS) && !hasSignupAllowlistEntry(config.AUTH_SIGNUP_ALLOWED_EMAILS)) {
|
|
57
58
|
throw new Error('AUTH_SIGNUP_GATE_ENABLED requires AUTH_SIGNUP_ALLOWED_EMAIL_DOMAINS or AUTH_SIGNUP_ALLOWED_EMAILS to be set.');
|
|
58
59
|
}
|
|
60
|
+
if (config.AUTH_SIGNUP_NOT_ALLOWED_MESSAGE !== undefined && config.AUTH_SIGNUP_NOT_ALLOWED_MESSAGE.length > SIGNUP_DENIAL_MESSAGE_MAX_LENGTH) {
|
|
61
|
+
throw new Error(`AUTH_SIGNUP_NOT_ALLOWED_MESSAGE must contain at most ${SIGNUP_DENIAL_MESSAGE_MAX_LENGTH} characters.`);
|
|
62
|
+
}
|
|
59
63
|
function hasSignupAllowlistEntry(value) {
|
|
60
64
|
return value.split(',').some((entry)=>entry.trim().length > 0);
|
|
61
65
|
}
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config.ts"],"sourcesContent":["import {bool, createConfig, num, str} from '@shipfox/config';\n\nexport const config = createConfig({\n ADMIN_BOOTSTRAP_TOKEN: str({\n desc: 'Deployment secret accepted once to create the first administrator owner. Set it before bootstrap and remove or rotate it after successful bootstrap.',\n default: undefined,\n }),\n AUTH_JWT_EXPIRES_IN: str({\n desc: 'How long an access token stays valid. Accepts a duration string such as 15m, 1h, or 7d.',\n default: '15m',\n }),\n AUTH_JOB_LEASE_TOKEN_EXPIRES_IN: str({\n desc: 'How long a job lease token stays valid. Set it longer than the longest job (JOB_MAX_DURATION is 60 minutes) plus a safety margin.',\n default: '90m',\n }),\n AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN: str({\n desc: 'How long a runner session token stays valid. A revoked registration token can leave existing sessions usable until this lifetime ends.',\n default: '1h',\n }),\n AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS: num({\n desc: 'How many days a refresh token stays valid before the user must sign in again.',\n default: 14,\n }),\n AUTH_REFRESH_ROTATION_GRACE_SECONDS: num({\n desc: 'Window after a refresh token is rotated during which the now-rotated token is still accepted, so concurrent refreshes from parallel tabs do not log the user out. Reuse past this window is treated as a compromise and revokes the session.',\n default: 30,\n }),\n AUTH_REFRESH_COOKIE_NAME: str({\n desc: 'Name of the browser cookie that stores the refresh token.',\n default: 'shipfox_refresh_token',\n }),\n AUTH_PASSWORD_ENABLED: bool({\n desc: 'Whether password login is available. Use true or false. Defaults to true. When false, password and email-verification routes are not registered, and server startup requires another module to contribute a login method.',\n default: true,\n }),\n AUTH_SIGNUP_GATE_ENABLED: bool({\n desc: 'Whether new account creation is restricted to the configured signup email allowlist. Defaults to false, which allows every signup.',\n default: false,\n }),\n AUTH_SIGNUP_ALLOWED_EMAIL_DOMAINS: str({\n desc: 'Comma-separated email domains allowed to create accounts, such as shipfox.io,acme.com. Required when AUTH_SIGNUP_GATE_ENABLED is true unless AUTH_SIGNUP_ALLOWED_EMAILS is set.',\n default: '',\n }),\n AUTH_SIGNUP_ALLOWED_EMAILS: str({\n desc: 'Comma-separated exact email addresses allowed to create accounts. Required when AUTH_SIGNUP_GATE_ENABLED is true unless AUTH_SIGNUP_ALLOWED_EMAIL_DOMAINS is set.',\n default: '',\n }),\n AUTH_SIGNUP_NOT_ALLOWED_MESSAGE: str({\n desc: '
|
|
1
|
+
{"version":3,"sources":["../src/config.ts"],"sourcesContent":["import {bool, createConfig, num, str} from '@shipfox/config';\nimport {SIGNUP_DENIAL_MESSAGE_MAX_LENGTH} from '#core/ports.js';\n\nexport const config = createConfig({\n ADMIN_BOOTSTRAP_TOKEN: str({\n desc: 'Deployment secret accepted once to create the first administrator owner. Set it before bootstrap and remove or rotate it after successful bootstrap.',\n default: undefined,\n }),\n AUTH_JWT_EXPIRES_IN: str({\n desc: 'How long an access token stays valid. Accepts a duration string such as 15m, 1h, or 7d.',\n default: '15m',\n }),\n AUTH_JOB_LEASE_TOKEN_EXPIRES_IN: str({\n desc: 'How long a job lease token stays valid. Set it longer than the longest job (JOB_MAX_DURATION is 60 minutes) plus a safety margin.',\n default: '90m',\n }),\n AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN: str({\n desc: 'How long a runner session token stays valid. A revoked registration token can leave existing sessions usable until this lifetime ends.',\n default: '1h',\n }),\n AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS: num({\n desc: 'How many days a refresh token stays valid before the user must sign in again.',\n default: 14,\n }),\n AUTH_REFRESH_ROTATION_GRACE_SECONDS: num({\n desc: 'Window after a refresh token is rotated during which the now-rotated token is still accepted, so concurrent refreshes from parallel tabs do not log the user out. Reuse past this window is treated as a compromise and revokes the session.',\n default: 30,\n }),\n AUTH_REFRESH_COOKIE_NAME: str({\n desc: 'Name of the browser cookie that stores the refresh token.',\n default: 'shipfox_refresh_token',\n }),\n AUTH_PASSWORD_ENABLED: bool({\n desc: 'Whether password login is available. Use true or false. Defaults to true. When false, password and email-verification routes are not registered, and server startup requires another module to contribute a login method.',\n default: true,\n }),\n AUTH_SIGNUP_GATE_ENABLED: bool({\n desc: 'Whether new account creation is restricted to the configured signup email allowlist. Defaults to false, which allows every signup.',\n default: false,\n }),\n AUTH_SIGNUP_ALLOWED_EMAIL_DOMAINS: str({\n desc: 'Comma-separated email domains allowed to create accounts, such as shipfox.io,acme.com. Required when AUTH_SIGNUP_GATE_ENABLED is true unless AUTH_SIGNUP_ALLOWED_EMAILS is set.',\n default: '',\n }),\n AUTH_SIGNUP_ALLOWED_EMAILS: str({\n desc: 'Comma-separated exact email addresses allowed to create accounts. Required when AUTH_SIGNUP_GATE_ENABLED is true unless AUTH_SIGNUP_ALLOWED_EMAIL_DOMAINS is set.',\n default: '',\n }),\n AUTH_SIGNUP_NOT_ALLOWED_MESSAGE: str({\n desc: 'Markdown message returned when the signup gate blocks an account. Use at most 500 characters. Optional. The default is This Shipfox deployment does not accept new accounts right now.',\n default: undefined,\n }),\n CLIENT_BASE_URL: str({\n desc: 'Base URL of the client app. Used to build links in emails such as password resets.',\n default: 'http://localhost:5173',\n }),\n});\n\nif (\n config.AUTH_SIGNUP_GATE_ENABLED &&\n !hasSignupAllowlistEntry(config.AUTH_SIGNUP_ALLOWED_EMAIL_DOMAINS) &&\n !hasSignupAllowlistEntry(config.AUTH_SIGNUP_ALLOWED_EMAILS)\n) {\n throw new Error(\n 'AUTH_SIGNUP_GATE_ENABLED requires AUTH_SIGNUP_ALLOWED_EMAIL_DOMAINS or AUTH_SIGNUP_ALLOWED_EMAILS to be set.',\n );\n}\n\nif (\n config.AUTH_SIGNUP_NOT_ALLOWED_MESSAGE !== undefined &&\n config.AUTH_SIGNUP_NOT_ALLOWED_MESSAGE.length > SIGNUP_DENIAL_MESSAGE_MAX_LENGTH\n) {\n throw new Error(\n `AUTH_SIGNUP_NOT_ALLOWED_MESSAGE must contain at most ${SIGNUP_DENIAL_MESSAGE_MAX_LENGTH} characters.`,\n );\n}\n\nfunction hasSignupAllowlistEntry(value: string): boolean {\n return value.split(',').some((entry) => entry.trim().length > 0);\n}\n"],"names":["bool","createConfig","num","str","SIGNUP_DENIAL_MESSAGE_MAX_LENGTH","config","ADMIN_BOOTSTRAP_TOKEN","desc","default","undefined","AUTH_JWT_EXPIRES_IN","AUTH_JOB_LEASE_TOKEN_EXPIRES_IN","AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN","AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS","AUTH_REFRESH_ROTATION_GRACE_SECONDS","AUTH_REFRESH_COOKIE_NAME","AUTH_PASSWORD_ENABLED","AUTH_SIGNUP_GATE_ENABLED","AUTH_SIGNUP_ALLOWED_EMAIL_DOMAINS","AUTH_SIGNUP_ALLOWED_EMAILS","AUTH_SIGNUP_NOT_ALLOWED_MESSAGE","CLIENT_BASE_URL","hasSignupAllowlistEntry","Error","length","value","split","some","entry","trim"],"mappings":"AAAA,SAAQA,IAAI,EAAEC,YAAY,EAAEC,GAAG,EAAEC,GAAG,QAAO,kBAAkB;AAC7D,SAAQC,gCAAgC,QAAO,iBAAiB;AAEhE,OAAO,MAAMC,SAASJ,aAAa;IACjCK,uBAAuBH,IAAI;QACzBI,MAAM;QACNC,SAASC;IACX;IACAC,qBAAqBP,IAAI;QACvBI,MAAM;QACNC,SAAS;IACX;IACAG,iCAAiCR,IAAI;QACnCI,MAAM;QACNC,SAAS;IACX;IACAI,sCAAsCT,IAAI;QACxCI,MAAM;QACNC,SAAS;IACX;IACAK,oCAAoCX,IAAI;QACtCK,MAAM;QACNC,SAAS;IACX;IACAM,qCAAqCZ,IAAI;QACvCK,MAAM;QACNC,SAAS;IACX;IACAO,0BAA0BZ,IAAI;QAC5BI,MAAM;QACNC,SAAS;IACX;IACAQ,uBAAuBhB,KAAK;QAC1BO,MAAM;QACNC,SAAS;IACX;IACAS,0BAA0BjB,KAAK;QAC7BO,MAAM;QACNC,SAAS;IACX;IACAU,mCAAmCf,IAAI;QACrCI,MAAM;QACNC,SAAS;IACX;IACAW,4BAA4BhB,IAAI;QAC9BI,MAAM;QACNC,SAAS;IACX;IACAY,iCAAiCjB,IAAI;QACnCI,MAAM;QACNC,SAASC;IACX;IACAY,iBAAiBlB,IAAI;QACnBI,MAAM;QACNC,SAAS;IACX;AACF,GAAG;AAEH,IACEH,OAAOY,wBAAwB,IAC/B,CAACK,wBAAwBjB,OAAOa,iCAAiC,KACjE,CAACI,wBAAwBjB,OAAOc,0BAA0B,GAC1D;IACA,MAAM,IAAII,MACR;AAEJ;AAEA,IACElB,OAAOe,+BAA+B,KAAKX,aAC3CJ,OAAOe,+BAA+B,CAACI,MAAM,GAAGpB,kCAChD;IACA,MAAM,IAAImB,MACR,CAAC,qDAAqD,EAAEnB,iCAAiC,YAAY,CAAC;AAE1G;AAEA,SAASkB,wBAAwBG,KAAa;IAC5C,OAAOA,MAAMC,KAAK,CAAC,KAAKC,IAAI,CAAC,CAACC,QAAUA,MAAMC,IAAI,GAAGL,MAAM,GAAG;AAChE"}
|
package/dist/core/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/core/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,SAAS,EAAc,MAAM,uBAAuB,CAAC;AAOlE,OAAO,EACL,KAAK,2BAA2B,EAEjC,MAAM,0CAA0C,CAAC;AAyBlD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EACL,8BAA8B,EAC9B,qBAAqB,EAErB,uBAAuB,EAMvB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/core/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/core/auth.js
CHANGED
|
@@ -25,7 +25,7 @@ async function assertSignupAllowed(params) {
|
|
|
25
25
|
source: params.source
|
|
26
26
|
});
|
|
27
27
|
if (!result.allowed) {
|
|
28
|
-
throw new SignupNotAllowedError(result.message ?? DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE);
|
|
28
|
+
throw new SignupNotAllowedError(result.message ?? DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE, result.format);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
let dummyHashCache;
|
package/dist/core/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/auth.ts"],"sourcesContent":["import {type AdminRole, emailSchema} from '@shipfox/api-auth-dto';\nimport {\n confirmEmailChallenge,\n consumeEmailChallengeProof,\n createEmailChallenge,\n resendEmailChallenge,\n} from '@shipfox/api-email-challenges';\nimport {\n type WorkspacesInterModuleClient,\n workspacesInterModuleContract,\n} from '@shipfox/api-workspaces-dto/inter-module';\nimport {isInterModuleKnownError} from '@shipfox/inter-module';\nimport {userAccessTokenKey} from '@shipfox/node-auth-root-key';\nimport {generateOpaqueToken, hashOpaqueToken} from '@shipfox/node-tokens';\nimport {config} from '#config.js';\nimport {consumePasswordReset, createPasswordReset} from '#db/password-resets.js';\nimport {\n createRefreshTokenForActiveUser,\n findActiveRefreshTokenByHash,\n findRefreshTokenByHash,\n revokeRefreshSession,\n revokeRefreshTokensForUser,\n rotateRefreshToken,\n} from '#db/refresh-tokens.js';\nimport {\n createUser as createDbUser,\n findUserByEmail,\n findUserById,\n markEmailVerified,\n provisionUser as provisionDbUser,\n updateUserPassword,\n} from '#db/users.js';\nimport {type AuthTokenRefreshOutcome, recordTokenRefreshed} from '#metrics/index.js';\nimport {getCurrentAdminRole} from './admin-role.js';\nimport type {RefreshToken} from './entities/refresh-token.js';\nimport type {User} from './entities/user.js';\nimport {\n AuthDependencyUnavailableError,\n EmailNotVerifiedError,\n EmailTakenError,\n InvalidCredentialsError,\n InvitationEmailMismatchError,\n SignupNotAllowedError,\n TokenAlreadyUsedError,\n TokenExpiredError,\n TokenInvalidError,\n UserNotFoundError,\n} from './errors.js';\nimport {signUserToken, type TokenMembership} from './jwt.js';\nimport {hashPassword, verifyPassword} from './password.js';\nimport type {SignupPolicy} from './ports.js';\nimport {createEnvironmentSignupPolicy} from './signup-policy.js';\n\nconst RESET_TTL_HOURS = 1;\nconst PASSWORD_VERIFICATION_PURPOSE = 'password-verification';\nconst DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE =\n 'This Shipfox deployment does not accept new accounts right now.';\n\nconst defaultSignupPolicy: SignupPolicy = createEnvironmentSignupPolicy();\n\nasync function assertSignupAllowed(params: {\n signupPolicy?: SignupPolicy | undefined;\n email: string;\n emailVerified: boolean;\n source: string;\n}): Promise<void> {\n const result = await (params.signupPolicy ?? defaultSignupPolicy).isSignupAllowed({\n email: params.email,\n emailVerified: params.emailVerified,\n source: params.source,\n });\n\n if (!result.allowed) {\n throw new SignupNotAllowedError(result.message ?? DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE);\n }\n}\n\nlet dummyHashCache: string | undefined;\nasync function getDummyHash(): Promise<string> {\n if (!dummyHashCache) {\n dummyHashCache = await hashPassword({password: 'dummy-for-timing-parity'});\n }\n return dummyHashCache;\n}\n\nfunction hoursFromNow(hours: number): Date {\n return new Date(Date.now() + hours * 60 * 60 * 1000);\n}\n\nfunction daysFromNow(days: number): Date {\n return new Date(Date.now() + days * 24 * 60 * 60 * 1000);\n}\n\nfunction recordRefreshOutcome(outcome: AuthTokenRefreshOutcome): void {\n recordTokenRefreshed(outcome);\n}\n\ntype TokenMemberships = TokenMembership[];\n\nasync function loadTokenMemberships(\n userId: string,\n workspaces: WorkspacesInterModuleClient,\n): Promise<TokenMemberships> {\n const memberships = await workspaces\n .listMembershipsForTokenClaims({userId})\n .catch((error: unknown) => {\n throw new AuthDependencyUnavailableError('workspaces', error);\n });\n return memberships.memberships;\n}\n\nasync function signAccessToken(\n user: User,\n memberships: TokenMemberships,\n refreshSessionId: string,\n): Promise<string> {\n return await signUserToken({\n userId: user.id,\n email: user.email,\n name: user.name,\n memberships,\n refreshSessionId,\n secret: userAccessTokenKey(),\n expiresIn: config.AUTH_JWT_EXPIRES_IN,\n });\n}\n\nfunction isWithinRotationGrace(refreshToken: RefreshToken): boolean {\n if (!refreshToken.rotatedAt) return false;\n const graceMs = config.AUTH_REFRESH_ROTATION_GRACE_SECONDS * 1000;\n return Date.now() - refreshToken.rotatedAt.getTime() <= graceMs;\n}\n\nasync function createRefreshSession(\n user: User,\n refreshSessionId: string,\n): Promise<{refreshToken: string; refreshSessionId: string}> {\n const refreshToken = generateOpaqueToken('refreshToken');\n const session = await createRefreshTokenForActiveUser({\n sessionId: refreshSessionId,\n userId: user.id,\n hashedToken: hashOpaqueToken(refreshToken),\n expiresAt: daysFromNow(config.AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS),\n });\n if (!session) throw new InvalidCredentialsError();\n return {refreshToken, refreshSessionId: session.sessionId};\n}\n\nasync function createSessionTokens(\n user: User,\n workspaces: WorkspacesInterModuleClient,\n): Promise<{token: string; refreshToken: string; adminRole: AdminRole | null}> {\n const refreshSessionId = crypto.randomUUID();\n const memberships = await loadTokenMemberships(user.id, workspaces);\n const token = await signAccessToken(user, memberships, refreshSessionId);\n const adminRole = await getCurrentAdminRole({userId: user.id});\n const {refreshToken} = await createRefreshSession(user, refreshSessionId);\n return {token, refreshToken, adminRole};\n}\n\nfunction passwordResetLink(rawToken: string): string {\n return `${config.CLIENT_BASE_URL}/auth/reset?token=${rawToken}`;\n}\n\nfunction invitationErrorFromKnownError(\n error: unknown,\n method: 'preflightInvitationAcceptance' | 'acceptInvitation',\n): Error {\n const contractMethod = workspacesInterModuleContract.methods[method];\n if (!isInterModuleKnownError(contractMethod, error)) return error as Error;\n\n if (error.code === 'invitation-token-invalid')\n return new TokenInvalidError('Invitation token is invalid');\n if (error.code === 'invitation-token-used') return new TokenAlreadyUsedError();\n if (error.code === 'invitation-token-expired') return new TokenExpiredError();\n return new InvitationEmailMismatchError();\n}\n\nfunction invitationErrorMessage(\n code:\n | 'invitation-token-invalid'\n | 'invitation-token-used'\n | 'invitation-token-expired'\n | 'invitation-email-mismatch',\n): string {\n if (code === 'invitation-token-invalid') return 'Invitation token is invalid';\n if (code === 'invitation-token-used') return 'Invitation has already been accepted';\n if (code === 'invitation-token-expired') return 'Invitation has expired';\n return 'Signup email does not match the invitation';\n}\n\nexport interface SignupParams {\n email: string;\n password: string;\n name?: string | undefined;\n signupPolicy?: SignupPolicy | undefined;\n}\n\nexport interface ProvisionUserParams {\n email: string;\n name?: string | null | undefined;\n signupPolicy?: SignupPolicy | undefined;\n}\n\n/**\n * Creates a verified, password-less user for an external identity provider.\n * Existing users are returned unchanged, including their password and profile.\n */\nexport async function provisionUser(params: ProvisionUserParams): Promise<User> {\n const email = emailSchema.parse(params.email);\n const existing = await findUserByEmail({email});\n if (existing) return existing;\n\n await assertSignupAllowed({\n signupPolicy: params.signupPolicy,\n email,\n emailVerified: true,\n source: 'external-identity',\n });\n\n return await provisionDbUser({\n email,\n name: params.name ?? null,\n });\n}\n\nexport type SignupResult = User & {\n emailChallenge: {id: string; nextResendAvailableAt: Date};\n};\n\nexport async function signup(params: SignupParams & {sourceIp?: string}): Promise<SignupResult> {\n const email = emailSchema.parse(params.email);\n const existing = await findUserByEmail({email});\n if (existing) {\n const canResumeVerification =\n existing.status === 'active' &&\n existing.emailVerifiedAt === null &&\n existing.hashedPassword !== null &&\n (await verifyPassword({password: params.password, hash: existing.hashedPassword}));\n if (canResumeVerification) {\n const emailChallenge = await createEmailChallenge({\n email: existing.email,\n purpose: PASSWORD_VERIFICATION_PURPOSE,\n continuation: existing.id,\n idempotencyKey: existing.id,\n sourceIp: params.sourceIp ?? '0.0.0.0',\n });\n return {...existing, emailChallenge};\n }\n throw new EmailTakenError(email);\n }\n\n await assertSignupAllowed({\n signupPolicy: params.signupPolicy,\n email,\n emailVerified: false,\n source: 'password',\n });\n\n const hashedPassword = await hashPassword({password: params.password});\n const user = await createDbUser({\n email,\n hashedPassword,\n name: params.name ?? null,\n signedUp: {viaInvitation: false},\n });\n\n const emailChallenge = await createEmailChallenge({\n email: user.email,\n purpose: PASSWORD_VERIFICATION_PURPOSE,\n continuation: user.id,\n idempotencyKey: user.id,\n sourceIp: params.sourceIp ?? '0.0.0.0',\n });\n\n return {...user, emailChallenge};\n}\n\nexport interface SignupWithInvitationParams extends SignupParams {\n invitationToken: string;\n workspaces: WorkspacesInterModuleClient;\n}\n\nexport interface SignupWithInvitationResult extends LoginResult {\n membership: {id: string; userId: string; workspaceId: string} | null;\n acceptError?: {code: string; message: string};\n}\n\nexport async function signupWithInvitation(\n params: SignupWithInvitationParams,\n): Promise<SignupWithInvitationResult> {\n // Step 1: Pre-validate the invitation BEFORE any user write so an invalid\n // token never produces an orphan user. Re-validation happens again inside\n // acceptWorkspaceInvitation (race-safe) after the user is created.\n try {\n await params.workspaces.preflightInvitationAcceptance({\n token: params.invitationToken,\n email: params.email,\n });\n } catch (error) {\n throw invitationErrorFromKnownError(error, 'preflightInvitationAcceptance');\n }\n\n // Step 2: Create the user as verified. The invitation email is the proof of\n // ownership, and this avoids a second auth-table write just to mark verified.\n const existing = await findUserByEmail({email: params.email});\n if (existing) {\n throw new EmailTakenError(params.email);\n }\n const hashedPassword = await hashPassword({password: params.password});\n const user = await createDbUser({\n email: params.email,\n hashedPassword,\n name: params.name ?? null,\n emailVerifiedAt: new Date(),\n signedUp: {viaInvitation: true},\n });\n\n // Step 3: Accept the invitation through the workspaces module boundary.\n // This is intentionally not one database transaction with auth; treat it like\n // calling another service. If it fails after user creation, return a verified\n // signed-in user and an explicit retryable accept error.\n let membership: SignupWithInvitationResult['membership'] = null;\n let acceptError: SignupWithInvitationResult['acceptError'];\n try {\n const result = await params.workspaces.acceptInvitation({\n token: params.invitationToken,\n userId: user.id,\n email: user.email,\n name: user.name,\n });\n membership = {\n id: result.membership.id,\n userId: result.membership.userId,\n workspaceId: result.membership.workspaceId,\n };\n } catch (error) {\n if (isInterModuleKnownError(workspacesInterModuleContract.methods.acceptInvitation, error)) {\n acceptError = {code: error.code, message: invitationErrorMessage(error.code)};\n } else {\n acceptError = {\n code: 'AcceptFailed',\n message: 'Could not accept the invitation; please retry from the invite link.',\n };\n }\n }\n\n // Step 4: Issue session. createSessionTokens reads memberships through the\n // workspaces module API, so a successful accept is reflected in the JWT.\n const {token, refreshToken, adminRole} = await createSessionTokens(user, params.workspaces);\n\n if (acceptError) {\n return {token, refreshToken, user, membership, acceptError, adminRole};\n }\n return {token, refreshToken, user, membership, adminRole};\n}\n\nexport interface CreateUserParams extends SignupParams {\n verified: boolean;\n}\n\nexport async function createUser(params: CreateUserParams): Promise<User> {\n const existing = await findUserByEmail({email: params.email});\n if (existing) {\n throw new EmailTakenError(params.email);\n }\n\n const hashedPassword = await hashPassword({password: params.password});\n const user = await createDbUser({\n email: params.email,\n hashedPassword,\n name: params.name ?? null,\n emailVerifiedAt: params.verified ? new Date() : null,\n });\n\n return user;\n}\n\nexport interface LoginParams {\n email: string;\n password: string;\n workspaces: WorkspacesInterModuleClient;\n}\n\nexport interface LoginResult {\n token: string;\n refreshToken: string;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport async function login(params: LoginParams): Promise<LoginResult> {\n const user = await findUserByEmail({email: params.email});\n\n if (!user) {\n await verifyPassword({password: params.password, hash: await getDummyHash()});\n throw new InvalidCredentialsError();\n }\n\n const hasPassword = user.hashedPassword !== null;\n const ok = await verifyPassword({\n password: params.password,\n hash: user.hashedPassword ?? (await getDummyHash()),\n });\n if (!hasPassword || !ok || user.status !== 'active') {\n throw new InvalidCredentialsError();\n }\n\n if (user.emailVerifiedAt === null) {\n throw new EmailNotVerifiedError();\n }\n\n const {token, refreshToken, adminRole} = await createSessionTokens(user, params.workspaces);\n\n return {token, refreshToken, user, adminRole};\n}\n\nexport interface CreateSessionForUserParams {\n userId?: string | undefined;\n email?: string | undefined;\n workspaces: WorkspacesInterModuleClient;\n}\n\nexport interface CreateSessionForUserResult {\n token: string;\n refreshToken: string;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport type CreateSessionForUserError =\n | AuthDependencyUnavailableError\n | EmailNotVerifiedError\n | InvalidCredentialsError\n | UserNotFoundError;\n\nexport async function createSessionForUser(\n params: CreateSessionForUserParams,\n): Promise<CreateSessionForUserResult> {\n const user = params.userId\n ? await findUserById({id: params.userId})\n : params.email\n ? await findUserByEmail({email: emailSchema.parse(params.email)})\n : undefined;\n\n if (!user) {\n throw new UserNotFoundError(params.userId ?? params.email ?? 'unknown');\n }\n if (user.emailVerifiedAt === null) {\n throw new EmailNotVerifiedError();\n }\n if (user.status !== 'active') {\n throw new InvalidCredentialsError();\n }\n\n const {token, refreshToken, adminRole} = await createSessionTokens(user, params.workspaces);\n\n return {token, refreshToken, user, adminRole};\n}\n\nexport interface RefreshAccessTokenResult {\n token: string;\n /** Undefined on a grace-window hit: keep the existing cookie instead of rotating it. */\n refreshToken: string | undefined;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport async function refreshAccessToken(params: {\n refreshToken: string;\n workspaces: WorkspacesInterModuleClient;\n}): Promise<RefreshAccessTokenResult> {\n const currentHashedToken = hashOpaqueToken(params.refreshToken);\n const current = await findRefreshTokenByHash({hashedToken: currentHashedToken});\n if (!current) {\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n\n const user = await findUserById({id: current.userId});\n if (user?.status !== 'active') {\n await revokeRefreshSession({sessionId: current.sessionId, userId: current.userId});\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n const adminRole = await getCurrentAdminRole({userId: user.id});\n\n // Within the grace window a rotated token means a concurrent refresh (e.g. a\n // second tab); past it, reuse of a retired token means a compromised session.\n if (current.rotatedAt) {\n if (isWithinRotationGrace(current)) {\n const memberships = await loadTokenMemberships(user.id, params.workspaces);\n const token = await signAccessToken(user, memberships, current.sessionId);\n recordRefreshOutcome('grace');\n return {token, refreshToken: undefined, user, adminRole};\n }\n await revokeRefreshTokensForUser({userId: user.id});\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token reused after rotation');\n }\n\n // Keep the external membership snapshot ahead of rotation so an outage\n // cannot retire a refresh token that the caller can still retry.\n const memberships = await loadTokenMemberships(user.id, params.workspaces);\n\n // Losing the CAS requires a state check: another request may have rotated,\n // revoked, or expired the token before this refresh could claim it.\n const nextRefreshToken = generateOpaqueToken('refreshToken');\n const rotated = await rotateRefreshToken({\n userId: current.userId,\n id: current.id,\n currentHashedToken,\n nextHashedToken: hashOpaqueToken(nextRefreshToken),\n expiresAt: daysFromNow(config.AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS),\n });\n if (!rotated) {\n const latestUser = await findUserById({id: current.userId});\n if (latestUser?.status !== 'active') {\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n const latest = await findRefreshTokenByHash({hashedToken: currentHashedToken});\n if (!latest || !isWithinRotationGrace(latest)) {\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n const token = await signAccessToken(user, memberships, latest.sessionId);\n recordRefreshOutcome('grace');\n return {token, refreshToken: undefined, user, adminRole};\n }\n\n const token = await signAccessToken(user, memberships, current.sessionId);\n recordRefreshOutcome('rotated');\n return {token, refreshToken: nextRefreshToken, user, adminRole};\n}\n\nexport interface ConfirmEmailVerificationResult {\n token: string;\n refreshToken: string;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport interface ResendEmailVerificationResult {\n nextResendAvailableAt: Date;\n}\n\nexport async function confirmEmailVerification(params: {\n email: string;\n challengeId: string;\n code: string;\n workspaces: WorkspacesInterModuleClient;\n}): Promise<ConfirmEmailVerificationResult> {\n const user = await findUserByEmail({email: emailSchema.parse(params.email)});\n if (!user) throw new TokenInvalidError('Verification code is invalid or expired');\n await confirmEmailChallenge({id: params.challengeId, code: params.code, continuation: user.id});\n await consumeEmailChallengeProof({\n id: params.challengeId,\n purpose: PASSWORD_VERIFICATION_PURPOSE,\n continuation: user.id,\n });\n\n const verifiedUser = await markEmailVerified({userId: user.id});\n if (verifiedUser?.status !== 'active') {\n throw new TokenInvalidError('Verification code is invalid or expired');\n }\n\n const {token, refreshToken, adminRole} = await createSessionTokens(\n verifiedUser,\n params.workspaces,\n );\n\n return {token, refreshToken, user: verifiedUser, adminRole};\n}\n\nexport async function resendEmailVerification(params: {\n email: string;\n challengeId: string;\n sourceIp: string;\n}): Promise<ResendEmailVerificationResult> {\n const user = await findUserByEmail({email: emailSchema.parse(params.email)});\n if (!user || user.emailVerifiedAt || user.status !== 'active') {\n return {nextResendAvailableAt: new Date()};\n }\n const result = await resendEmailChallenge({\n id: params.challengeId,\n continuation: user.id,\n sourceIp: params.sourceIp,\n });\n\n return {nextResendAvailableAt: result.nextResendAvailableAt};\n}\n\nexport async function requestPasswordReset(params: {email: string}): Promise<void> {\n const user = await findUserByEmail({email: params.email});\n if (user?.status !== 'active' || user.hashedPassword === null) {\n return;\n }\n\n const rawToken = generateOpaqueToken('passwordReset');\n await createPasswordReset({\n userId: user.id,\n hashedToken: hashOpaqueToken(rawToken),\n expiresAt: hoursFromNow(RESET_TTL_HOURS),\n sendEmail: {\n email: user.email,\n resetLink: passwordResetLink(rawToken),\n expiresInHours: RESET_TTL_HOURS,\n },\n });\n}\n\nexport interface ConfirmPasswordResetResult {\n token: string;\n refreshToken: string;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport async function confirmPasswordReset(params: {\n token: string;\n newPassword: string;\n workspaces: WorkspacesInterModuleClient;\n}): Promise<ConfirmPasswordResetResult> {\n const consumed = await consumePasswordReset({hashedToken: hashOpaqueToken(params.token)});\n if (!consumed) {\n throw new TokenInvalidError('Reset token is invalid or expired');\n }\n\n const existingUser = await findUserById({id: consumed.userId});\n if (existingUser?.status !== 'active' || existingUser.hashedPassword === null) {\n throw new TokenInvalidError('Reset token is invalid or expired');\n }\n\n const hashedPassword = await hashPassword({password: params.newPassword});\n const user = await updateUserPassword({userId: consumed.userId, hashedPassword});\n if (user?.status !== 'active') {\n throw new TokenInvalidError('Reset token is invalid or expired');\n }\n\n await revokeRefreshTokensForUser({userId: consumed.userId});\n\n const {token, refreshToken, adminRole} = await createSessionTokens(user, params.workspaces);\n\n return {token, refreshToken, user, adminRole};\n}\n\nexport async function changePassword(params: {\n userId: string;\n currentPassword: string;\n newPassword: string;\n refreshToken?: string | undefined;\n}): Promise<void> {\n const user = await findUserById({id: params.userId});\n if (!user) {\n throw new UserNotFoundError(params.userId);\n }\n\n const hasPassword = user.hashedPassword !== null;\n const ok = await verifyPassword({\n password: params.currentPassword,\n hash: user.hashedPassword ?? (await getDummyHash()),\n });\n if (!hasPassword || !ok) {\n throw new InvalidCredentialsError();\n }\n\n const hashedPassword = await hashPassword({password: params.newPassword});\n const currentRefreshSession = params.refreshToken\n ? await findActiveRefreshTokenByHash({hashedToken: hashOpaqueToken(params.refreshToken)})\n : undefined;\n const exceptRefreshTokenId =\n currentRefreshSession?.userId === user.id ? currentRefreshSession.id : undefined;\n\n await updateUserPassword({userId: user.id, hashedPassword});\n await revokeRefreshTokensForUser({userId: user.id, exceptRefreshTokenId});\n}\n\nexport async function logout(params: {refreshToken?: string | undefined}): Promise<void> {\n if (!params.refreshToken) return;\n const refreshToken = await findRefreshTokenByHash({\n hashedToken: hashOpaqueToken(params.refreshToken),\n });\n if (!refreshToken) return;\n await revokeRefreshSession({sessionId: refreshToken.sessionId, userId: refreshToken.userId});\n}\n\nexport interface GetCurrentUserResult {\n user: User;\n}\n\nexport async function getCurrentUser(params: {userId: string}): Promise<GetCurrentUserResult> {\n const user = await findUserById({id: params.userId});\n if (!user) {\n throw new UserNotFoundError(params.userId);\n }\n\n return {user};\n}\n"],"names":["emailSchema","confirmEmailChallenge","consumeEmailChallengeProof","createEmailChallenge","resendEmailChallenge","workspacesInterModuleContract","isInterModuleKnownError","userAccessTokenKey","generateOpaqueToken","hashOpaqueToken","config","consumePasswordReset","createPasswordReset","createRefreshTokenForActiveUser","findActiveRefreshTokenByHash","findRefreshTokenByHash","revokeRefreshSession","revokeRefreshTokensForUser","rotateRefreshToken","createUser","createDbUser","findUserByEmail","findUserById","markEmailVerified","provisionUser","provisionDbUser","updateUserPassword","recordTokenRefreshed","getCurrentAdminRole","AuthDependencyUnavailableError","EmailNotVerifiedError","EmailTakenError","InvalidCredentialsError","InvitationEmailMismatchError","SignupNotAllowedError","TokenAlreadyUsedError","TokenExpiredError","TokenInvalidError","UserNotFoundError","signUserToken","hashPassword","verifyPassword","createEnvironmentSignupPolicy","RESET_TTL_HOURS","PASSWORD_VERIFICATION_PURPOSE","DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE","defaultSignupPolicy","assertSignupAllowed","params","result","signupPolicy","isSignupAllowed","email","emailVerified","source","allowed","message","dummyHashCache","getDummyHash","password","hoursFromNow","hours","Date","now","daysFromNow","days","recordRefreshOutcome","outcome","loadTokenMemberships","userId","workspaces","memberships","listMembershipsForTokenClaims","catch","error","signAccessToken","user","refreshSessionId","id","name","secret","expiresIn","AUTH_JWT_EXPIRES_IN","isWithinRotationGrace","refreshToken","rotatedAt","graceMs","AUTH_REFRESH_ROTATION_GRACE_SECONDS","getTime","createRefreshSession","session","sessionId","hashedToken","expiresAt","AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS","createSessionTokens","crypto","randomUUID","token","adminRole","passwordResetLink","rawToken","CLIENT_BASE_URL","invitationErrorFromKnownError","method","contractMethod","methods","code","invitationErrorMessage","parse","existing","signup","canResumeVerification","status","emailVerifiedAt","hashedPassword","hash","emailChallenge","purpose","continuation","idempotencyKey","sourceIp","signedUp","viaInvitation","signupWithInvitation","preflightInvitationAcceptance","invitationToken","membership","acceptError","acceptInvitation","workspaceId","verified","login","hasPassword","ok","createSessionForUser","undefined","refreshAccessToken","currentHashedToken","current","nextRefreshToken","rotated","nextHashedToken","latestUser","latest","confirmEmailVerification","challengeId","verifiedUser","resendEmailVerification","nextResendAvailableAt","requestPasswordReset","sendEmail","resetLink","expiresInHours","confirmPasswordReset","consumed","existingUser","newPassword","changePassword","currentPassword","currentRefreshSession","exceptRefreshTokenId","logout","getCurrentUser"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/core/auth.ts"],"sourcesContent":["import {type AdminRole, emailSchema} from '@shipfox/api-auth-dto';\nimport {\n confirmEmailChallenge,\n consumeEmailChallengeProof,\n createEmailChallenge,\n resendEmailChallenge,\n} from '@shipfox/api-email-challenges';\nimport {\n type WorkspacesInterModuleClient,\n workspacesInterModuleContract,\n} from '@shipfox/api-workspaces-dto/inter-module';\nimport {isInterModuleKnownError} from '@shipfox/inter-module';\nimport {userAccessTokenKey} from '@shipfox/node-auth-root-key';\nimport {generateOpaqueToken, hashOpaqueToken} from '@shipfox/node-tokens';\nimport {config} from '#config.js';\nimport {consumePasswordReset, createPasswordReset} from '#db/password-resets.js';\nimport {\n createRefreshTokenForActiveUser,\n findActiveRefreshTokenByHash,\n findRefreshTokenByHash,\n revokeRefreshSession,\n revokeRefreshTokensForUser,\n rotateRefreshToken,\n} from '#db/refresh-tokens.js';\nimport {\n createUser as createDbUser,\n findUserByEmail,\n findUserById,\n markEmailVerified,\n provisionUser as provisionDbUser,\n updateUserPassword,\n} from '#db/users.js';\nimport {type AuthTokenRefreshOutcome, recordTokenRefreshed} from '#metrics/index.js';\nimport {getCurrentAdminRole} from './admin-role.js';\nimport type {RefreshToken} from './entities/refresh-token.js';\nimport type {User} from './entities/user.js';\nimport {\n AuthDependencyUnavailableError,\n EmailNotVerifiedError,\n EmailTakenError,\n InvalidCredentialsError,\n InvitationEmailMismatchError,\n SignupNotAllowedError,\n TokenAlreadyUsedError,\n TokenExpiredError,\n TokenInvalidError,\n UserNotFoundError,\n} from './errors.js';\nimport {signUserToken, type TokenMembership} from './jwt.js';\nimport {hashPassword, verifyPassword} from './password.js';\nimport type {SignupPolicy} from './ports.js';\nimport {createEnvironmentSignupPolicy} from './signup-policy.js';\n\nconst RESET_TTL_HOURS = 1;\nconst PASSWORD_VERIFICATION_PURPOSE = 'password-verification';\nconst DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE =\n 'This Shipfox deployment does not accept new accounts right now.';\n\nconst defaultSignupPolicy: SignupPolicy = createEnvironmentSignupPolicy();\n\nasync function assertSignupAllowed(params: {\n signupPolicy?: SignupPolicy | undefined;\n email: string;\n emailVerified: boolean;\n source: string;\n}): Promise<void> {\n const result = await (params.signupPolicy ?? defaultSignupPolicy).isSignupAllowed({\n email: params.email,\n emailVerified: params.emailVerified,\n source: params.source,\n });\n\n if (!result.allowed) {\n throw new SignupNotAllowedError(\n result.message ?? DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE,\n result.format,\n );\n }\n}\n\nlet dummyHashCache: string | undefined;\nasync function getDummyHash(): Promise<string> {\n if (!dummyHashCache) {\n dummyHashCache = await hashPassword({password: 'dummy-for-timing-parity'});\n }\n return dummyHashCache;\n}\n\nfunction hoursFromNow(hours: number): Date {\n return new Date(Date.now() + hours * 60 * 60 * 1000);\n}\n\nfunction daysFromNow(days: number): Date {\n return new Date(Date.now() + days * 24 * 60 * 60 * 1000);\n}\n\nfunction recordRefreshOutcome(outcome: AuthTokenRefreshOutcome): void {\n recordTokenRefreshed(outcome);\n}\n\ntype TokenMemberships = TokenMembership[];\n\nasync function loadTokenMemberships(\n userId: string,\n workspaces: WorkspacesInterModuleClient,\n): Promise<TokenMemberships> {\n const memberships = await workspaces\n .listMembershipsForTokenClaims({userId})\n .catch((error: unknown) => {\n throw new AuthDependencyUnavailableError('workspaces', error);\n });\n return memberships.memberships;\n}\n\nasync function signAccessToken(\n user: User,\n memberships: TokenMemberships,\n refreshSessionId: string,\n): Promise<string> {\n return await signUserToken({\n userId: user.id,\n email: user.email,\n name: user.name,\n memberships,\n refreshSessionId,\n secret: userAccessTokenKey(),\n expiresIn: config.AUTH_JWT_EXPIRES_IN,\n });\n}\n\nfunction isWithinRotationGrace(refreshToken: RefreshToken): boolean {\n if (!refreshToken.rotatedAt) return false;\n const graceMs = config.AUTH_REFRESH_ROTATION_GRACE_SECONDS * 1000;\n return Date.now() - refreshToken.rotatedAt.getTime() <= graceMs;\n}\n\nasync function createRefreshSession(\n user: User,\n refreshSessionId: string,\n): Promise<{refreshToken: string; refreshSessionId: string}> {\n const refreshToken = generateOpaqueToken('refreshToken');\n const session = await createRefreshTokenForActiveUser({\n sessionId: refreshSessionId,\n userId: user.id,\n hashedToken: hashOpaqueToken(refreshToken),\n expiresAt: daysFromNow(config.AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS),\n });\n if (!session) throw new InvalidCredentialsError();\n return {refreshToken, refreshSessionId: session.sessionId};\n}\n\nasync function createSessionTokens(\n user: User,\n workspaces: WorkspacesInterModuleClient,\n): Promise<{token: string; refreshToken: string; adminRole: AdminRole | null}> {\n const refreshSessionId = crypto.randomUUID();\n const memberships = await loadTokenMemberships(user.id, workspaces);\n const token = await signAccessToken(user, memberships, refreshSessionId);\n const adminRole = await getCurrentAdminRole({userId: user.id});\n const {refreshToken} = await createRefreshSession(user, refreshSessionId);\n return {token, refreshToken, adminRole};\n}\n\nfunction passwordResetLink(rawToken: string): string {\n return `${config.CLIENT_BASE_URL}/auth/reset?token=${rawToken}`;\n}\n\nfunction invitationErrorFromKnownError(\n error: unknown,\n method: 'preflightInvitationAcceptance' | 'acceptInvitation',\n): Error {\n const contractMethod = workspacesInterModuleContract.methods[method];\n if (!isInterModuleKnownError(contractMethod, error)) return error as Error;\n\n if (error.code === 'invitation-token-invalid')\n return new TokenInvalidError('Invitation token is invalid');\n if (error.code === 'invitation-token-used') return new TokenAlreadyUsedError();\n if (error.code === 'invitation-token-expired') return new TokenExpiredError();\n return new InvitationEmailMismatchError();\n}\n\nfunction invitationErrorMessage(\n code:\n | 'invitation-token-invalid'\n | 'invitation-token-used'\n | 'invitation-token-expired'\n | 'invitation-email-mismatch',\n): string {\n if (code === 'invitation-token-invalid') return 'Invitation token is invalid';\n if (code === 'invitation-token-used') return 'Invitation has already been accepted';\n if (code === 'invitation-token-expired') return 'Invitation has expired';\n return 'Signup email does not match the invitation';\n}\n\nexport interface SignupParams {\n email: string;\n password: string;\n name?: string | undefined;\n signupPolicy?: SignupPolicy | undefined;\n}\n\nexport interface ProvisionUserParams {\n email: string;\n name?: string | null | undefined;\n signupPolicy?: SignupPolicy | undefined;\n}\n\n/**\n * Creates a verified, password-less user for an external identity provider.\n * Existing users are returned unchanged, including their password and profile.\n */\nexport async function provisionUser(params: ProvisionUserParams): Promise<User> {\n const email = emailSchema.parse(params.email);\n const existing = await findUserByEmail({email});\n if (existing) return existing;\n\n await assertSignupAllowed({\n signupPolicy: params.signupPolicy,\n email,\n emailVerified: true,\n source: 'external-identity',\n });\n\n return await provisionDbUser({\n email,\n name: params.name ?? null,\n });\n}\n\nexport type SignupResult = User & {\n emailChallenge: {id: string; nextResendAvailableAt: Date};\n};\n\nexport async function signup(params: SignupParams & {sourceIp?: string}): Promise<SignupResult> {\n const email = emailSchema.parse(params.email);\n const existing = await findUserByEmail({email});\n if (existing) {\n const canResumeVerification =\n existing.status === 'active' &&\n existing.emailVerifiedAt === null &&\n existing.hashedPassword !== null &&\n (await verifyPassword({password: params.password, hash: existing.hashedPassword}));\n if (canResumeVerification) {\n const emailChallenge = await createEmailChallenge({\n email: existing.email,\n purpose: PASSWORD_VERIFICATION_PURPOSE,\n continuation: existing.id,\n idempotencyKey: existing.id,\n sourceIp: params.sourceIp ?? '0.0.0.0',\n });\n return {...existing, emailChallenge};\n }\n throw new EmailTakenError(email);\n }\n\n await assertSignupAllowed({\n signupPolicy: params.signupPolicy,\n email,\n emailVerified: false,\n source: 'password',\n });\n\n const hashedPassword = await hashPassword({password: params.password});\n const user = await createDbUser({\n email,\n hashedPassword,\n name: params.name ?? null,\n signedUp: {viaInvitation: false},\n });\n\n const emailChallenge = await createEmailChallenge({\n email: user.email,\n purpose: PASSWORD_VERIFICATION_PURPOSE,\n continuation: user.id,\n idempotencyKey: user.id,\n sourceIp: params.sourceIp ?? '0.0.0.0',\n });\n\n return {...user, emailChallenge};\n}\n\nexport interface SignupWithInvitationParams extends SignupParams {\n invitationToken: string;\n workspaces: WorkspacesInterModuleClient;\n}\n\nexport interface SignupWithInvitationResult extends LoginResult {\n membership: {id: string; userId: string; workspaceId: string} | null;\n acceptError?: {code: string; message: string};\n}\n\nexport async function signupWithInvitation(\n params: SignupWithInvitationParams,\n): Promise<SignupWithInvitationResult> {\n // Step 1: Pre-validate the invitation BEFORE any user write so an invalid\n // token never produces an orphan user. Re-validation happens again inside\n // acceptWorkspaceInvitation (race-safe) after the user is created.\n try {\n await params.workspaces.preflightInvitationAcceptance({\n token: params.invitationToken,\n email: params.email,\n });\n } catch (error) {\n throw invitationErrorFromKnownError(error, 'preflightInvitationAcceptance');\n }\n\n // Step 2: Create the user as verified. The invitation email is the proof of\n // ownership, and this avoids a second auth-table write just to mark verified.\n const existing = await findUserByEmail({email: params.email});\n if (existing) {\n throw new EmailTakenError(params.email);\n }\n const hashedPassword = await hashPassword({password: params.password});\n const user = await createDbUser({\n email: params.email,\n hashedPassword,\n name: params.name ?? null,\n emailVerifiedAt: new Date(),\n signedUp: {viaInvitation: true},\n });\n\n // Step 3: Accept the invitation through the workspaces module boundary.\n // This is intentionally not one database transaction with auth; treat it like\n // calling another service. If it fails after user creation, return a verified\n // signed-in user and an explicit retryable accept error.\n let membership: SignupWithInvitationResult['membership'] = null;\n let acceptError: SignupWithInvitationResult['acceptError'];\n try {\n const result = await params.workspaces.acceptInvitation({\n token: params.invitationToken,\n userId: user.id,\n email: user.email,\n name: user.name,\n });\n membership = {\n id: result.membership.id,\n userId: result.membership.userId,\n workspaceId: result.membership.workspaceId,\n };\n } catch (error) {\n if (isInterModuleKnownError(workspacesInterModuleContract.methods.acceptInvitation, error)) {\n acceptError = {code: error.code, message: invitationErrorMessage(error.code)};\n } else {\n acceptError = {\n code: 'AcceptFailed',\n message: 'Could not accept the invitation; please retry from the invite link.',\n };\n }\n }\n\n // Step 4: Issue session. createSessionTokens reads memberships through the\n // workspaces module API, so a successful accept is reflected in the JWT.\n const {token, refreshToken, adminRole} = await createSessionTokens(user, params.workspaces);\n\n if (acceptError) {\n return {token, refreshToken, user, membership, acceptError, adminRole};\n }\n return {token, refreshToken, user, membership, adminRole};\n}\n\nexport interface CreateUserParams extends SignupParams {\n verified: boolean;\n}\n\nexport async function createUser(params: CreateUserParams): Promise<User> {\n const existing = await findUserByEmail({email: params.email});\n if (existing) {\n throw new EmailTakenError(params.email);\n }\n\n const hashedPassword = await hashPassword({password: params.password});\n const user = await createDbUser({\n email: params.email,\n hashedPassword,\n name: params.name ?? null,\n emailVerifiedAt: params.verified ? new Date() : null,\n });\n\n return user;\n}\n\nexport interface LoginParams {\n email: string;\n password: string;\n workspaces: WorkspacesInterModuleClient;\n}\n\nexport interface LoginResult {\n token: string;\n refreshToken: string;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport async function login(params: LoginParams): Promise<LoginResult> {\n const user = await findUserByEmail({email: params.email});\n\n if (!user) {\n await verifyPassword({password: params.password, hash: await getDummyHash()});\n throw new InvalidCredentialsError();\n }\n\n const hasPassword = user.hashedPassword !== null;\n const ok = await verifyPassword({\n password: params.password,\n hash: user.hashedPassword ?? (await getDummyHash()),\n });\n if (!hasPassword || !ok || user.status !== 'active') {\n throw new InvalidCredentialsError();\n }\n\n if (user.emailVerifiedAt === null) {\n throw new EmailNotVerifiedError();\n }\n\n const {token, refreshToken, adminRole} = await createSessionTokens(user, params.workspaces);\n\n return {token, refreshToken, user, adminRole};\n}\n\nexport interface CreateSessionForUserParams {\n userId?: string | undefined;\n email?: string | undefined;\n workspaces: WorkspacesInterModuleClient;\n}\n\nexport interface CreateSessionForUserResult {\n token: string;\n refreshToken: string;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport type CreateSessionForUserError =\n | AuthDependencyUnavailableError\n | EmailNotVerifiedError\n | InvalidCredentialsError\n | UserNotFoundError;\n\nexport async function createSessionForUser(\n params: CreateSessionForUserParams,\n): Promise<CreateSessionForUserResult> {\n const user = params.userId\n ? await findUserById({id: params.userId})\n : params.email\n ? await findUserByEmail({email: emailSchema.parse(params.email)})\n : undefined;\n\n if (!user) {\n throw new UserNotFoundError(params.userId ?? params.email ?? 'unknown');\n }\n if (user.emailVerifiedAt === null) {\n throw new EmailNotVerifiedError();\n }\n if (user.status !== 'active') {\n throw new InvalidCredentialsError();\n }\n\n const {token, refreshToken, adminRole} = await createSessionTokens(user, params.workspaces);\n\n return {token, refreshToken, user, adminRole};\n}\n\nexport interface RefreshAccessTokenResult {\n token: string;\n /** Undefined on a grace-window hit: keep the existing cookie instead of rotating it. */\n refreshToken: string | undefined;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport async function refreshAccessToken(params: {\n refreshToken: string;\n workspaces: WorkspacesInterModuleClient;\n}): Promise<RefreshAccessTokenResult> {\n const currentHashedToken = hashOpaqueToken(params.refreshToken);\n const current = await findRefreshTokenByHash({hashedToken: currentHashedToken});\n if (!current) {\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n\n const user = await findUserById({id: current.userId});\n if (user?.status !== 'active') {\n await revokeRefreshSession({sessionId: current.sessionId, userId: current.userId});\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n const adminRole = await getCurrentAdminRole({userId: user.id});\n\n // Within the grace window a rotated token means a concurrent refresh (e.g. a\n // second tab); past it, reuse of a retired token means a compromised session.\n if (current.rotatedAt) {\n if (isWithinRotationGrace(current)) {\n const memberships = await loadTokenMemberships(user.id, params.workspaces);\n const token = await signAccessToken(user, memberships, current.sessionId);\n recordRefreshOutcome('grace');\n return {token, refreshToken: undefined, user, adminRole};\n }\n await revokeRefreshTokensForUser({userId: user.id});\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token reused after rotation');\n }\n\n // Keep the external membership snapshot ahead of rotation so an outage\n // cannot retire a refresh token that the caller can still retry.\n const memberships = await loadTokenMemberships(user.id, params.workspaces);\n\n // Losing the CAS requires a state check: another request may have rotated,\n // revoked, or expired the token before this refresh could claim it.\n const nextRefreshToken = generateOpaqueToken('refreshToken');\n const rotated = await rotateRefreshToken({\n userId: current.userId,\n id: current.id,\n currentHashedToken,\n nextHashedToken: hashOpaqueToken(nextRefreshToken),\n expiresAt: daysFromNow(config.AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS),\n });\n if (!rotated) {\n const latestUser = await findUserById({id: current.userId});\n if (latestUser?.status !== 'active') {\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n const latest = await findRefreshTokenByHash({hashedToken: currentHashedToken});\n if (!latest || !isWithinRotationGrace(latest)) {\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n const token = await signAccessToken(user, memberships, latest.sessionId);\n recordRefreshOutcome('grace');\n return {token, refreshToken: undefined, user, adminRole};\n }\n\n const token = await signAccessToken(user, memberships, current.sessionId);\n recordRefreshOutcome('rotated');\n return {token, refreshToken: nextRefreshToken, user, adminRole};\n}\n\nexport interface ConfirmEmailVerificationResult {\n token: string;\n refreshToken: string;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport interface ResendEmailVerificationResult {\n nextResendAvailableAt: Date;\n}\n\nexport async function confirmEmailVerification(params: {\n email: string;\n challengeId: string;\n code: string;\n workspaces: WorkspacesInterModuleClient;\n}): Promise<ConfirmEmailVerificationResult> {\n const user = await findUserByEmail({email: emailSchema.parse(params.email)});\n if (!user) throw new TokenInvalidError('Verification code is invalid or expired');\n await confirmEmailChallenge({id: params.challengeId, code: params.code, continuation: user.id});\n await consumeEmailChallengeProof({\n id: params.challengeId,\n purpose: PASSWORD_VERIFICATION_PURPOSE,\n continuation: user.id,\n });\n\n const verifiedUser = await markEmailVerified({userId: user.id});\n if (verifiedUser?.status !== 'active') {\n throw new TokenInvalidError('Verification code is invalid or expired');\n }\n\n const {token, refreshToken, adminRole} = await createSessionTokens(\n verifiedUser,\n params.workspaces,\n );\n\n return {token, refreshToken, user: verifiedUser, adminRole};\n}\n\nexport async function resendEmailVerification(params: {\n email: string;\n challengeId: string;\n sourceIp: string;\n}): Promise<ResendEmailVerificationResult> {\n const user = await findUserByEmail({email: emailSchema.parse(params.email)});\n if (!user || user.emailVerifiedAt || user.status !== 'active') {\n return {nextResendAvailableAt: new Date()};\n }\n const result = await resendEmailChallenge({\n id: params.challengeId,\n continuation: user.id,\n sourceIp: params.sourceIp,\n });\n\n return {nextResendAvailableAt: result.nextResendAvailableAt};\n}\n\nexport async function requestPasswordReset(params: {email: string}): Promise<void> {\n const user = await findUserByEmail({email: params.email});\n if (user?.status !== 'active' || user.hashedPassword === null) {\n return;\n }\n\n const rawToken = generateOpaqueToken('passwordReset');\n await createPasswordReset({\n userId: user.id,\n hashedToken: hashOpaqueToken(rawToken),\n expiresAt: hoursFromNow(RESET_TTL_HOURS),\n sendEmail: {\n email: user.email,\n resetLink: passwordResetLink(rawToken),\n expiresInHours: RESET_TTL_HOURS,\n },\n });\n}\n\nexport interface ConfirmPasswordResetResult {\n token: string;\n refreshToken: string;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport async function confirmPasswordReset(params: {\n token: string;\n newPassword: string;\n workspaces: WorkspacesInterModuleClient;\n}): Promise<ConfirmPasswordResetResult> {\n const consumed = await consumePasswordReset({hashedToken: hashOpaqueToken(params.token)});\n if (!consumed) {\n throw new TokenInvalidError('Reset token is invalid or expired');\n }\n\n const existingUser = await findUserById({id: consumed.userId});\n if (existingUser?.status !== 'active' || existingUser.hashedPassword === null) {\n throw new TokenInvalidError('Reset token is invalid or expired');\n }\n\n const hashedPassword = await hashPassword({password: params.newPassword});\n const user = await updateUserPassword({userId: consumed.userId, hashedPassword});\n if (user?.status !== 'active') {\n throw new TokenInvalidError('Reset token is invalid or expired');\n }\n\n await revokeRefreshTokensForUser({userId: consumed.userId});\n\n const {token, refreshToken, adminRole} = await createSessionTokens(user, params.workspaces);\n\n return {token, refreshToken, user, adminRole};\n}\n\nexport async function changePassword(params: {\n userId: string;\n currentPassword: string;\n newPassword: string;\n refreshToken?: string | undefined;\n}): Promise<void> {\n const user = await findUserById({id: params.userId});\n if (!user) {\n throw new UserNotFoundError(params.userId);\n }\n\n const hasPassword = user.hashedPassword !== null;\n const ok = await verifyPassword({\n password: params.currentPassword,\n hash: user.hashedPassword ?? (await getDummyHash()),\n });\n if (!hasPassword || !ok) {\n throw new InvalidCredentialsError();\n }\n\n const hashedPassword = await hashPassword({password: params.newPassword});\n const currentRefreshSession = params.refreshToken\n ? await findActiveRefreshTokenByHash({hashedToken: hashOpaqueToken(params.refreshToken)})\n : undefined;\n const exceptRefreshTokenId =\n currentRefreshSession?.userId === user.id ? currentRefreshSession.id : undefined;\n\n await updateUserPassword({userId: user.id, hashedPassword});\n await revokeRefreshTokensForUser({userId: user.id, exceptRefreshTokenId});\n}\n\nexport async function logout(params: {refreshToken?: string | undefined}): Promise<void> {\n if (!params.refreshToken) return;\n const refreshToken = await findRefreshTokenByHash({\n hashedToken: hashOpaqueToken(params.refreshToken),\n });\n if (!refreshToken) return;\n await revokeRefreshSession({sessionId: refreshToken.sessionId, userId: refreshToken.userId});\n}\n\nexport interface GetCurrentUserResult {\n user: User;\n}\n\nexport async function getCurrentUser(params: {userId: string}): Promise<GetCurrentUserResult> {\n const user = await findUserById({id: params.userId});\n if (!user) {\n throw new UserNotFoundError(params.userId);\n }\n\n return {user};\n}\n"],"names":["emailSchema","confirmEmailChallenge","consumeEmailChallengeProof","createEmailChallenge","resendEmailChallenge","workspacesInterModuleContract","isInterModuleKnownError","userAccessTokenKey","generateOpaqueToken","hashOpaqueToken","config","consumePasswordReset","createPasswordReset","createRefreshTokenForActiveUser","findActiveRefreshTokenByHash","findRefreshTokenByHash","revokeRefreshSession","revokeRefreshTokensForUser","rotateRefreshToken","createUser","createDbUser","findUserByEmail","findUserById","markEmailVerified","provisionUser","provisionDbUser","updateUserPassword","recordTokenRefreshed","getCurrentAdminRole","AuthDependencyUnavailableError","EmailNotVerifiedError","EmailTakenError","InvalidCredentialsError","InvitationEmailMismatchError","SignupNotAllowedError","TokenAlreadyUsedError","TokenExpiredError","TokenInvalidError","UserNotFoundError","signUserToken","hashPassword","verifyPassword","createEnvironmentSignupPolicy","RESET_TTL_HOURS","PASSWORD_VERIFICATION_PURPOSE","DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE","defaultSignupPolicy","assertSignupAllowed","params","result","signupPolicy","isSignupAllowed","email","emailVerified","source","allowed","message","format","dummyHashCache","getDummyHash","password","hoursFromNow","hours","Date","now","daysFromNow","days","recordRefreshOutcome","outcome","loadTokenMemberships","userId","workspaces","memberships","listMembershipsForTokenClaims","catch","error","signAccessToken","user","refreshSessionId","id","name","secret","expiresIn","AUTH_JWT_EXPIRES_IN","isWithinRotationGrace","refreshToken","rotatedAt","graceMs","AUTH_REFRESH_ROTATION_GRACE_SECONDS","getTime","createRefreshSession","session","sessionId","hashedToken","expiresAt","AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS","createSessionTokens","crypto","randomUUID","token","adminRole","passwordResetLink","rawToken","CLIENT_BASE_URL","invitationErrorFromKnownError","method","contractMethod","methods","code","invitationErrorMessage","parse","existing","signup","canResumeVerification","status","emailVerifiedAt","hashedPassword","hash","emailChallenge","purpose","continuation","idempotencyKey","sourceIp","signedUp","viaInvitation","signupWithInvitation","preflightInvitationAcceptance","invitationToken","membership","acceptError","acceptInvitation","workspaceId","verified","login","hasPassword","ok","createSessionForUser","undefined","refreshAccessToken","currentHashedToken","current","nextRefreshToken","rotated","nextHashedToken","latestUser","latest","confirmEmailVerification","challengeId","verifiedUser","resendEmailVerification","nextResendAvailableAt","requestPasswordReset","sendEmail","resetLink","expiresInHours","confirmPasswordReset","consumed","existingUser","newPassword","changePassword","currentPassword","currentRefreshSession","exceptRefreshTokenId","logout","getCurrentUser"],"mappings":"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"}
|
package/dist/core/errors.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SignupDenialMessageFormat } from './ports.js';
|
|
1
2
|
export declare class WorkspaceNotFoundError extends Error {
|
|
2
3
|
constructor(workspaceId: string);
|
|
3
4
|
}
|
|
@@ -20,7 +21,8 @@ export declare class EmailTakenError extends Error {
|
|
|
20
21
|
constructor(email: string);
|
|
21
22
|
}
|
|
22
23
|
export declare class SignupNotAllowedError extends Error {
|
|
23
|
-
|
|
24
|
+
readonly format?: SignupDenialMessageFormat | undefined;
|
|
25
|
+
constructor(message: string, format?: SignupDenialMessageFormat | undefined);
|
|
24
26
|
}
|
|
25
27
|
export declare class InvalidCredentialsError extends Error {
|
|
26
28
|
constructor();
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,YAAY,CAAC;AAE1D,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;IAG5C,QAAQ,CAAC,MAAM,CAAC,EAAE,yBAAyB;IAF7C,YACE,OAAO,EAAE,MAAM,EACN,MAAM,CAAC,EAAE,yBAAyB,YAAA,EAI5C;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"}
|
package/dist/core/errors.js
CHANGED
|
@@ -41,8 +41,8 @@ export class EmailTakenError extends Error {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
export class SignupNotAllowedError extends Error {
|
|
44
|
-
constructor(message){
|
|
45
|
-
super(message);
|
|
44
|
+
constructor(message, format){
|
|
45
|
+
super(message), this.format = format;
|
|
46
46
|
this.name = 'SignupNotAllowedError';
|
|
47
47
|
}
|
|
48
48
|
}
|
package/dist/core/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/errors.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../../src/core/errors.ts"],"sourcesContent":["import type {SignupDenialMessageFormat} from './ports.js';\n\nexport class WorkspaceNotFoundError extends Error {\n constructor(workspaceId: string) {\n super(`Workspace not found: ${workspaceId}`);\n this.name = 'WorkspaceNotFoundError';\n }\n}\n\nexport class ApiKeyNotFoundError extends Error {\n constructor(id: string) {\n super(`API key not found: ${id}`);\n this.name = 'ApiKeyNotFoundError';\n }\n}\n\nexport class InvitationNotFoundError extends Error {\n constructor(id: string) {\n super(`Invitation not found: ${id}`);\n this.name = 'InvitationNotFoundError';\n }\n}\n\nexport class InvitationWorkspaceMismatchError extends Error {\n constructor() {\n super('Invitation does not belong to this workspace');\n this.name = 'InvitationWorkspaceMismatchError';\n }\n}\n\nexport class MembershipNotFoundError extends Error {\n constructor(userId: string, workspaceId: string) {\n super(`Membership not found for user ${userId} in workspace ${workspaceId}`);\n this.name = 'MembershipNotFoundError';\n }\n}\n\nexport class UserNotFoundError extends Error {\n constructor(idOrEmail: string) {\n super(`User not found: ${idOrEmail}`);\n this.name = 'UserNotFoundError';\n }\n}\n\nexport class EmailTakenError extends Error {\n constructor(email: string) {\n super(`Email already registered: ${email}`);\n this.name = 'EmailTakenError';\n }\n}\n\nexport class SignupNotAllowedError extends Error {\n constructor(\n message: string,\n readonly format?: SignupDenialMessageFormat,\n ) {\n super(message);\n this.name = 'SignupNotAllowedError';\n }\n}\n\nexport class InvalidCredentialsError extends Error {\n constructor() {\n super('Invalid credentials');\n this.name = 'InvalidCredentialsError';\n }\n}\n\nexport class EmailNotVerifiedError extends Error {\n constructor() {\n super('Email not verified');\n this.name = 'EmailNotVerifiedError';\n }\n}\n\nexport class AuthDependencyUnavailableError extends Error {\n readonly dependency: string;\n override readonly cause: unknown;\n\n constructor(dependency: string, cause: unknown) {\n super(`Authentication dependency unavailable: ${dependency}`);\n this.name = 'AuthDependencyUnavailableError';\n this.dependency = dependency;\n this.cause = cause;\n }\n}\n\nexport class AdminRoleRequiredError extends Error {\n readonly minimumRole: import('@shipfox/api-auth-dto').AdminRole;\n\n constructor(minimumRole: import('@shipfox/api-auth-dto').AdminRole) {\n super(`Administrator role required: ${minimumRole}`);\n this.name = 'AdminRoleRequiredError';\n this.minimumRole = minimumRole;\n }\n}\n\nexport class LastAdminOwnerError extends Error {\n constructor() {\n super('Cannot remove the final active administrator owner');\n this.name = 'LastAdminOwnerError';\n }\n}\n\nexport class AdminGrantNotFoundError extends Error {\n constructor() {\n super('Administrator grant not found');\n this.name = 'AdminGrantNotFoundError';\n }\n}\n\nexport class AdminGrantAlreadyExistsError extends Error {\n constructor() {\n super('Administrator grant already exists');\n this.name = 'AdminGrantAlreadyExistsError';\n }\n}\n\nexport class AdminBootstrapClosedError extends Error {\n constructor() {\n super('First administrator owner has already been created');\n this.name = 'AdminBootstrapClosedError';\n }\n}\n\nexport class InvalidAdminBootstrapTokenError extends Error {\n constructor() {\n super('Bootstrap token is invalid');\n this.name = 'InvalidAdminBootstrapTokenError';\n }\n}\n\nexport class AdminIdempotencyKeyReuseError extends Error {\n constructor() {\n super('Idempotency key was already used for a different command');\n this.name = 'AdminIdempotencyKeyReuseError';\n }\n}\n\nexport class TokenInvalidError extends Error {\n constructor(reason?: string) {\n super(reason ? `Invalid token: ${reason}` : 'Invalid token');\n this.name = 'TokenInvalidError';\n }\n}\n\nexport class TokenExpiredError extends Error {\n constructor() {\n super('Token has expired');\n this.name = 'TokenExpiredError';\n }\n}\n\nexport class TokenAlreadyUsedError extends Error {\n constructor() {\n super('Token has already been used');\n this.name = 'TokenAlreadyUsedError';\n }\n}\n\nexport class MembershipRequiredError extends Error {\n constructor(workspaceId: string) {\n super(`Membership required for workspace: ${workspaceId}`);\n this.name = 'MembershipRequiredError';\n }\n}\n\nexport class LastMemberError extends Error {\n constructor(workspaceId: string) {\n super(`Cannot remove the last member of workspace: ${workspaceId}`);\n this.name = 'LastMemberError';\n }\n}\n\nexport class InvitationEmailMismatchError extends Error {\n constructor() {\n super('Invitation email does not match authenticated user');\n this.name = 'InvitationEmailMismatchError';\n }\n}\n\nexport class OpenInvitationExistsError extends Error {\n constructor(email: string) {\n super(`An open invitation already exists for: ${email}`);\n this.name = 'OpenInvitationExistsError';\n }\n}\n"],"names":["WorkspaceNotFoundError","Error","workspaceId","name","ApiKeyNotFoundError","id","InvitationNotFoundError","InvitationWorkspaceMismatchError","MembershipNotFoundError","userId","UserNotFoundError","idOrEmail","EmailTakenError","email","SignupNotAllowedError","message","format","InvalidCredentialsError","EmailNotVerifiedError","AuthDependencyUnavailableError","dependency","cause","AdminRoleRequiredError","minimumRole","LastAdminOwnerError","AdminGrantNotFoundError","AdminGrantAlreadyExistsError","AdminBootstrapClosedError","InvalidAdminBootstrapTokenError","AdminIdempotencyKeyReuseError","TokenInvalidError","reason","TokenExpiredError","TokenAlreadyUsedError","MembershipRequiredError","LastMemberError","InvitationEmailMismatchError","OpenInvitationExistsError"],"mappings":"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"}
|
package/dist/core/ports.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export declare const SIGNUP_DENIAL_MESSAGE_MAX_LENGTH = 500;
|
|
2
|
+
export type SignupDenialMessageFormat = 'markdown';
|
|
1
3
|
export interface SignupPolicy {
|
|
2
4
|
isSignupAllowed(params: {
|
|
3
5
|
email: string;
|
|
@@ -8,6 +10,7 @@ export interface SignupPolicy {
|
|
|
8
10
|
} | {
|
|
9
11
|
allowed: false;
|
|
10
12
|
message?: string;
|
|
13
|
+
format?: SignupDenialMessageFormat;
|
|
11
14
|
}>;
|
|
12
15
|
}
|
|
13
16
|
//# sourceMappingURL=ports.d.ts.map
|
package/dist/core/ports.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../../src/core/ports.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,eAAe,CAAC,MAAM,EAAE;QACtB,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,OAAO,CAAC;QACvB,MAAM,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../../src/core/ports.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gCAAgC,MAAM,CAAC;AACpD,MAAM,MAAM,yBAAyB,GAAG,UAAU,CAAC;AAEnD,MAAM,WAAW,YAAY;IAC3B,eAAe,CAAC,MAAM,EAAE;QACtB,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,OAAO,CAAC;QACvB,MAAM,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CACT;QAAC,OAAO,EAAE,IAAI,CAAA;KAAC,GAAG;QAAC,OAAO,EAAE,KAAK,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,yBAAyB,CAAA;KAAC,CACzF,CAAC;CACH"}
|
package/dist/core/ports.js
CHANGED
package/dist/core/ports.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/ports.ts"],"sourcesContent":["export interface SignupPolicy {\n isSignupAllowed(params: {\n email: string;\n emailVerified: boolean;\n source: string;\n }): Promise
|
|
1
|
+
{"version":3,"sources":["../../src/core/ports.ts"],"sourcesContent":["export const SIGNUP_DENIAL_MESSAGE_MAX_LENGTH = 500;\nexport type SignupDenialMessageFormat = 'markdown';\n\nexport interface SignupPolicy {\n isSignupAllowed(params: {\n email: string;\n emailVerified: boolean;\n source: string;\n }): Promise<\n {allowed: true} | {allowed: false; message?: string; format?: SignupDenialMessageFormat}\n >;\n}\n"],"names":["SIGNUP_DENIAL_MESSAGE_MAX_LENGTH"],"mappings":"AAAA,OAAO,MAAMA,mCAAmC,IAAI"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signup-policy.d.ts","sourceRoot":"","sources":["../../src/core/signup-policy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,YAAY,CAAC;AAE7C,eAAO,MAAM,kCAAkC,oEACoB,CAAC;AAEpE;;;;;GAKG;AACH,wBAAgB,6BAA6B,IAAI,YAAY,
|
|
1
|
+
{"version":3,"file":"signup-policy.d.ts","sourceRoot":"","sources":["../../src/core/signup-policy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,YAAY,CAAC;AAE7C,eAAO,MAAM,kCAAkC,oEACoB,CAAC;AAEpE;;;;;GAKG;AACH,wBAAgB,6BAA6B,IAAI,YAAY,CAsB5D"}
|
|
@@ -24,7 +24,8 @@ export const DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE = 'This Shipfox deployment does
|
|
|
24
24
|
});
|
|
25
25
|
return Promise.resolve({
|
|
26
26
|
allowed: false,
|
|
27
|
-
message: config.AUTH_SIGNUP_NOT_ALLOWED_MESSAGE ?? DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE
|
|
27
|
+
message: config.AUTH_SIGNUP_NOT_ALLOWED_MESSAGE ?? DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE,
|
|
28
|
+
format: 'markdown'
|
|
28
29
|
});
|
|
29
30
|
}
|
|
30
31
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/signup-policy.ts"],"sourcesContent":["import {config} from '#config.js';\nimport type {SignupPolicy} from './ports.js';\n\nexport const DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE =\n 'This Shipfox deployment does not accept new accounts right now.';\n\n/**\n * Builds the default signup policy from the Auth module's environment settings.\n *\n * The allowlist is resolved once at startup. Environment values are normalized\n * before matching, but the configured denial message is returned unchanged.\n */\nexport function createEnvironmentSignupPolicy(): SignupPolicy {\n const allowedEmails = parseAllowlist(config.AUTH_SIGNUP_ALLOWED_EMAILS);\n const allowedDomains = parseAllowlist(config.AUTH_SIGNUP_ALLOWED_EMAIL_DOMAINS);\n\n return {\n isSignupAllowed({email}) {\n if (!config.AUTH_SIGNUP_GATE_ENABLED) return Promise.resolve({allowed: true});\n\n const normalizedEmail = normalize(email);\n if (allowedEmails.has(normalizedEmail)) return Promise.resolve({allowed: true});\n\n const atIndex = normalizedEmail.lastIndexOf('@');\n const domain = atIndex === -1 ? '' : normalizedEmail.slice(atIndex + 1);\n if (allowedDomains.has(domain)) return Promise.resolve({allowed: true});\n\n return Promise.resolve({\n allowed: false,\n message: config.AUTH_SIGNUP_NOT_ALLOWED_MESSAGE ?? DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE,\n });\n },\n };\n}\n\nfunction parseAllowlist(value: string): ReadonlySet<string> {\n return new Set(value.split(',').map(normalize).filter(Boolean));\n}\n\nfunction normalize(value: string): string {\n return value.trim().toLowerCase();\n}\n"],"names":["config","DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE","createEnvironmentSignupPolicy","allowedEmails","parseAllowlist","AUTH_SIGNUP_ALLOWED_EMAILS","allowedDomains","AUTH_SIGNUP_ALLOWED_EMAIL_DOMAINS","isSignupAllowed","email","AUTH_SIGNUP_GATE_ENABLED","Promise","resolve","allowed","normalizedEmail","normalize","has","atIndex","lastIndexOf","domain","slice","message","AUTH_SIGNUP_NOT_ALLOWED_MESSAGE","value","Set","split","map","filter","Boolean","trim","toLowerCase"],"mappings":"AAAA,SAAQA,MAAM,QAAO,aAAa;AAGlC,OAAO,MAAMC,qCACX,kEAAkE;AAEpE;;;;;CAKC,GACD,OAAO,SAASC;IACd,MAAMC,gBAAgBC,eAAeJ,OAAOK,0BAA0B;IACtE,MAAMC,iBAAiBF,eAAeJ,OAAOO,iCAAiC;IAE9E,OAAO;QACLC,iBAAgB,EAACC,KAAK,EAAC;YACrB,IAAI,CAACT,OAAOU,wBAAwB,EAAE,OAAOC,QAAQC,OAAO,CAAC;gBAACC,SAAS;YAAI;YAE3E,MAAMC,kBAAkBC,UAAUN;YAClC,IAAIN,cAAca,GAAG,CAACF,kBAAkB,OAAOH,QAAQC,OAAO,CAAC;gBAACC,SAAS;YAAI;YAE7E,MAAMI,UAAUH,gBAAgBI,WAAW,CAAC;YAC5C,MAAMC,SAASF,YAAY,CAAC,IAAI,KAAKH,gBAAgBM,KAAK,CAACH,UAAU;YACrE,IAAIX,eAAeU,GAAG,CAACG,SAAS,OAAOR,QAAQC,OAAO,CAAC;gBAACC,SAAS;YAAI;YAErE,OAAOF,QAAQC,OAAO,CAAC;gBACrBC,SAAS;gBACTQ,SAASrB,OAAOsB,+BAA+B,IAAIrB;
|
|
1
|
+
{"version":3,"sources":["../../src/core/signup-policy.ts"],"sourcesContent":["import {config} from '#config.js';\nimport type {SignupPolicy} from './ports.js';\n\nexport const DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE =\n 'This Shipfox deployment does not accept new accounts right now.';\n\n/**\n * Builds the default signup policy from the Auth module's environment settings.\n *\n * The allowlist is resolved once at startup. Environment values are normalized\n * before matching, but the configured denial message is returned unchanged.\n */\nexport function createEnvironmentSignupPolicy(): SignupPolicy {\n const allowedEmails = parseAllowlist(config.AUTH_SIGNUP_ALLOWED_EMAILS);\n const allowedDomains = parseAllowlist(config.AUTH_SIGNUP_ALLOWED_EMAIL_DOMAINS);\n\n return {\n isSignupAllowed({email}) {\n if (!config.AUTH_SIGNUP_GATE_ENABLED) return Promise.resolve({allowed: true});\n\n const normalizedEmail = normalize(email);\n if (allowedEmails.has(normalizedEmail)) return Promise.resolve({allowed: true});\n\n const atIndex = normalizedEmail.lastIndexOf('@');\n const domain = atIndex === -1 ? '' : normalizedEmail.slice(atIndex + 1);\n if (allowedDomains.has(domain)) return Promise.resolve({allowed: true});\n\n return Promise.resolve({\n allowed: false,\n message: config.AUTH_SIGNUP_NOT_ALLOWED_MESSAGE ?? DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE,\n format: 'markdown',\n });\n },\n };\n}\n\nfunction parseAllowlist(value: string): ReadonlySet<string> {\n return new Set(value.split(',').map(normalize).filter(Boolean));\n}\n\nfunction normalize(value: string): string {\n return value.trim().toLowerCase();\n}\n"],"names":["config","DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE","createEnvironmentSignupPolicy","allowedEmails","parseAllowlist","AUTH_SIGNUP_ALLOWED_EMAILS","allowedDomains","AUTH_SIGNUP_ALLOWED_EMAIL_DOMAINS","isSignupAllowed","email","AUTH_SIGNUP_GATE_ENABLED","Promise","resolve","allowed","normalizedEmail","normalize","has","atIndex","lastIndexOf","domain","slice","message","AUTH_SIGNUP_NOT_ALLOWED_MESSAGE","format","value","Set","split","map","filter","Boolean","trim","toLowerCase"],"mappings":"AAAA,SAAQA,MAAM,QAAO,aAAa;AAGlC,OAAO,MAAMC,qCACX,kEAAkE;AAEpE;;;;;CAKC,GACD,OAAO,SAASC;IACd,MAAMC,gBAAgBC,eAAeJ,OAAOK,0BAA0B;IACtE,MAAMC,iBAAiBF,eAAeJ,OAAOO,iCAAiC;IAE9E,OAAO;QACLC,iBAAgB,EAACC,KAAK,EAAC;YACrB,IAAI,CAACT,OAAOU,wBAAwB,EAAE,OAAOC,QAAQC,OAAO,CAAC;gBAACC,SAAS;YAAI;YAE3E,MAAMC,kBAAkBC,UAAUN;YAClC,IAAIN,cAAca,GAAG,CAACF,kBAAkB,OAAOH,QAAQC,OAAO,CAAC;gBAACC,SAAS;YAAI;YAE7E,MAAMI,UAAUH,gBAAgBI,WAAW,CAAC;YAC5C,MAAMC,SAASF,YAAY,CAAC,IAAI,KAAKH,gBAAgBM,KAAK,CAACH,UAAU;YACrE,IAAIX,eAAeU,GAAG,CAACG,SAAS,OAAOR,QAAQC,OAAO,CAAC;gBAACC,SAAS;YAAI;YAErE,OAAOF,QAAQC,OAAO,CAAC;gBACrBC,SAAS;gBACTQ,SAASrB,OAAOsB,+BAA+B,IAAIrB;gBACnDsB,QAAQ;YACV;QACF;IACF;AACF;AAEA,SAASnB,eAAeoB,KAAa;IACnC,OAAO,IAAIC,IAAID,MAAME,KAAK,CAAC,KAAKC,GAAG,CAACZ,WAAWa,MAAM,CAACC;AACxD;AAEA,SAASd,UAAUS,KAAa;IAC9B,OAAOA,MAAMM,IAAI,GAAGC,WAAW;AACjC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type { AdminGrant } from '#core/entities/admin-grant.js';
|
|
|
11
11
|
export type { User, UserStatus } from '#core/entities/user.js';
|
|
12
12
|
export { AdminBootstrapClosedError, AdminGrantAlreadyExistsError, AdminGrantNotFoundError, AdminIdempotencyKeyReuseError, AdminRoleRequiredError, AuthDependencyUnavailableError, EmailNotVerifiedError, InvalidAdminBootstrapTokenError, InvalidCredentialsError, LastAdminOwnerError, SignupNotAllowedError, UserNotFoundError, } from '#core/errors.js';
|
|
13
13
|
export { issueJobLeaseToken, jobLeaseParamsFrom, verifyJobLeaseToken, } from '#core/job-lease-token.js';
|
|
14
|
-
export type { SignupPolicy } from '#core/ports.js';
|
|
14
|
+
export type { SignupDenialMessageFormat, SignupPolicy } from '#core/ports.js';
|
|
15
15
|
export { issueRunnerSessionToken, verifyRunnerSessionToken, } from '#core/runner-session-token.js';
|
|
16
16
|
export { createEnvironmentSignupPolicy, DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE, } from '#core/signup-policy.js';
|
|
17
17
|
export { type AuthenticatedSessionContext, createJwtAuthMethod, getAuthenticatedSessionContext, type RefreshSessionId, type UserId, } from '#presentation/auth/jwt-auth.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,sBAAsB,CAAC;AAGxD,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAqBjD,YAAY,EAAC,SAAS,EAAE,mBAAmB,EAAE,wBAAwB,EAAC,MAAM,uBAAuB,CAAC;AACpG,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,2BAA2B,EAC3B,wBAAwB,EACxB,+BAA+B,EAC/B,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,yBAAyB,EACzB,0BAA0B,EAC1B,0BAA0B,EAC1B,mBAAmB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAC,oBAAoB,EAAE,aAAa,EAAC,MAAM,eAAe,CAAC;AAClE,YAAY,EAAC,UAAU,EAAE,qBAAqB,EAAC,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAC,eAAe,EAAC,MAAM,sBAAsB,CAAC;AACrD,YAAY,EAAC,UAAU,EAAC,MAAM,+BAA+B,CAAC;AAC9D,YAAY,EAAC,IAAI,EAAE,UAAU,EAAC,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EACL,yBAAyB,EACzB,4BAA4B,EAC5B,uBAAuB,EACvB,6BAA6B,EAC7B,sBAAsB,EACtB,8BAA8B,EAC9B,qBAAqB,EACrB,+BAA+B,EAC/B,uBAAuB,EACvB,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,sBAAsB,CAAC;AAGxD,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAqBjD,YAAY,EAAC,SAAS,EAAE,mBAAmB,EAAE,wBAAwB,EAAC,MAAM,uBAAuB,CAAC;AACpG,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,2BAA2B,EAC3B,wBAAwB,EACxB,+BAA+B,EAC/B,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,yBAAyB,EACzB,0BAA0B,EAC1B,0BAA0B,EAC1B,mBAAmB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAC,oBAAoB,EAAE,aAAa,EAAC,MAAM,eAAe,CAAC;AAClE,YAAY,EAAC,UAAU,EAAE,qBAAqB,EAAC,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAC,eAAe,EAAC,MAAM,sBAAsB,CAAC;AACrD,YAAY,EAAC,UAAU,EAAC,MAAM,+BAA+B,CAAC;AAC9D,YAAY,EAAC,IAAI,EAAE,UAAU,EAAC,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EACL,yBAAyB,EACzB,4BAA4B,EAC5B,uBAAuB,EACvB,6BAA6B,EAC7B,sBAAsB,EACtB,8BAA8B,EAC9B,qBAAqB,EACrB,+BAA+B,EAC/B,uBAAuB,EACvB,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAC,yBAAyB,EAAE,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAC5E,OAAO,EACL,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,6BAA6B,EAC7B,kCAAkC,GACnC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,2BAA2B,EAChC,mBAAmB,EACnB,8BAA8B,EAC9B,KAAK,gBAAgB,EACrB,KAAK,MAAM,GACZ,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAC,0BAA0B,EAAC,MAAM,wCAAwC,CAAC;AAClF,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAC,6BAA6B,EAAC,MAAM,2CAA2C,CAAC;AAIxF,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,OAAO,0CAA0C,EAAE,2BAA2B,CAAC;IAC3F,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,EACV,YAA8C,GAC/C,EAAE,uBAAuB,GAAG,aAAa,CAiBzC"}
|