@rebasepro/server 0.13.1-canary.gef9608c → 0.14.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/dist/{admin_block-EmIHae6X.js → admin_block-H86dCPsj.js} +61 -2
- package/dist/admin_block-H86dCPsj.js.map +1 -0
- package/dist/api/ast-schema-editor.d.ts +74 -6
- package/dist/api/errors.d.ts +12 -0
- package/dist/api/rest/api-generator.d.ts +26 -1
- package/dist/api/rest/idempotency.d.ts +25 -4
- package/dist/api/rest/query-parser.d.ts +15 -1
- package/dist/api/rest/write-validation.d.ts +40 -4
- package/dist/auth/adapter-middleware.d.ts +3 -1
- package/dist/auth/admin-roles.d.ts +24 -0
- package/dist/auth/apple-oauth.d.ts +16 -11
- package/dist/auth/bitbucket-oauth.d.ts +2 -4
- package/dist/auth/builtin-auth-adapter.d.ts +11 -0
- package/dist/auth/discord-oauth.d.ts +5 -6
- package/dist/auth/facebook-oauth.d.ts +2 -4
- package/dist/auth/github-oauth.d.ts +2 -4
- package/dist/auth/gitlab-oauth.d.ts +6 -4
- package/dist/auth/google-oauth.d.ts +1 -0
- package/dist/auth/index.d.ts +7 -0
- package/dist/auth/interfaces.d.ts +81 -1
- package/dist/auth/jwt.d.ts +40 -0
- package/dist/auth/linkedin-oauth.d.ts +2 -4
- package/dist/auth/mfa-crypto.d.ts +54 -3
- package/dist/auth/mfa-gate.d.ts +42 -0
- package/dist/auth/mfa-routes.d.ts +26 -1
- package/dist/auth/mfa.d.ts +16 -0
- package/dist/auth/microsoft-oauth.d.ts +19 -4
- package/dist/auth/middleware.d.ts +26 -0
- package/dist/auth/oauth-code-flow.d.ts +66 -0
- package/dist/auth/oauth-signin-policy.d.ts +61 -0
- package/dist/auth/oidc-id-token.d.ts +61 -0
- package/dist/auth/rate-limiter.d.ts +62 -5
- package/dist/auth/registration-policy.d.ts +31 -0
- package/dist/auth/rls-scope.d.ts +25 -0
- package/dist/auth/routes.d.ts +15 -0
- package/dist/auth/slack-oauth.d.ts +2 -4
- package/dist/auth/spotify-oauth.d.ts +2 -4
- package/dist/auth/token-revocation.d.ts +38 -0
- package/dist/auth/twitter-oauth.d.ts +2 -5
- package/dist/{auth-DU4ShERo.js → auth-CYoPVf-E.js} +1985 -395
- package/dist/auth-CYoPVf-E.js.map +1 -0
- package/dist/{backup-BJiY_mM-.js → backup-C6ljYVTp.js} +2 -2
- package/dist/{backup-BJiY_mM-.js.map → backup-C6ljYVTp.js.map} +1 -1
- package/dist/boot/ddl-bootstrap.d.ts +70 -0
- package/dist/{contract-routes-Dj8i5AiM.js → contract-routes-Bet-eCNJ.js} +3 -3
- package/dist/{contract-routes-Dj8i5AiM.js.map → contract-routes-Bet-eCNJ.js.map} +1 -1
- package/dist/cron/cron-loader.d.ts +26 -1
- package/dist/cron/cron-routes.d.ts +1 -1
- package/dist/cron/cron-scheduler.d.ts +13 -3
- package/dist/cron/define-cron.d.ts +17 -3
- package/dist/cron/index.d.ts +2 -2
- package/dist/{cron-loader-B1S2MCSl.js → cron-loader-YhhQeVBM.js} +43 -8
- package/dist/cron-loader-YhhQeVBM.js.map +1 -0
- package/dist/{cron-routes-D5a9v1HY.js → cron-routes-maM_RlUu.js} +10 -4
- package/dist/cron-routes-maM_RlUu.js.map +1 -0
- package/dist/{cron-scheduler-BhFZWR0T.js → cron-scheduler-DIpYBmZP.js} +20 -8
- package/dist/cron-scheduler-DIpYBmZP.js.map +1 -0
- package/dist/{cron-store-Cu4sOseb.js → cron-store-Dvr4Y1sZ.js} +60 -51
- package/dist/cron-store-Dvr4Y1sZ.js.map +1 -0
- package/dist/ddl-bootstrap-BhXbTnBl.js +183 -0
- package/dist/ddl-bootstrap-BhXbTnBl.js.map +1 -0
- package/dist/email/html.d.ts +54 -0
- package/dist/email/index.d.ts +3 -0
- package/dist/email/link-base.d.ts +39 -0
- package/dist/email/smtp-email-service.d.ts +7 -1
- package/dist/email/templates.d.ts +9 -1
- package/dist/email/types.d.ts +11 -1
- package/dist/env.d.ts +5 -0
- package/dist/{errors-BpudWAVU.js → errors-EBYiaJ2E.js} +14 -2
- package/dist/errors-EBYiaJ2E.js.map +1 -0
- package/dist/function-loader-DDS1v7YX.js +139 -0
- package/dist/function-loader-DDS1v7YX.js.map +1 -0
- package/dist/{function-routes-C0cLIy3N.js → function-routes-Btcez1T-.js} +18 -6
- package/dist/function-routes-Btcez1T-.js.map +1 -0
- package/dist/functions/define-function.d.ts +25 -6
- package/dist/functions/function-loader.d.ts +23 -0
- package/dist/functions/function-routes.d.ts +7 -1
- package/dist/functions/request-timeout.d.ts +34 -0
- package/dist/history/history-routes.d.ts +6 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.es.js +1893 -714
- package/dist/index.es.js.map +1 -1
- package/dist/init/docs.d.ts +10 -1
- package/dist/init/process-safety.d.ts +25 -0
- package/dist/init.d.ts +38 -0
- package/dist/{jwt-CzeviDcB.js → jwt-_IFqfTOg.js} +57 -9
- package/dist/jwt-_IFqfTOg.js.map +1 -0
- package/dist/logger-DfvF_8r-.js +190 -0
- package/dist/logger-DfvF_8r-.js.map +1 -0
- package/dist/{openapi-generator-D5xsfVJY.js → openapi-generator-DPKtUC9X.js} +326 -55
- package/dist/openapi-generator-DPKtUC9X.js.map +1 -0
- package/dist/request-timeout-RivJsME0.js +65 -0
- package/dist/request-timeout-RivJsME0.js.map +1 -0
- package/dist/schema-editor-routes-CRcS3ArS.js +437 -0
- package/dist/schema-editor-routes-CRcS3ArS.js.map +1 -0
- package/dist/serve-spa.d.ts +5 -4
- package/dist/services/outbound-url-guard.d.ts +53 -0
- package/dist/services/routed-realtime-service.d.ts +10 -2
- package/dist/services/webhook-service.d.ts +76 -1
- package/dist/singleton.d.ts +25 -8
- package/dist/{src-CKOZBpeJ.js → src-C7rkDGxA.js} +619 -70
- package/dist/src-C7rkDGxA.js.map +1 -0
- package/dist/{src-_qQ3RNCK.js → src-Cz9nMgUR.js} +93 -3
- package/dist/src-Cz9nMgUR.js.map +1 -0
- package/dist/storage/LocalStorageController.d.ts +12 -1
- package/dist/storage/image-transform.d.ts +57 -0
- package/dist/storage/keys.d.ts +98 -0
- package/dist/storage/routes.d.ts +11 -0
- package/dist/storage/tus-handler.d.ts +22 -2
- package/dist/utils/logger.d.ts +18 -0
- package/dist/utils/sql.d.ts +11 -6
- package/package.json +6 -5
- package/dist/admin_block-EmIHae6X.js.map +0 -1
- package/dist/auth-DU4ShERo.js.map +0 -1
- package/dist/backend-CIxN4FVm.js +0 -15
- package/dist/backend-CIxN4FVm.js.map +0 -1
- package/dist/cron-loader-B1S2MCSl.js.map +0 -1
- package/dist/cron-routes-D5a9v1HY.js.map +0 -1
- package/dist/cron-scheduler-BhFZWR0T.js.map +0 -1
- package/dist/cron-store-Cu4sOseb.js.map +0 -1
- package/dist/errors-BpudWAVU.js.map +0 -1
- package/dist/function-loader-B_1fYfUY.js +0 -86
- package/dist/function-loader-B_1fYfUY.js.map +0 -1
- package/dist/function-routes-C0cLIy3N.js.map +0 -1
- package/dist/jwt-CzeviDcB.js.map +0 -1
- package/dist/logger-BYU66ENZ.js +0 -94
- package/dist/logger-BYU66ENZ.js.map +0 -1
- package/dist/openapi-generator-D5xsfVJY.js.map +0 -1
- package/dist/schema-editor-routes-C1DxDnDR.js +0 -248
- package/dist/schema-editor-routes-C1DxDnDR.js.map +0 -1
- package/dist/src-CKOZBpeJ.js.map +0 -1
- package/dist/src-_qQ3RNCK.js.map +0 -1
|
@@ -32,9 +32,23 @@ interface RateLimiterOptions {
|
|
|
32
32
|
* appends the address it saw to `X-Forwarded-For`, so the real client IP is
|
|
33
33
|
* the Nth entry from the right. Anything further left is client-supplied and
|
|
34
34
|
* ignored — which is what stops a caller spoofing `X-Forwarded-For` to
|
|
35
|
-
* rotate rate-limit keys.
|
|
36
|
-
*
|
|
37
|
-
*
|
|
35
|
+
* rotate rate-limit keys.
|
|
36
|
+
*
|
|
37
|
+
* **Defaults to `0`: no proxy is trusted, and `X-Forwarded-For` and
|
|
38
|
+
* `X-Real-IP` are both ignored in favour of the socket address.** A server
|
|
39
|
+
* cannot tell from a request whether a proxy put that header there or the
|
|
40
|
+
* caller did, so the number of hops is a deployment fact that has to be
|
|
41
|
+
* declared. It previously defaulted to `1`, and nothing in the repo ever
|
|
42
|
+
* set it — so on any directly-exposed server (`rebase dev`, a self-hosted
|
|
43
|
+
* `rebase-server`, a passthrough load balancer) one header gave every
|
|
44
|
+
* IP-keyed limiter an effective limit of one request per attacker-chosen
|
|
45
|
+
* value.
|
|
46
|
+
*
|
|
47
|
+
* Set it — via this option or `TRUSTED_PROXY_HOPS` — to the number of
|
|
48
|
+
* proxies you actually run. Behind one ingress or load balancer that is
|
|
49
|
+
* `1`. Leaving it unset behind a proxy makes every client share the
|
|
50
|
+
* proxy's address as one bucket, which the boot-time warning below tells
|
|
51
|
+
* you about the first time it happens.
|
|
38
52
|
*/
|
|
39
53
|
trustedProxyHops?: number;
|
|
40
54
|
}
|
|
@@ -54,6 +68,26 @@ export declare const defaultAuthLimiter: MiddlewareHandler<HonoEnv>;
|
|
|
54
68
|
* 50 requests per 15 minutes per IP.
|
|
55
69
|
*/
|
|
56
70
|
export declare const strictAuthLimiter: MiddlewareHandler<HonoEnv>;
|
|
71
|
+
/**
|
|
72
|
+
* Limiter for `POST /auth/send-verification`, keyed by the authenticated user.
|
|
73
|
+
*
|
|
74
|
+
* That route had no limiter at all while every one of its email-sending
|
|
75
|
+
* siblings had one, and being authenticated is not the protection it looks
|
|
76
|
+
* like: registration does not verify the address it is given, so an attacker
|
|
77
|
+
* registers a victim's address, signs in to the account they just made, and
|
|
78
|
+
* loops the route. Each call mints a token and mails the victim.
|
|
79
|
+
*
|
|
80
|
+
* An IP limiter cannot express what is wanted here — the recipient is the
|
|
81
|
+
* quantity being protected, not the caller — so this one keys on the uid, which
|
|
82
|
+
* on this route is one-to-one with the recipient address (emails are unique per
|
|
83
|
+
* account). `strictAuthLimiter` still runs in front of it to bound the caller by
|
|
84
|
+
* IP; this bounds what any single address can be sent.
|
|
85
|
+
*
|
|
86
|
+
* Five per 15 minutes is generous for "I didn't get the email, resend it" and
|
|
87
|
+
* useless as a mail bomb. Unauthenticated requests fall back to the IP bucket so
|
|
88
|
+
* the limiter is never a no-op if it is ever mounted before the auth middleware.
|
|
89
|
+
*/
|
|
90
|
+
export declare const verificationEmailLimiter: MiddlewareHandler<HonoEnv>;
|
|
57
91
|
/**
|
|
58
92
|
* Key generator for API-key-based rate limiting.
|
|
59
93
|
*
|
|
@@ -66,6 +100,14 @@ export declare function apiKeyKeyGenerator(c: Parameters<MiddlewareHandler<HonoE
|
|
|
66
100
|
export interface DataRateLimitConfig {
|
|
67
101
|
/** Turn the whole thing off — for a deployment whose proxy already does it. */
|
|
68
102
|
enabled?: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Trusted reverse-proxy hops, for the IP bucket. Same meaning and same
|
|
105
|
+
* fail-safe default of 0 as {@link RateLimiterOptions.trustedProxyHops} —
|
|
106
|
+
* threaded explicitly because this limiter builds its own key generator,
|
|
107
|
+
* and before this existed the option was accepted by the surrounding
|
|
108
|
+
* limiter and then ignored for the one bucket that keys on an address.
|
|
109
|
+
*/
|
|
110
|
+
trustedProxyHops?: number;
|
|
69
111
|
windowMs?: number;
|
|
70
112
|
/** Fallback for an API key with no `rate_limit` of its own. Default 1000. */
|
|
71
113
|
apiKey?: number;
|
|
@@ -73,13 +115,28 @@ export interface DataRateLimitConfig {
|
|
|
73
115
|
user?: number;
|
|
74
116
|
/**
|
|
75
117
|
* Per IP, for requests with no principal at all. Default 300.
|
|
76
|
-
* `null` disables the anonymous bucket entirely
|
|
77
|
-
* anonymous traffic must not be throttled (public webhook functions).
|
|
118
|
+
* `null` disables the anonymous bucket entirely.
|
|
78
119
|
*/
|
|
79
120
|
anonymous?: number | null;
|
|
121
|
+
/**
|
|
122
|
+
* Per IP, for anonymous requests to `/api/functions/*`. Default 3000 —
|
|
123
|
+
* deliberately far looser than {@link anonymous}, because the functions
|
|
124
|
+
* router is public by default for webhook receivers (Stripe, GitHub) whose
|
|
125
|
+
* bursts arrive from a handful of provider IPs and would trip the data
|
|
126
|
+
* API's ceiling.
|
|
127
|
+
*
|
|
128
|
+
* The looseness bounds the *value*, not the existence of the limit: this
|
|
129
|
+
* router is the one that invites anonymous callers, so it is the last one
|
|
130
|
+
* that should have no ceiling at all. `null` disables it — an explicit
|
|
131
|
+
* choice, which is what the hardcoded `anonymous: null` here used to be
|
|
132
|
+
* without any way to say otherwise.
|
|
133
|
+
*/
|
|
134
|
+
anonymousFunctions?: number | null;
|
|
80
135
|
/** Share counts across replicas. Defaults to this process's memory. */
|
|
81
136
|
store?: RateLimitStore;
|
|
82
137
|
}
|
|
138
|
+
/** @see DataRateLimitConfig.anonymousFunctions */
|
|
139
|
+
export declare const DEFAULT_FUNCTIONS_ANONYMOUS_LIMIT = 3000;
|
|
83
140
|
/**
|
|
84
141
|
* Rate limiting for the data API.
|
|
85
142
|
*
|
|
@@ -55,3 +55,34 @@ export declare function isRegistrationOpen(policy: RegistrationPolicy): boolean;
|
|
|
55
55
|
* on whether the table is empty, so now go and look".
|
|
56
56
|
*/
|
|
57
57
|
export declare function isSteadyStateRegistrationOpen(policy: Omit<RegistrationPolicy, "needsSetup">): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Whether `POST /auth/anonymous` may mint a user.
|
|
60
|
+
*
|
|
61
|
+
* Anonymous sign-in *is* registration: it inserts a `users` row and assigns
|
|
62
|
+
* `defaultRole` exactly as `POST /auth/register` does. It simply never asked.
|
|
63
|
+
* Both anonymous routes were mounted unconditionally and consulted none of the
|
|
64
|
+
* gates above, so a backend with `allowRegistration: false` **and**
|
|
65
|
+
* `disableSelfRegistration: true` — the documented hard kill switch — still
|
|
66
|
+
* handed a permanent account to any caller in two unauthenticated requests:
|
|
67
|
+
* `POST /auth/anonymous` for the row and the session, then
|
|
68
|
+
* `POST /auth/anonymous/link` to put an email and password on it.
|
|
69
|
+
*
|
|
70
|
+
* Two terms, because the two questions are genuinely different:
|
|
71
|
+
*
|
|
72
|
+
* - `allowAnonymous` is the feature switch, and it is **opt-in**. There was no
|
|
73
|
+
* key at all before, so a deployment could not turn anonymous auth off even
|
|
74
|
+
* after finding it on; defaulting it to `true` here would keep that hole
|
|
75
|
+
* open for everyone who never learns the key exists.
|
|
76
|
+
* - `disableSelfRegistration` still wins over it. Its docstring calls it a
|
|
77
|
+
* hard kill switch that blocks "self-registration outright", and an account
|
|
78
|
+
* created without credentials is still an account created by the public.
|
|
79
|
+
* An operator who sets both gets the kill switch they asked for.
|
|
80
|
+
*
|
|
81
|
+
* `allowRegistration` deliberately does **not** gate this. A public read-mostly
|
|
82
|
+
* app that wants anonymous sessions but no sign-up forms is a real deployment,
|
|
83
|
+
* and `allowAnonymous: true` says so precisely.
|
|
84
|
+
*/
|
|
85
|
+
export declare function isAnonymousAuthOpen(policy: {
|
|
86
|
+
allowAnonymous?: boolean;
|
|
87
|
+
disableSelfRegistration?: boolean;
|
|
88
|
+
}): boolean;
|
package/dist/auth/rls-scope.d.ts
CHANGED
|
@@ -12,6 +12,31 @@
|
|
|
12
12
|
* @module
|
|
13
13
|
*/
|
|
14
14
|
import type { DataDriver } from "@rebasepro/types";
|
|
15
|
+
/**
|
|
16
|
+
* The identity the trusted server plane runs as: `rebase.dataAsAdmin`, and any
|
|
17
|
+
* caller presenting the service key.
|
|
18
|
+
*
|
|
19
|
+
* Naming it makes the privilege legible, because it is narrower than it reads.
|
|
20
|
+
* This is **not** an RLS bypass — a driver scoped with it is an
|
|
21
|
+
* `AuthenticatedPostgresBackendDriver`, so every statement runs in a
|
|
22
|
+
* transaction that has done `SET LOCAL ROLE rebase_user` with
|
|
23
|
+
* `app.uid = 'service'`, and policies are evaluated. Two consequences worth
|
|
24
|
+
* knowing before you write a policy:
|
|
25
|
+
*
|
|
26
|
+
* - It passes the default policies through their `rolesOverlap(['admin'])`
|
|
27
|
+
* arm — the same arm an application user holding the `admin` role passes.
|
|
28
|
+
* - `policy.serverContext()` compiles to `auth.uid() IS NULL` and is therefore
|
|
29
|
+
* **false** for it. A collection with `disableDefaultPolicies: true` whose
|
|
30
|
+
* only rule is `serverContext()` denies these writes (42501) and returns
|
|
31
|
+
* zero rows for these reads.
|
|
32
|
+
*
|
|
33
|
+
* The true bypass is `rebase.sql()`, which runs on the owner connection and
|
|
34
|
+
* never goes near `withAuth`.
|
|
35
|
+
*/
|
|
36
|
+
export declare const SERVICE_IDENTITY: {
|
|
37
|
+
uid: string;
|
|
38
|
+
roles: string[];
|
|
39
|
+
};
|
|
15
40
|
/**
|
|
16
41
|
* Scope a DataDriver via `withAuth()` for RLS.
|
|
17
42
|
*
|
package/dist/auth/routes.d.ts
CHANGED
|
@@ -18,8 +18,23 @@ export interface AuthModuleConfig {
|
|
|
18
18
|
defaultRole?: string;
|
|
19
19
|
/** Optional array of OAuth providers */
|
|
20
20
|
oauthProviders?: OAuthProvider<unknown>[];
|
|
21
|
+
/**
|
|
22
|
+
* Redirect URIs the OAuth routes will accept, for every provider.
|
|
23
|
+
*
|
|
24
|
+
* Left unset, the only check is the provider's own registered-URI match,
|
|
25
|
+
* which authorises every URI registered on that OAuth client. Compared on
|
|
26
|
+
* origin plus path; query and fragment are ignored, as is a trailing slash.
|
|
27
|
+
*/
|
|
28
|
+
allowedRedirectUris?: string[];
|
|
21
29
|
/** When true, blocks all self-registration regardless of `allowRegistration`. */
|
|
22
30
|
disableSelfRegistration?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Opt-in: allow `POST /auth/anonymous` to mint a user without credentials.
|
|
33
|
+
*
|
|
34
|
+
* Off by default. See {@link isAnonymousAuthOpen} for why this is opt-in
|
|
35
|
+
* rather than opt-out, and why `disableSelfRegistration` overrides it.
|
|
36
|
+
*/
|
|
37
|
+
allowAnonymous?: boolean;
|
|
23
38
|
/**
|
|
24
39
|
* Auth hooks for customizing password hashing, credential
|
|
25
40
|
* verification, lifecycle hooks, etc.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { OAuthProvider } from "./interfaces";
|
|
2
|
+
import { type OAuthCodeFlowPayload } from "./oauth-code-flow";
|
|
2
3
|
/**
|
|
3
4
|
* Creates a Slack OAuth Provider integration (OAuth 2.0 / "Sign in with Slack").
|
|
4
5
|
* Uses the OpenID Connect flow with the "openid,email,profile" scopes.
|
|
@@ -6,7 +7,4 @@ import type { OAuthProvider } from "./interfaces";
|
|
|
6
7
|
export declare function createSlackProvider(config: {
|
|
7
8
|
clientId: string;
|
|
8
9
|
clientSecret: string;
|
|
9
|
-
}): OAuthProvider<
|
|
10
|
-
code: string;
|
|
11
|
-
redirectUri: string;
|
|
12
|
-
}>;
|
|
10
|
+
}): OAuthProvider<OAuthCodeFlowPayload>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { OAuthProvider } from "./interfaces";
|
|
2
|
+
import { type OAuthCodeFlowPayload } from "./oauth-code-flow";
|
|
2
3
|
/**
|
|
3
4
|
* Creates a Spotify OAuth Provider integration.
|
|
4
5
|
* Uses the authorization code flow with the "user-read-email" scope.
|
|
@@ -6,7 +7,4 @@ import type { OAuthProvider } from "./interfaces";
|
|
|
6
7
|
export declare function createSpotifyProvider(config: {
|
|
7
8
|
clientId: string;
|
|
8
9
|
clientSecret: string;
|
|
9
|
-
}): OAuthProvider<
|
|
10
|
-
code: string;
|
|
11
|
-
redirectUri: string;
|
|
12
|
-
}>;
|
|
10
|
+
}): OAuthProvider<OAuthCodeFlowPayload>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { AuthRepository } from "./interfaces";
|
|
2
|
+
import type { AccessTokenPayload } from "./jwt";
|
|
3
|
+
/**
|
|
4
|
+
* Has this access token been revoked?
|
|
5
|
+
*
|
|
6
|
+
* `logout`, `change-password`, `reset-password` and `DELETE /auth/sessions` all
|
|
7
|
+
* stamp a `tokensValidAfter` watermark on the user. Every one of them also
|
|
8
|
+
* deletes refresh-token rows, which is what made the gap easy to miss: the
|
|
9
|
+
* session really is gone, and the *refresh* path really does check the
|
|
10
|
+
* watermark — so signing out looked like it worked.
|
|
11
|
+
*
|
|
12
|
+
* The access token was untouched. It is a bearer credential that nothing
|
|
13
|
+
* consulted a database about, so it stayed valid for its full lifetime after
|
|
14
|
+
* every one of those actions. "Sign out everywhere" left the stolen token
|
|
15
|
+
* working; so did changing your password because you thought it had leaked.
|
|
16
|
+
*
|
|
17
|
+
* The watermark had exactly one read in the repository, on refresh. This adds
|
|
18
|
+
* the other one.
|
|
19
|
+
*
|
|
20
|
+
* ## Cost
|
|
21
|
+
*
|
|
22
|
+
* One indexed lookup per request, on the paths that already make one. The
|
|
23
|
+
* adapter re-reads roles for every authenticated request; the admin routes now
|
|
24
|
+
* do too. Both call this with the row they were already fetching in mind — a
|
|
25
|
+
* repository that wants to serve both from one query is free to cache
|
|
26
|
+
* internally, but nothing here assumes it.
|
|
27
|
+
*
|
|
28
|
+
* ## Failure
|
|
29
|
+
*
|
|
30
|
+
* Fails **open**, deliberately, and this is the one place in the auth stack
|
|
31
|
+
* where that is right. The watermark is an extra revocation signal layered over
|
|
32
|
+
* a token that has already been verified — signature, expiry and purpose all
|
|
33
|
+
* checked. Refusing every request when the database is unreachable would turn a
|
|
34
|
+
* transient outage into a total sign-out of an entire deployment, and the
|
|
35
|
+
* attacker this protects against needs to have already stolen a live token. The
|
|
36
|
+
* failure is logged at warn so it is visible rather than silent.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isAccessTokenRevoked(authRepo: Pick<AuthRepository, "getTokensValidAfter">, payload: Pick<AccessTokenPayload, "uid" | "iat">): Promise<boolean>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { OAuthProvider } from "./interfaces";
|
|
2
|
+
import { type OAuthCodeFlowPayload } from "./oauth-code-flow";
|
|
2
3
|
/**
|
|
3
4
|
* Creates a Twitter/X OAuth 2.0 Provider integration.
|
|
4
5
|
*
|
|
@@ -11,8 +12,4 @@ import type { OAuthProvider } from "./interfaces";
|
|
|
11
12
|
export declare function createTwitterProvider(config: {
|
|
12
13
|
clientId: string;
|
|
13
14
|
clientSecret: string;
|
|
14
|
-
}): OAuthProvider<
|
|
15
|
-
code: string;
|
|
16
|
-
redirectUri: string;
|
|
17
|
-
codeVerifier: string;
|
|
18
|
-
}>;
|
|
15
|
+
}): OAuthProvider<OAuthCodeFlowPayload>;
|