@plitzi/sdk-server 0.33.2 → 0.34.1
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/CHANGELOG.md +54 -0
- package/README.md +283 -8
- package/dist/actions.d.ts +62 -0
- package/dist/actions.js +12 -0
- package/dist/adapters/cloudAdapters.d.ts +72 -0
- package/dist/adapters/cloudAdapters.js +303 -0
- package/dist/adapters/space/index.d.ts +93 -0
- package/dist/adapters/space/index.js +122 -0
- package/dist/adapters/space/resolvers.d.ts +69 -0
- package/dist/adapters/space/resolvers.js +79 -0
- package/dist/adapters/space/space.test.d.ts +1 -0
- package/dist/adapters/space/types.d.ts +64 -0
- package/dist/adapters/space/types.js +41 -0
- package/dist/auth.d.ts +50 -0
- package/dist/auth.js +19 -0
- package/dist/core/auth/api.d.ts +400 -11
- package/dist/core/auth/api.js +1044 -50
- package/dist/core/auth/createAuth.d.ts +135 -30
- package/dist/core/auth/createAuth.js +96 -24
- package/dist/core/auth/credentials.d.ts +9 -3
- package/dist/core/auth/credentials.js +10 -4
- package/dist/core/auth/csrf.d.ts +89 -0
- package/dist/core/auth/csrf.js +234 -0
- package/dist/core/auth/csrf.test.d.ts +1 -0
- package/dist/core/auth/identity.d.ts +6 -0
- package/dist/core/auth/identity.js +37 -8
- package/dist/core/auth/lifecycle.test.d.ts +1 -0
- package/dist/core/auth/oauth/index.d.ts +8 -0
- package/dist/core/auth/oauth/index.js +13 -15
- package/dist/core/auth/passwords.d.ts +15 -0
- package/dist/core/auth/passwords.js +32 -0
- package/dist/core/auth/redirects.d.ts +18 -0
- package/dist/core/auth/redirects.js +31 -0
- package/dist/core/auth/redirects.test.d.ts +1 -0
- package/dist/core/auth/routes.d.ts +4 -2
- package/dist/core/auth/routes.js +234 -9
- package/dist/core/auth/session.d.ts +11 -0
- package/dist/core/auth/session.js +1 -1
- package/dist/core/auth/spaceTokens.d.ts +38 -2
- package/dist/core/auth/spaceTokens.js +54 -2
- package/dist/core/auth/throttle.d.ts +5 -0
- package/dist/core/auth/throttle.js +81 -0
- package/dist/core/auth/throttle.test.d.ts +1 -0
- package/dist/core/auth/tokens.d.ts +83 -12
- package/dist/core/auth/tokens.js +51 -9
- package/dist/core/auth/totp.d.ts +48 -0
- package/dist/core/auth/totp.js +118 -0
- package/dist/core/auth/totp.test.d.ts +1 -0
- package/dist/core/createServer.d.ts +0 -6
- package/dist/core/createServer.js +35 -3
- package/dist/core/fontStore.d.ts +33 -0
- package/dist/core/fontStore.js +53 -0
- package/dist/core/fontStore.test.d.ts +1 -0
- package/dist/core/handlers/authMiddleware.js +1 -1
- package/dist/core/handlers/authRouteHandlers.d.ts +9 -1
- package/dist/core/handlers/authRouteHandlers.js +4 -3
- package/dist/core/handlers/csrfMiddleware.d.ts +20 -0
- package/dist/core/handlers/csrfMiddleware.js +32 -0
- package/dist/core/handlers/oauthRouteHandlers.d.ts +46 -0
- package/dist/core/handlers/oauthRouteHandlers.js +159 -0
- package/dist/core/handlers/socialRouteHandlers.d.ts +40 -0
- package/dist/core/handlers/socialRouteHandlers.js +129 -0
- package/dist/core/handlers/socialRouteHandlers.test.d.ts +1 -0
- package/dist/core/handlers/types.d.ts +22 -5
- package/dist/core/health.d.ts +11 -1
- package/dist/core/health.js +27 -3
- package/dist/core/http/dispatcher.d.ts +1 -1
- package/dist/core/http/dispatcher.js +20 -1
- package/dist/core/http/stages/authApi.js +1 -1
- package/dist/core/http/stages/fontAssets.d.ts +9 -0
- package/dist/core/http/stages/fontAssets.js +27 -0
- package/dist/core/http/stages/oauth.d.ts +19 -0
- package/dist/core/http/stages/oauth.js +118 -0
- package/dist/core/http/types.d.ts +12 -0
- package/dist/core/oauth/authorize.d.ts +22 -0
- package/dist/core/oauth/authorize.js +254 -0
- package/dist/core/oauth/authorize.test.d.ts +1 -0
- package/dist/core/oauth/challenge.d.ts +11 -0
- package/dist/core/oauth/challenge.js +33 -0
- package/dist/core/oauth/consentPage.d.ts +12 -0
- package/dist/core/oauth/consentPage.js +131 -0
- package/dist/core/oauth/metadata.d.ts +41 -0
- package/dist/core/oauth/metadata.js +71 -0
- package/dist/core/oauth/params.d.ts +5 -0
- package/dist/core/oauth/params.js +5 -0
- package/dist/core/oauth/pkce.d.ts +6 -0
- package/dist/core/oauth/pkce.js +15 -0
- package/dist/core/oauth/records.d.ts +72 -0
- package/dist/core/oauth/records.js +49 -0
- package/dist/core/oauth/register.d.ts +5 -0
- package/dist/core/oauth/register.js +55 -0
- package/dist/core/oauth/respond.d.ts +21 -0
- package/dist/core/oauth/respond.js +59 -0
- package/dist/core/oauth/token.d.ts +14 -0
- package/dist/core/oauth/token.js +130 -0
- package/dist/core/previewToken.d.ts +20 -0
- package/dist/core/previewToken.js +29 -1
- package/dist/core/server/pageServer.js +4 -1
- package/dist/core/services/action.d.ts +6 -4
- package/dist/core/services/action.js +103 -10
- package/dist/core/services/registry.js +2 -0
- package/dist/core/services/resolve.d.ts +8 -0
- package/dist/core/services/resolve.js +12 -1
- package/dist/core/staticFiles.js +1 -2
- package/dist/handlers.d.ts +7 -1
- package/dist/handlers.js +4 -1
- package/dist/helpers/buildResponseHelpers.d.ts +8 -0
- package/dist/helpers/buildResponseHelpers.js +9 -1
- package/dist/helpers/buildServerInfo.d.ts +8 -3
- package/dist/helpers/buildServerInfo.js +18 -14
- package/dist/helpers/cache/keys.d.ts +13 -0
- package/dist/helpers/cache/keys.js +12 -1
- package/dist/helpers/onAbort.d.ts +12 -0
- package/dist/helpers/onAbort.js +22 -0
- package/dist/helpers/onAbort.test.d.ts +1 -0
- package/dist/helpers/outboundGuard.d.ts +27 -0
- package/dist/helpers/outboundGuard.js +49 -0
- package/dist/helpers/outboundGuard.test.d.ts +1 -0
- package/dist/helpers/serverLog.d.ts +6 -3
- package/dist/helpers/serverLog.js +20 -3
- package/dist/index.d.ts +6 -0
- package/dist/index.js +9 -4
- package/dist/kernel.d.ts +5 -22
- package/dist/kernel.js +1 -14
- package/dist/modules/actions/{handler.js → connectorWrite.js} +5 -2
- package/dist/modules/actions/connectorWrite.test.d.ts +1 -0
- package/dist/modules/actions/index.d.ts +40 -0
- package/dist/modules/actions/index.js +46 -0
- package/dist/modules/actions/moduleFor.d.ts +3 -0
- package/dist/modules/actions/moduleFor.js +35 -0
- package/dist/modules/actions/moduleFor.test.d.ts +1 -0
- package/dist/modules/actions/runtime/check.d.ts +24 -0
- package/dist/modules/actions/runtime/check.js +111 -0
- package/dist/modules/actions/runtime/check.test.d.ts +1 -0
- package/dist/modules/actions/runtime/errors.d.ts +11 -0
- package/dist/modules/actions/runtime/errors.js +17 -0
- package/dist/modules/actions/runtime/guards.d.ts +126 -0
- package/dist/modules/actions/runtime/guards.js +246 -0
- package/dist/modules/actions/runtime/guards.test.d.ts +1 -0
- package/dist/modules/actions/runtime/kvStore.d.ts +27 -0
- package/dist/modules/actions/runtime/kvStore.js +41 -0
- package/dist/modules/actions/runtime/kvStore.test.d.ts +1 -0
- package/dist/modules/actions/runtime/limits.d.ts +11 -0
- package/dist/modules/actions/runtime/limits.js +35 -0
- package/dist/modules/actions/runtime/memoryKv.d.ts +2 -0
- package/dist/modules/actions/runtime/memoryKv.js +77 -0
- package/dist/modules/actions/runtime/namespaceKv.d.ts +9 -0
- package/dist/modules/actions/runtime/namespaceKv.js +19 -0
- package/dist/modules/actions/runtime/precheck.d.ts +27 -0
- package/dist/modules/actions/runtime/precheck.js +45 -0
- package/dist/modules/actions/runtime/renderResolver.d.ts +15 -0
- package/dist/modules/actions/runtime/renderResolver.js +130 -0
- package/dist/modules/actions/runtime/renderResolver.test.d.ts +1 -0
- package/dist/modules/actions/runtime/renderShare.d.ts +29 -0
- package/dist/modules/actions/runtime/renderShare.js +36 -0
- package/dist/modules/actions/runtime/renderShare.test.d.ts +1 -0
- package/dist/modules/actions/runtime/report.d.ts +14 -0
- package/dist/modules/actions/runtime/report.js +22 -0
- package/dist/modules/actions/runtime/runAction.d.ts +12 -0
- package/dist/modules/actions/runtime/runAction.js +405 -0
- package/dist/modules/actions/runtime/runAction.test.d.ts +1 -0
- package/dist/modules/actions/runtime/runLogger.d.ts +31 -0
- package/dist/modules/actions/runtime/runLogger.js +66 -0
- package/dist/modules/actions/runtime/runLogger.test.d.ts +1 -0
- package/dist/modules/actions/runtime/schedule.d.ts +35 -0
- package/dist/modules/actions/runtime/schedule.js +76 -0
- package/dist/modules/actions/runtime/schedule.test.d.ts +1 -0
- package/dist/modules/actions/runtime/scope.d.ts +40 -0
- package/dist/modules/actions/runtime/scope.js +103 -0
- package/dist/modules/actions/runtime/triggers.d.ts +12 -0
- package/dist/modules/actions/runtime/triggers.js +14 -0
- package/dist/modules/actions/taskCatalog.d.ts +26 -0
- package/dist/modules/actions/taskCatalog.js +24 -0
- package/dist/modules/actions/taskCatalog.test.d.ts +1 -0
- package/dist/modules/actions/tasks/auth.d.ts +2 -0
- package/dist/modules/actions/tasks/auth.js +40 -0
- package/dist/modules/actions/tasks/builtins.d.ts +8 -0
- package/dist/modules/actions/tasks/builtins.js +24 -0
- package/dist/modules/actions/tasks/connector.d.ts +2 -0
- package/dist/modules/actions/tasks/connector.js +141 -0
- package/dist/modules/actions/tasks/db.d.ts +2 -0
- package/dist/modules/actions/tasks/db.js +59 -0
- package/dist/modules/actions/tasks/db.test.d.ts +1 -0
- package/dist/modules/actions/tasks/flow.d.ts +3 -0
- package/dist/modules/actions/tasks/flow.js +109 -0
- package/dist/modules/actions/tasks/helpers.d.ts +16 -0
- package/dist/modules/actions/tasks/helpers.js +30 -0
- package/dist/modules/actions/tasks/http.d.ts +2 -0
- package/dist/modules/actions/tasks/http.js +111 -0
- package/dist/modules/actions/tasks/http.test.d.ts +1 -0
- package/dist/modules/actions/tasks/kv.d.ts +2 -0
- package/dist/modules/actions/tasks/kv.js +102 -0
- package/dist/modules/actions/tasks/kv.test.d.ts +1 -0
- package/dist/modules/actions/tasks/registry.d.ts +10 -0
- package/dist/modules/actions/tasks/registry.js +36 -0
- package/dist/modules/actions/tasks/transform.d.ts +2 -0
- package/dist/modules/actions/tasks/transform.js +34 -0
- package/dist/modules/actions/transport/callHandler.d.ts +24 -0
- package/dist/modules/actions/transport/callHandler.js +233 -0
- package/dist/modules/actions/transport/callHandler.test.d.ts +1 -0
- package/dist/modules/actions/transport/cancelHandler.d.ts +23 -0
- package/dist/modules/actions/transport/cancelHandler.js +23 -0
- package/dist/modules/actions/transport/catalogHandler.d.ts +18 -0
- package/dist/modules/actions/transport/catalogHandler.js +21 -0
- package/dist/modules/actions/transport/stream.d.ts +23 -0
- package/dist/modules/actions/transport/stream.js +100 -0
- package/dist/modules/actions/transport/verifySignature.d.ts +16 -0
- package/dist/modules/actions/transport/verifySignature.js +72 -0
- package/dist/modules/actions/transport/webhookHandler.d.ts +25 -0
- package/dist/modules/actions/transport/webhookHandler.js +206 -0
- package/dist/modules/actions/transport/webhookHandler.test.d.ts +1 -0
- package/dist/modules/actions/types.d.ts +234 -0
- package/dist/modules/connectors/engine.js +4 -1
- package/dist/modules/connectors/resolver.d.ts +10 -2
- package/dist/modules/connectors/resolver.js +8 -3
- package/dist/modules/mysql/accounts.d.ts +5 -0
- package/dist/modules/mysql/accounts.js +365 -0
- package/dist/modules/mysql/accounts.test.d.ts +1 -0
- package/dist/modules/mysql/admin.d.ts +58 -0
- package/dist/modules/mysql/admin.js +133 -0
- package/dist/modules/mysql/config.d.ts +82 -0
- package/dist/modules/mysql/config.js +30 -0
- package/dist/modules/mysql/identities.d.ts +35 -0
- package/dist/modules/mysql/identities.js +43 -0
- package/dist/modules/mysql/index.d.ts +67 -0
- package/dist/modules/mysql/index.js +62 -0
- package/dist/modules/mysql/pool.d.ts +3 -0
- package/dist/modules/mysql/pool.js +88 -0
- package/dist/modules/mysql/query.d.ts +12 -0
- package/dist/modules/mysql/query.js +21 -0
- package/dist/modules/mysql/schema.d.ts +43 -0
- package/dist/modules/mysql/schema.js +340 -0
- package/dist/modules/mysql/schema.test.d.ts +1 -0
- package/dist/modules/mysql/sessions.d.ts +33 -0
- package/dist/modules/mysql/sessions.js +46 -0
- package/dist/modules/mysql/spaceTokens.d.ts +11 -0
- package/dist/modules/mysql/spaceTokens.js +59 -0
- package/dist/modules/mysql/store.integration.test.d.ts +1 -0
- package/dist/modules/rsc/connectorRscData.d.ts +25 -0
- package/dist/modules/rsc/connectorRscData.js +51 -0
- package/dist/modules/rsc/connectorRscData.test.d.ts +1 -0
- package/dist/modules/rsc/handler.js +16 -3
- package/dist/modules/rsc/matchRscPage.d.ts +14 -0
- package/dist/modules/rsc/matchRscPage.js +26 -0
- package/dist/modules/rsc/resolveRscData.d.ts +14 -1
- package/dist/modules/rsc/resolveRscData.js +23 -34
- package/dist/modules/ssr/Component.d.ts +6 -2
- package/dist/modules/ssr/Component.js +3 -1
- package/dist/modules/ssr/draftSession.test.d.ts +1 -0
- package/dist/modules/ssr/handler.js +15 -2
- package/dist/modules/ssr/loadPluginComponents.js +22 -5
- package/dist/modules/ssr/prepareRender.js +87 -8
- package/dist/modules/ssr/prepareRender.test.d.ts +1 -0
- package/dist/modules/ssr/preview.d.ts +21 -8
- package/dist/modules/ssr/preview.js +38 -14
- package/dist/modules/ssr/registerExternalPlugins.js +2 -2
- package/dist/modules/ssr/resolvePageSeo.d.ts +18 -0
- package/dist/modules/ssr/resolvePageSeo.js +26 -0
- package/dist/modules/ssr/resolvePageSeo.test.d.ts +1 -0
- package/dist/modules/ssr/template.test.d.ts +1 -0
- package/dist/modules/ssr/views/template.ejs +71 -5
- package/dist/mysql.d.ts +26 -0
- package/dist/mysql.js +4 -0
- package/dist/oauth.d.ts +24 -0
- package/dist/oauth.js +10 -0
- package/dist/plugins/compile.d.ts +1 -0
- package/dist/plugins/compile.js +37 -3
- package/dist/plugins/manager.d.ts +40 -0
- package/dist/plugins/manager.js +113 -13
- package/dist/plugins/manager.test.d.ts +1 -0
- package/package.json +43 -14
- package/skills/plitzi-authoring/SKILL.md +259 -0
- /package/dist/{modules/actions/handler.test.d.ts → adapters/cloudAdapters.test.d.ts} +0 -0
- /package/dist/modules/actions/{handler.d.ts → connectorWrite.d.ts} +0 -0
package/dist/core/auth/api.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CredentialCarrier } from './credentials';
|
|
2
|
+
import { Csrf } from './csrf';
|
|
2
3
|
import { Actor, Identity } from './identity';
|
|
3
|
-
import { Tokens } from './tokens';
|
|
4
|
+
import { AuthFailure, Tokens } from './tokens';
|
|
4
5
|
import { SSRSession } from '@plitzi/sdk-shared';
|
|
5
6
|
/**
|
|
6
7
|
* An account, as whatever stores accounts reports one. Deliberately not a user model: these are the only fields any
|
|
@@ -18,11 +19,81 @@ export interface AccountRecord {
|
|
|
18
19
|
passwordHash?: string;
|
|
19
20
|
/** Unix seconds, for the refresh credential this account currently holds. */
|
|
20
21
|
refreshExpiresAt?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Unix seconds the session being renewed BEGAN, for `lifetimes.session` — the cap on how long one may live
|
|
24
|
+
* however often it renews. Only `findByRefreshToken` needs to report it, and only a deployment that sets a cap.
|
|
25
|
+
*
|
|
26
|
+
* The renewal window is already an idle timeout: a session nobody refreshes dies with its refresh token. This is
|
|
27
|
+
* the other half, and without it a session that renews quietly renews forever.
|
|
28
|
+
*/
|
|
29
|
+
sessionStartedAt?: number;
|
|
21
30
|
}
|
|
22
31
|
export interface AccountAccess {
|
|
23
32
|
roles: string[];
|
|
24
33
|
permissions: string[];
|
|
25
34
|
}
|
|
35
|
+
/** What a session was created from, so a "your devices" list can name it. */
|
|
36
|
+
export interface SessionClient {
|
|
37
|
+
userAgent?: string;
|
|
38
|
+
ip?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* What an account is allowed to be. `inactive` is the account's own doing (deactivated, never confirmed);
|
|
42
|
+
* `blocked` is the deployment's. Neither may hold a session — the distinction is for whoever has to explain it.
|
|
43
|
+
*/
|
|
44
|
+
export type AccountStatus = 'active' | 'inactive' | 'blocked';
|
|
45
|
+
/** One live session, as its owner may see it. Never the credential itself. */
|
|
46
|
+
export interface SessionSummary {
|
|
47
|
+
id: number;
|
|
48
|
+
userAgent?: string;
|
|
49
|
+
ip?: string;
|
|
50
|
+
createdAt: number;
|
|
51
|
+
expiresAt: number;
|
|
52
|
+
/** The session asking. A device list without it invites someone to revoke the one they are using. */
|
|
53
|
+
current: boolean;
|
|
54
|
+
}
|
|
55
|
+
/** A second factor as the store keeps it. `secret` is the TOTP seed; `recoveryCodes` are already hashed. */
|
|
56
|
+
export interface MfaRecord {
|
|
57
|
+
secret: string;
|
|
58
|
+
/** Unix seconds the enrolment was proven with a real code. Absent means started and never finished. */
|
|
59
|
+
confirmedAt?: number;
|
|
60
|
+
recoveryCodes?: string[];
|
|
61
|
+
}
|
|
62
|
+
/** Something worth writing down. Fed to an audit log, a webhook, a SIEM — whatever the deployment has. */
|
|
63
|
+
export interface SecurityEvent {
|
|
64
|
+
type: 'login' | 'login.failed' | 'login.mfa-required' | 'logout' | 'signup' | 'password.changed' | 'password.reset' | 'profile.changed' | 'email.changed' | 'account.deleted' | 'session.revoked' | 'mfa.enabled' | 'mfa.disabled' | 'mfa.failed' | 'admin.status-changed' | 'admin.roles-changed' | 'admin.account-deleted' | 'admin.impersonated';
|
|
65
|
+
/** Who it happened to. Absent when the attempt named nobody that exists. */
|
|
66
|
+
userId?: number;
|
|
67
|
+
/** Who did it, when that is somebody else — an administrator acting on an account. */
|
|
68
|
+
actorId?: number;
|
|
69
|
+
at: number;
|
|
70
|
+
detail?: Record<string, unknown>;
|
|
71
|
+
carrier?: CredentialCarrier;
|
|
72
|
+
}
|
|
73
|
+
export interface AccountQuery {
|
|
74
|
+
/** Matched against username and email. */
|
|
75
|
+
search?: string;
|
|
76
|
+
status?: AccountStatus;
|
|
77
|
+
limit?: number;
|
|
78
|
+
offset?: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Why a session is being written — which is not something the pair itself can say, and the difference matters to
|
|
82
|
+
* any store that keeps more than one session per account.
|
|
83
|
+
*
|
|
84
|
+
* A sign-in creates a session. A renewal **replaces one**, and a store that cannot tell them apart grows a row per
|
|
85
|
+
* renewal: a device list that fills with ghosts of the same browser, and a revoked session that comes back because
|
|
86
|
+
* the row it was meant to overwrite is still there. A store that keeps a single pair on the account row ignores
|
|
87
|
+
* this and overwrites either way, which is what it always did.
|
|
88
|
+
*/
|
|
89
|
+
export interface SessionContext {
|
|
90
|
+
/** The credential being renewed. Absent for a fresh sign-in. */
|
|
91
|
+
replaces?: {
|
|
92
|
+
refreshToken?: string;
|
|
93
|
+
accessToken?: string;
|
|
94
|
+
};
|
|
95
|
+
client?: SessionClient;
|
|
96
|
+
}
|
|
26
97
|
/**
|
|
27
98
|
* The account store, as auth needs to see it. Every method is optional except the three the session cycle cannot do
|
|
28
99
|
* without, and **what is absent decides what this server offers**: a deployment with no `createAccount` has no
|
|
@@ -30,8 +101,14 @@ export interface AccountAccess {
|
|
|
30
101
|
* deployment with its own user table and one that signs everybody in through an external provider.
|
|
31
102
|
*/
|
|
32
103
|
export interface AccountAdapters {
|
|
33
|
-
/**
|
|
34
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Persist a freshly minted pair.
|
|
106
|
+
*
|
|
107
|
+
* `context` says whether this is a new session or one replacing another — see {@link SessionContext}. A store
|
|
108
|
+
* with one pair per account may ignore it and overwrite; a store with a `session` table must not, or every
|
|
109
|
+
* renewal leaves a row behind.
|
|
110
|
+
*/
|
|
111
|
+
saveSession: (userId: number, session: SSRSession, context?: SessionContext) => Promise<void>;
|
|
35
112
|
/** Clear the pair, by whichever half the caller holds, or by account. */
|
|
36
113
|
clearSession: (target: {
|
|
37
114
|
accessToken?: string;
|
|
@@ -40,6 +117,40 @@ export interface AccountAdapters {
|
|
|
40
117
|
}) => Promise<void>;
|
|
41
118
|
/** Global roles and permissions, for the body a grant answers with. */
|
|
42
119
|
loadAccess: (userId: number) => Promise<AccountAccess>;
|
|
120
|
+
/** By id. Needed wherever a flow acts on the account already signed in — changing a password, deleting itself. */
|
|
121
|
+
findById?: (userId: number) => Promise<AccountRecord | undefined>;
|
|
122
|
+
/**
|
|
123
|
+
* Change what an account says about itself. Only the keys present are touched — an absent `email` means "leave
|
|
124
|
+
* it", never "clear it", which is what an implementation writing every column unconditionally would do.
|
|
125
|
+
*/
|
|
126
|
+
updateAccount?: (userId: number, changes: {
|
|
127
|
+
username?: string;
|
|
128
|
+
email?: string;
|
|
129
|
+
}) => Promise<AccountRecord>;
|
|
130
|
+
/**
|
|
131
|
+
* Suspend, block or restore. Separate from `deleteAccount` because they are different acts with different
|
|
132
|
+
* consequences: a suspension is reversible and keeps everything the account made.
|
|
133
|
+
*/
|
|
134
|
+
setStatus?: (userId: number, status: AccountStatus) => Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* Erase the account. Whether that means deleting a row or anonymising one is the deployment's call and it is a
|
|
137
|
+
* real one — content, audit logs and invoices usually have to outlive the person. Either way it must end every
|
|
138
|
+
* session, which deleting the account's sessions does.
|
|
139
|
+
*/
|
|
140
|
+
deleteAccount?: (userId: number) => Promise<void>;
|
|
141
|
+
/** Page through accounts, for an administrator. `total` is the count before the page was taken. */
|
|
142
|
+
listAccounts?: (query: AccountQuery) => Promise<{
|
|
143
|
+
accounts: AccountRecord[];
|
|
144
|
+
total: number;
|
|
145
|
+
}>;
|
|
146
|
+
/** Replace an account's global roles with exactly these. */
|
|
147
|
+
setRoles?: (userId: number, roles: string[]) => Promise<void>;
|
|
148
|
+
/** The account's live sessions. `currentToken` marks the one asking, so a device list can say "this device". */
|
|
149
|
+
listSessions?: (userId: number, currentToken?: string) => Promise<SessionSummary[]>;
|
|
150
|
+
/** End one session. Scoped by account: a session id from another account must not resolve. */
|
|
151
|
+
revokeSession?: (userId: number, sessionId: number) => Promise<boolean>;
|
|
152
|
+
/** End every session except the one asking. */
|
|
153
|
+
revokeOtherSessions?: (userId: number, currentToken: string) => Promise<number>;
|
|
43
154
|
findByUsername?: (username: string) => Promise<AccountRecord | undefined>;
|
|
44
155
|
findByRefreshToken?: (token: string) => Promise<AccountRecord | undefined>;
|
|
45
156
|
createAccount?: (account: {
|
|
@@ -53,7 +164,51 @@ export interface AccountAdapters {
|
|
|
53
164
|
findByResetToken?: (token: string) => Promise<AccountRecord | undefined>;
|
|
54
165
|
setValidationToken?: (userId: number, token: string) => Promise<void>;
|
|
55
166
|
findByValidationToken?: (token: string) => Promise<AccountRecord | undefined>;
|
|
56
|
-
|
|
167
|
+
/**
|
|
168
|
+
* Park an address the account asked to move to, with the token that will confirm it. It is NOT the account's
|
|
169
|
+
* address yet, and nothing may sign in with it — the whole point is that the old one keeps working until the new
|
|
170
|
+
* one is proven, so a typo is a nuisance instead of a lockout.
|
|
171
|
+
*
|
|
172
|
+
* Supplying these three (with `sendMail`) is what turns an email change into a confirmed one. A deployment that
|
|
173
|
+
* leaves them out changes the address on the spot, which is the simpler thing and a legitimate choice.
|
|
174
|
+
*/
|
|
175
|
+
setPendingEmail?: (userId: number, email: string, token: string) => Promise<void>;
|
|
176
|
+
/** The account waiting on this confirmation, and the address it is waiting for. */
|
|
177
|
+
findByPendingEmail?: (token: string) => Promise<{
|
|
178
|
+
account: AccountRecord;
|
|
179
|
+
email: string;
|
|
180
|
+
} | undefined>;
|
|
181
|
+
/** Forget the parked address — it was confirmed, superseded, or the account was closed. */
|
|
182
|
+
clearPendingEmail?: (userId: number) => Promise<void>;
|
|
183
|
+
/**
|
|
184
|
+
* Confirm or un-confirm the address. Both directions from one adapter, because changing an email has to be able
|
|
185
|
+
* to take verification away — a `markVerified` that only ever set it made that impossible to express.
|
|
186
|
+
*/
|
|
187
|
+
setVerified?: (userId: number, verified: boolean) => Promise<void>;
|
|
188
|
+
/**
|
|
189
|
+
* A one-time code, hashed. `purpose` separates a sign-in link from anything else that uses the same table, and
|
|
190
|
+
* `identifier` is what it was issued against — an email address, usually.
|
|
191
|
+
*/
|
|
192
|
+
saveOtp?: (code: {
|
|
193
|
+
purpose: string;
|
|
194
|
+
identifier: string;
|
|
195
|
+
codeHash: string;
|
|
196
|
+
expiresAt: number;
|
|
197
|
+
userId?: number;
|
|
198
|
+
}) => Promise<void>;
|
|
199
|
+
/** The live code for this purpose and identifier, if there is one. Expired rows may be returned or not. */
|
|
200
|
+
findOtp?: (purpose: string, identifier: string) => Promise<{
|
|
201
|
+
id: number;
|
|
202
|
+
codeHash: string;
|
|
203
|
+
expiresAt: number;
|
|
204
|
+
userId?: number;
|
|
205
|
+
} | undefined>;
|
|
206
|
+
/** Spend it. Deleting rather than flagging: a spent code that still exists is one a lookup can forget to exclude. */
|
|
207
|
+
consumeOtp?: (id: number) => Promise<void>;
|
|
208
|
+
/** The second factor for this account, if it has one. Without it, no MFA is offered at all. */
|
|
209
|
+
loadMfa?: (userId: number) => Promise<MfaRecord | undefined>;
|
|
210
|
+
saveMfa?: (userId: number, record: MfaRecord) => Promise<void>;
|
|
211
|
+
deleteMfa?: (userId: number) => Promise<void>;
|
|
57
212
|
/** Called for validation and password-reset mail. Without it neither flow is offered. */
|
|
58
213
|
sendMail?: (message: {
|
|
59
214
|
to: string;
|
|
@@ -70,15 +225,110 @@ export interface AccountAdapters {
|
|
|
70
225
|
status?: number;
|
|
71
226
|
} | undefined>;
|
|
72
227
|
}
|
|
228
|
+
/** What a flow was asked to do, for the throttle below. */
|
|
229
|
+
export type ThrottledAction = 'login' | 'signup' | 'forgotPassword' | 'resetPassword' | 'changePassword' | 'exchange' | 'mfa' | 'passwordless';
|
|
230
|
+
export interface ThrottleAttempt {
|
|
231
|
+
action: ThrottledAction;
|
|
232
|
+
/** What is being attempted against — a username, an email, a token. Never a password. */
|
|
233
|
+
key: string;
|
|
234
|
+
carrier?: CredentialCarrier;
|
|
235
|
+
/**
|
|
236
|
+
* Not a question but a report: this one worked, so whatever was counted against the key can be forgotten. The
|
|
237
|
+
* return value is ignored.
|
|
238
|
+
*
|
|
239
|
+
* It matters because the check happens BEFORE the password is examined — which is what makes a throttled attempt
|
|
240
|
+
* cost no hash — so without this the counter cannot tell ten failures from ten sign-ins. An app that signs the
|
|
241
|
+
* same account in repeatedly would lock it out by succeeding. A limiter that ignores this is simply stricter.
|
|
242
|
+
*/
|
|
243
|
+
succeeded?: boolean;
|
|
244
|
+
}
|
|
245
|
+
export interface PasswordPolicy {
|
|
246
|
+
/**
|
|
247
|
+
* Default 8, which is NIST SP 800-63B's floor. There are deliberately no composition rules: requiring a digit
|
|
248
|
+
* and a symbol measurably produces `Password1!` and nothing safer.
|
|
249
|
+
*/
|
|
250
|
+
minLength?: number;
|
|
251
|
+
/** Anything else this deployment decides — a breach-list lookup, a strength estimator. Return why, or nothing. */
|
|
252
|
+
validate?: (password: string, context: {
|
|
253
|
+
username?: string;
|
|
254
|
+
email?: string;
|
|
255
|
+
}) => Promise<string | undefined>;
|
|
256
|
+
}
|
|
73
257
|
export interface AuthApiConfig {
|
|
74
|
-
/**
|
|
258
|
+
/**
|
|
259
|
+
* How passwords are hashed and checked. **Defaults to scrypt from the standard library**, so a deployment that
|
|
260
|
+
* has no opinion does not have to have one — and, more to the point, cannot end up with password login quietly
|
|
261
|
+
* switched off because it did not supply a function it had no reason to know about.
|
|
262
|
+
*
|
|
263
|
+
* Supply both to keep an existing algorithm: a store full of bcrypt hashes needs bcrypt.
|
|
264
|
+
*/
|
|
75
265
|
verifyPassword?: (plain: string, hash: string) => Promise<boolean>;
|
|
76
266
|
hashPassword?: (plain: string) => Promise<string>;
|
|
77
|
-
/** Opaque, single-use strings for validation and reset links. */
|
|
267
|
+
/** Opaque, single-use strings for validation and reset links. Defaults to 128 random bits, hex. */
|
|
78
268
|
generateToken?: () => string;
|
|
79
269
|
/** Whether a new account may sign in immediately or has to confirm its address first. */
|
|
80
270
|
verifyOnSignup?: boolean;
|
|
271
|
+
/**
|
|
272
|
+
* The global capability an administrator must hold to act on somebody else's account. Named rather than fixed,
|
|
273
|
+
* because what a deployment calls its permissions is its own vocabulary. Default `userManage`.
|
|
274
|
+
*/
|
|
275
|
+
adminPermission?: string;
|
|
276
|
+
/**
|
|
277
|
+
* The capability that lets somebody obtain a session AS another account, and the switch that offers the flow at
|
|
278
|
+
* all: **absent, there is no impersonation** and `/auth/admin/impersonate` answers 404.
|
|
279
|
+
*
|
|
280
|
+
* Deliberately its own permission rather than part of `adminPermission`. Support staff who can suspend an account
|
|
281
|
+
* and support staff who can become one are not the same grant, and defaulting the second to the first would hand
|
|
282
|
+
* it out to everybody who already had the first — a power that appears from an upgrade nobody read.
|
|
283
|
+
*/
|
|
284
|
+
impersonationPermission?: string;
|
|
285
|
+
/** What a password has to be. Applied wherever one is set: signing up, resetting, changing. */
|
|
286
|
+
password?: PasswordPolicy;
|
|
287
|
+
/**
|
|
288
|
+
* May this attempt proceed? Answered **in memory by default**, so no deployment is unthrottled by omission.
|
|
289
|
+
*
|
|
290
|
+
* There used to be no default, on the reasoning that where the counter lives is a deployment decision. It is —
|
|
291
|
+
* but the consequence of leaving it out was that the ordinary deployment shipped an unmetered password oracle,
|
|
292
|
+
* because nobody configures an option they have not read about. The counting is per process; supply this to put
|
|
293
|
+
* one counter behind the whole fleet, which is what a cluster wants.
|
|
294
|
+
*
|
|
295
|
+
* A refusal is a 429 with `retryAfter`, raised before any password is checked so it costs no hash.
|
|
296
|
+
*/
|
|
297
|
+
rateLimit?: (attempt: ThrottleAttempt) => Promise<boolean | {
|
|
298
|
+
allowed: boolean;
|
|
299
|
+
retryAfter?: number;
|
|
300
|
+
}>;
|
|
301
|
+
/** Where a failed delivery is reported. Defaults to `console.error`; it is never thrown — see `deliver`. */
|
|
302
|
+
onMailError?: (error: unknown, message: {
|
|
303
|
+
to: string;
|
|
304
|
+
template: string;
|
|
305
|
+
}) => void;
|
|
306
|
+
/**
|
|
307
|
+
* Every act worth recording, as it happens.
|
|
308
|
+
*
|
|
309
|
+
* An audit trail, a webhook, an alert on ten failed sign-ins — all the same feed, and none of them something the
|
|
310
|
+
* server should decide the shape of. Never awaited and never allowed to fail a request: a logging outage must
|
|
311
|
+
* not become an authentication outage.
|
|
312
|
+
*/
|
|
313
|
+
onEvent?: (event: SecurityEvent) => void;
|
|
314
|
+
/** What an authenticator app calls this deployment when somebody enrols. Defaults to the token issuer. */
|
|
315
|
+
mfaIssuer?: string;
|
|
316
|
+
/** Set by `createAuth`, so `GET /auth/csrf` can mint one. Nothing here enforces the check — the routes do. */
|
|
317
|
+
csrf?: Csrf;
|
|
81
318
|
}
|
|
319
|
+
/** What an exchange came to, before it is turned into either an HTTP body or a rendered page's session. */
|
|
320
|
+
export type ExchangeResult = {
|
|
321
|
+
ok: true;
|
|
322
|
+
account: AccountRecord;
|
|
323
|
+
access: AccountAccess;
|
|
324
|
+
session: SSRSession;
|
|
325
|
+
} | {
|
|
326
|
+
ok: false;
|
|
327
|
+
offered: boolean;
|
|
328
|
+
status: number;
|
|
329
|
+
error: string;
|
|
330
|
+
reason?: AuthFailure;
|
|
331
|
+
};
|
|
82
332
|
/** What a handler answers: a body, and optionally what should happen to the session cookies. */
|
|
83
333
|
export type AuthOutcome = {
|
|
84
334
|
ok: true;
|
|
@@ -86,10 +336,19 @@ export type AuthOutcome = {
|
|
|
86
336
|
body: object;
|
|
87
337
|
session?: SSRSession;
|
|
88
338
|
endSession?: boolean;
|
|
89
|
-
|
|
339
|
+
csrf?: string;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* `endSession` on a REFUSAL is not a contradiction: it is how a flow says "what this browser is holding is
|
|
343
|
+
* dead, stop holding it". Without it the cookies survive the refusal that proved them worthless, and the
|
|
344
|
+
* readable hint — which outlives the access token on purpose, to advertise the renewal window — sends the page
|
|
345
|
+
* back to renew on every single load. One 401 per page view, forever, for a session that ended weeks ago.
|
|
346
|
+
*/
|
|
347
|
+
| {
|
|
90
348
|
ok: false;
|
|
91
349
|
status: number;
|
|
92
350
|
body: object;
|
|
351
|
+
endSession?: boolean;
|
|
93
352
|
};
|
|
94
353
|
/**
|
|
95
354
|
* The HTTP surface of authentication, as functions rather than routes.
|
|
@@ -112,6 +371,17 @@ export declare const createAuthApi: ({ tokens, identity, adapters, config }: {
|
|
|
112
371
|
passwordReset: boolean;
|
|
113
372
|
emailVerification: boolean;
|
|
114
373
|
exchange: boolean;
|
|
374
|
+
mfa: boolean;
|
|
375
|
+
passwordless: boolean;
|
|
376
|
+
profile: boolean;
|
|
377
|
+
passwordChange: boolean;
|
|
378
|
+
accountDeletion: boolean;
|
|
379
|
+
sessionList: boolean;
|
|
380
|
+
administration: boolean;
|
|
381
|
+
/** Changing an address by confirming it, rather than on the spot. Needs somewhere to park it and a way to write. */
|
|
382
|
+
emailChange: boolean;
|
|
383
|
+
/** Off unless the deployment named the capability it takes — see `impersonationPermission`. */
|
|
384
|
+
impersonation: boolean;
|
|
115
385
|
};
|
|
116
386
|
/** What a client reads to know which of the flows below actually exist here. */
|
|
117
387
|
describe: () => AuthOutcome;
|
|
@@ -121,8 +391,39 @@ export declare const createAuthApi: ({ tokens, identity, adapters, config }: {
|
|
|
121
391
|
* signed-out from the refusal itself. It costs no query: the actor was resolved before routing.
|
|
122
392
|
*/
|
|
123
393
|
session: (actor?: Actor) => AuthOutcome;
|
|
124
|
-
login: (credentials: Record<string, unknown
|
|
125
|
-
|
|
394
|
+
login: (credentials: Record<string, unknown>, carrier?: CredentialCarrier) => Promise<AuthOutcome>;
|
|
395
|
+
/**
|
|
396
|
+
* The second half of a sign-in that owed a factor.
|
|
397
|
+
*
|
|
398
|
+
* Takes a TOTP code or a recovery code. A recovery code is SPENT — removed from the stored list — because one
|
|
399
|
+
* that survives being used is a password with extra steps.
|
|
400
|
+
*/
|
|
401
|
+
completeMfa: (mfaToken: string, code: string, carrier?: CredentialCarrier) => Promise<AuthOutcome>;
|
|
402
|
+
/**
|
|
403
|
+
* Signing in with a code sent to an email address, and no password at all.
|
|
404
|
+
*
|
|
405
|
+
* Two decisions worth stating. **The request answers the same whether the address exists or not** — anything
|
|
406
|
+
* else turns this endpoint into a way to ask which addresses have accounts. And **it never creates an
|
|
407
|
+
* account**: a sign-in flow that silently registers whoever asks is a different feature, and one a deployment
|
|
408
|
+
* should choose deliberately rather than inherit.
|
|
409
|
+
*/
|
|
410
|
+
passwordless: {
|
|
411
|
+
request: (email: string, carrier?: CredentialCarrier) => Promise<AuthOutcome>;
|
|
412
|
+
complete: (email: string, code: string, carrier?: CredentialCarrier) => Promise<AuthOutcome>;
|
|
413
|
+
};
|
|
414
|
+
/**
|
|
415
|
+
* Everything about this account's second factor. `begin` hands back a secret and the URI an app scans; nothing
|
|
416
|
+
* is in force until `confirm` proves a code from it.
|
|
417
|
+
*/
|
|
418
|
+
mfa: {
|
|
419
|
+
status: (actor: Actor | undefined) => Promise<AuthOutcome>;
|
|
420
|
+
begin: (actor: Actor | undefined) => Promise<AuthOutcome>;
|
|
421
|
+
/** Proves the app was actually set up, and only then does the factor start being required. */
|
|
422
|
+
confirm: (actor: Actor | undefined, code: string) => Promise<AuthOutcome>;
|
|
423
|
+
/** Removing a factor is a security downgrade, so it asks for the password the way deleting an account does. */
|
|
424
|
+
disable: (actor: Actor | undefined, password?: string) => Promise<AuthOutcome>;
|
|
425
|
+
};
|
|
426
|
+
refresh: (refreshToken?: string, carrier?: CredentialCarrier) => Promise<AuthOutcome>;
|
|
126
427
|
/** Reachable with an expired access token on purpose, or a live refresh token could never be revoked. */
|
|
127
428
|
logout: (credentials: {
|
|
128
429
|
accessToken?: string;
|
|
@@ -135,14 +436,102 @@ export declare const createAuthApi: ({ tokens, identity, adapters, config }: {
|
|
|
135
436
|
*/
|
|
136
437
|
revokeSessions: (userId?: number) => Promise<AuthOutcome>;
|
|
137
438
|
exchange: (provider: string, token: string, carrier: CredentialCarrier) => Promise<AuthOutcome>;
|
|
439
|
+
/**
|
|
440
|
+
* The same exchange, answering in accounts rather than in HTTP.
|
|
441
|
+
*
|
|
442
|
+
* A page server needs who came back and the session they got; the flow above needs a body and a status. They
|
|
443
|
+
* are the same act, and the translation between them has one correct answer — so it is made once, here, rather
|
|
444
|
+
* than by every deployment that wires an external identity provider into a rendered page.
|
|
445
|
+
*/
|
|
446
|
+
exchangeAccount: (provider: string, token: string, carrier: CredentialCarrier) => Promise<ExchangeResult>;
|
|
138
447
|
signup: (fields: Record<string, unknown>) => Promise<AuthOutcome>;
|
|
139
448
|
/** Answers the same either way: whether an address has an account here is not something a stranger may probe. */
|
|
140
449
|
forgotPassword: (email: string) => Promise<AuthOutcome>;
|
|
141
450
|
resetPassword: (token: string, password: string) => Promise<AuthOutcome>;
|
|
142
451
|
validateAccount: (token: string) => Promise<AuthOutcome>;
|
|
143
|
-
resendVerification: (email: string) => Promise<AuthOutcome>;
|
|
452
|
+
resendVerification: (email: string, redirect?: string) => Promise<AuthOutcome>;
|
|
453
|
+
/**
|
|
454
|
+
* Change what an account says about itself.
|
|
455
|
+
*
|
|
456
|
+
* Both fields are sign-in identifiers, so both are checked for collision first — a store that only has a unique
|
|
457
|
+
* index reports the clash as a driver error five hundred lines away.
|
|
458
|
+
*/
|
|
459
|
+
updateProfile: (actor: Actor | undefined, fields: Record<string, unknown>) => Promise<AuthOutcome>;
|
|
460
|
+
/**
|
|
461
|
+
* Takes the parked address, having proved somebody reads it.
|
|
462
|
+
*
|
|
463
|
+
* Public, and it has to be: the link is opened from a mail client, in whatever browser that happens to be, with
|
|
464
|
+
* no session. The token is the whole credential — which is why it is single-use and why confirming also marks
|
|
465
|
+
* the account verified: an address that answered a link is an address that was proven.
|
|
466
|
+
*/
|
|
467
|
+
confirmEmailChange: (token: string) => Promise<AuthOutcome>;
|
|
468
|
+
/**
|
|
469
|
+
* Change a password, having proved you know the current one.
|
|
470
|
+
*
|
|
471
|
+
* Then **every other session ends**. Changing a password is what somebody does when they think a credential
|
|
472
|
+
* escaped, and one that leaves the other sessions signed in has not done the thing they asked for. The session
|
|
473
|
+
* making the change survives, or the act of securing the account would sign them out of it.
|
|
474
|
+
*/
|
|
475
|
+
changePassword: (actor: Actor | undefined, currentPassword: string, newPassword: string) => Promise<AuthOutcome>;
|
|
476
|
+
/**
|
|
477
|
+
* Close the account.
|
|
478
|
+
*
|
|
479
|
+
* Confirmed with the password when there is one — this is irreversible, and a borrowed session should not be
|
|
480
|
+
* able to do it. An account with no password (one that signs in through a provider) cannot be asked, so the
|
|
481
|
+
* session alone has to be enough.
|
|
482
|
+
*/
|
|
483
|
+
deleteSelf: (actor: Actor | undefined, password?: string) => Promise<AuthOutcome>;
|
|
484
|
+
/** The devices this account is signed in on, with the one asking marked. */
|
|
485
|
+
listSessions: (actor: Actor | undefined) => Promise<AuthOutcome>;
|
|
486
|
+
/** End one of them. Scoped to the caller's own account, or a session id would be an IDOR. */
|
|
487
|
+
revokeSession: (actor: Actor | undefined, sessionId: number) => Promise<AuthOutcome>;
|
|
488
|
+
/** "Sign out everywhere else" — the thing to do from a device you still hold after losing one you do not. */
|
|
489
|
+
revokeOtherSessions: (actor: Actor | undefined) => Promise<AuthOutcome>;
|
|
490
|
+
admin: {
|
|
491
|
+
list: (actor: Actor | undefined, query: AccountQuery) => Promise<AuthOutcome>;
|
|
492
|
+
get: (actor: Actor | undefined, userId: number) => Promise<AuthOutcome>;
|
|
493
|
+
/**
|
|
494
|
+
* Suspend, block or restore an account — and **end its sessions when it stops being active**.
|
|
495
|
+
*
|
|
496
|
+
* That second half is the whole point. A ban that leaves the credential working is not a ban, it is a note
|
|
497
|
+
* in a database; the person stays signed in until their token happens to lapse.
|
|
498
|
+
*/
|
|
499
|
+
setStatus: (actor: Actor | undefined, userId: number, status: AccountStatus) => Promise<AuthOutcome>;
|
|
500
|
+
setRoles: (actor: Actor | undefined, userId: number, roles: unknown) => Promise<AuthOutcome>;
|
|
501
|
+
remove: (actor: Actor | undefined, userId: number) => Promise<AuthOutcome>;
|
|
502
|
+
/**
|
|
503
|
+
* A session AS somebody else, for support that has to see what they see.
|
|
504
|
+
*
|
|
505
|
+
* Three properties make it something a deployment can live with, and all three are deliberate:
|
|
506
|
+
*
|
|
507
|
+
* - **It says so.** The credential carries `act` (RFC 8693), so every request made with it can be told from
|
|
508
|
+
* one the account holder made — `Actor.impersonatedBy` is that claim, read back. A borrowed session that
|
|
509
|
+
* is indistinguishable from a real one turns an audit log into fiction.
|
|
510
|
+
* - **It is short and cannot renew.** Fifteen minutes by default and no refresh token, so it expires rather
|
|
511
|
+
* than becoming a permanent second key to somebody's account.
|
|
512
|
+
* - **It is off unless asked for**, behind its own permission — see `impersonationPermission`.
|
|
513
|
+
*
|
|
514
|
+
* The session is answered in the BODY and no cookie is written. Swapping the administrator's own session
|
|
515
|
+
* cookie for this one would sign them out of the account they administer from, and getting back would mean
|
|
516
|
+
* signing in again; whoever asked decides where to put a credential they were handed.
|
|
517
|
+
*
|
|
518
|
+
* One caveat a deployment has to know: a store that keeps a single session per account — no `session` table —
|
|
519
|
+
* will overwrite the subject's own session with this one, signing them out. Anything with per-session rows,
|
|
520
|
+
* which is what `createMysqlStore` builds, does not.
|
|
521
|
+
*/
|
|
522
|
+
impersonate: (actor: Actor | undefined, userId: number, carrier?: CredentialCarrier) => Promise<AuthOutcome>;
|
|
523
|
+
};
|
|
524
|
+
/**
|
|
525
|
+
* A CSRF token for whoever is asking, bound to their session when they have one. Answered as a body AND as the
|
|
526
|
+
* cookie the binding writes, so a page can take whichever it finds easier to read.
|
|
527
|
+
*/
|
|
528
|
+
issueCsrf: (sessionToken?: string) => AuthOutcome;
|
|
144
529
|
/** Exposed so a deployment can mint a session outside the flows above — a rendered page's login form. */
|
|
145
|
-
issueSession: (userId: number
|
|
530
|
+
issueSession: (userId: number, context?: SessionContext, options?: {
|
|
531
|
+
actingAs?: number;
|
|
532
|
+
ttlSeconds?: number;
|
|
533
|
+
renewable?: boolean;
|
|
534
|
+
}) => Promise<SSRSession>;
|
|
146
535
|
resolveActor: (carrier: CredentialCarrier) => Promise<import('./identity').ActorResult>;
|
|
147
536
|
};
|
|
148
537
|
export type AuthApi = ReturnType<typeof createAuthApi>;
|