@scalemule/nextjs 0.0.39 → 0.0.41
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/client.d.mts +5 -1
- package/dist/client.d.ts +5 -1
- package/dist/{index-BoENfro3.d.mts → index-BIIUrnPr.d.mts} +54 -1
- package/dist/{index-BoENfro3.d.ts → index-BIIUrnPr.d.ts} +54 -1
- package/dist/index.d.mts +15 -4
- package/dist/index.d.ts +15 -4
- package/dist/index.js +176 -89
- package/dist/index.mjs +168 -90
- package/dist/server/auth.js +275 -6
- package/dist/server/auth.mjs +275 -6
- package/dist/server/index.d.mts +252 -4
- package/dist/server/index.d.ts +252 -4
- package/dist/server/index.js +403 -6
- package/dist/server/index.mjs +393 -7
- package/dist/server/webhook-handler.d.mts +3 -2
- package/dist/server/webhook-handler.d.ts +3 -2
- package/dist/server/webhook-handler.js +3 -0
- package/dist/server/webhook-handler.mjs +3 -0
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/{webhook-handler-CjSSCdvI.d.ts → webhook-handler-BjFbqZuq.d.ts} +4 -1
- package/dist/{webhook-handler-Cz9jtet2.d.mts → webhook-handler-BqzCYRNJ.d.mts} +4 -1
- package/package.json +3 -2
package/dist/server/index.d.mts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { S as ServerConfig } from '../webhook-handler-
|
|
2
|
-
export { a as ScaleMuleServer, V as VideoFailedEvent, b as VideoReadyEvent, c as VideoTranscodedEvent, d as VideoUploadedEvent, W as WebhookEvent, e as WebhookRoutesConfig, f as createServerClient, g as createWebhookHandler, h as createWebhookRoutes, p as parseWebhookEvent, r as registerVideoWebhook, i as resolveGatewayUrl, v as verifyWebhookSignature } from '../webhook-handler-
|
|
3
|
-
import { p as ClientContext, A as ApiError } from '../index-
|
|
1
|
+
import { S as ServerConfig } from '../webhook-handler-BqzCYRNJ.mjs';
|
|
2
|
+
export { a as ScaleMuleServer, V as VideoFailedEvent, b as VideoReadyEvent, c as VideoTranscodedEvent, d as VideoUploadedEvent, W as WebhookEvent, e as WebhookRoutesConfig, f as createServerClient, g as createWebhookHandler, h as createWebhookRoutes, p as parseWebhookEvent, r as registerVideoWebhook, i as resolveGatewayUrl, v as verifyWebhookSignature } from '../webhook-handler-BqzCYRNJ.mjs';
|
|
3
|
+
import { p as ClientContext, A as ApiError } from '../index-BIIUrnPr.mjs';
|
|
4
4
|
import { NextRequest, NextResponse } from 'next/server';
|
|
5
|
+
import '@scalemule/money';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Client Context Extraction Utilities (Next.js)
|
|
@@ -110,6 +111,12 @@ declare function buildFlagContext(clientContext: Pick<ClientContext, 'ip'> | und
|
|
|
110
111
|
*/
|
|
111
112
|
declare const SESSION_COOKIE_NAME = "sm_session";
|
|
112
113
|
declare const USER_ID_COOKIE_NAME = "sm_user_id";
|
|
114
|
+
/**
|
|
115
|
+
* Known accounts cookie — stores display metadata (email, name, avatar) for
|
|
116
|
+
* accounts that have logged in on this device. NOT httpOnly so client JS can
|
|
117
|
+
* read it to render the account switcher UI. Contains NO tokens or secrets.
|
|
118
|
+
*/
|
|
119
|
+
declare const KNOWN_ACCOUNTS_COOKIE_NAME = "sm_known_accounts";
|
|
113
120
|
interface SessionCookieOptions {
|
|
114
121
|
/** Cookie max age in seconds (default: 7 days) */
|
|
115
122
|
maxAge?: number;
|
|
@@ -181,6 +188,51 @@ declare function getSession(): Promise<SessionData | null>;
|
|
|
181
188
|
* Use this when you need to read cookies from a Request directly.
|
|
182
189
|
*/
|
|
183
190
|
declare function getSessionFromRequest(request: Request): SessionData | null;
|
|
191
|
+
/**
|
|
192
|
+
* Known account entry stored in cookie.
|
|
193
|
+
* Contains display metadata ONLY — no tokens, no secrets.
|
|
194
|
+
*/
|
|
195
|
+
interface KnownAccountEntry {
|
|
196
|
+
userId: string;
|
|
197
|
+
email?: string;
|
|
198
|
+
fullName?: string;
|
|
199
|
+
avatarUrl?: string;
|
|
200
|
+
provider?: string;
|
|
201
|
+
lastActiveAt: string;
|
|
202
|
+
displayLabel?: string;
|
|
203
|
+
colorIndex?: number;
|
|
204
|
+
}
|
|
205
|
+
type AccountSwitcherPrivacy = 'full' | 'masked' | 'minimal';
|
|
206
|
+
/**
|
|
207
|
+
* Add an account to the known accounts cookie.
|
|
208
|
+
* Called after successful login. The cookie is NOT httpOnly so client JS
|
|
209
|
+
* can read it to render the account switcher UI.
|
|
210
|
+
*
|
|
211
|
+
* Appends Set-Cookie headers to an existing Headers object.
|
|
212
|
+
*/
|
|
213
|
+
declare function appendKnownAccountCookie(headers: Headers, account: KnownAccountEntry, existingCookie: string | null, options?: SessionCookieOptions, privacy?: AccountSwitcherPrivacy): void;
|
|
214
|
+
/**
|
|
215
|
+
* Remove a specific account from the known accounts cookie.
|
|
216
|
+
*/
|
|
217
|
+
declare function removeKnownAccountFromCookie(headers: Headers, userId: string, existingCookie: string | null, options?: SessionCookieOptions): void;
|
|
218
|
+
/**
|
|
219
|
+
* Clear the known accounts cookie entirely.
|
|
220
|
+
*/
|
|
221
|
+
declare function clearKnownAccountsCookie(headers: Headers, options?: SessionCookieOptions): void;
|
|
222
|
+
/**
|
|
223
|
+
* Read known accounts from a Request's cookies.
|
|
224
|
+
*/
|
|
225
|
+
declare function getKnownAccountsFromRequest(request: Request): KnownAccountEntry[];
|
|
226
|
+
/**
|
|
227
|
+
* Read the raw known accounts cookie value from a Request.
|
|
228
|
+
*/
|
|
229
|
+
declare function getKnownAccountsCookieRaw(request: Request): string | null;
|
|
230
|
+
/**
|
|
231
|
+
* Normalize all entries in the known accounts cookie to the given privacy level.
|
|
232
|
+
* Returns a Set-Cookie header string if any entries changed, or null if nothing changed.
|
|
233
|
+
* Used on /me requests to migrate legacy full-PII cookies to the configured privacy level.
|
|
234
|
+
*/
|
|
235
|
+
declare function normalizeKnownAccountsCookie(request: Request, privacy: AccountSwitcherPrivacy | undefined, options?: SessionCookieOptions): string | null;
|
|
184
236
|
/**
|
|
185
237
|
* Require authentication - throws Response if not authenticated
|
|
186
238
|
*
|
|
@@ -220,6 +272,19 @@ interface AuthRoutesConfig {
|
|
|
220
272
|
cookies?: SessionCookieOptions;
|
|
221
273
|
/** Enable CSRF validation on state-changing requests (POST/DELETE/PATCH) */
|
|
222
274
|
csrf?: boolean;
|
|
275
|
+
/**
|
|
276
|
+
* Enable the account switcher — remembers which accounts have logged in on
|
|
277
|
+
* this device (metadata only, no tokens). Switching requires re-authentication.
|
|
278
|
+
* Adds routes: switch-account, forget-account, forget-all-accounts, known-accounts.
|
|
279
|
+
*/
|
|
280
|
+
enableAccountSwitcher?: boolean;
|
|
281
|
+
/**
|
|
282
|
+
* Privacy level for account switcher display metadata.
|
|
283
|
+
* - 'full': Store email, name, avatar as-is (default)
|
|
284
|
+
* - 'masked': Mask email, truncate name to initial
|
|
285
|
+
* - 'minimal': No PII — just a colored "Account" label
|
|
286
|
+
*/
|
|
287
|
+
accountSwitcherPrivacy?: 'full' | 'masked' | 'minimal';
|
|
223
288
|
/** Callbacks */
|
|
224
289
|
onLogin?: (user: {
|
|
225
290
|
id: string;
|
|
@@ -1011,4 +1076,187 @@ declare function invalidateBundleCache(key?: string): void;
|
|
|
1011
1076
|
*/
|
|
1012
1077
|
declare function prefetchBundles(keys: string[]): Promise<void>;
|
|
1013
1078
|
|
|
1014
|
-
|
|
1079
|
+
/**
|
|
1080
|
+
* Safe-redirect validation for authentication callback URLs.
|
|
1081
|
+
*
|
|
1082
|
+
* Prevents open-redirect attacks on `returnTo` / `callbackUrl` / `next`
|
|
1083
|
+
* params after login / registration / password-reset flows. An attacker
|
|
1084
|
+
* who can inject a `?returnTo=https://evil.example` into your login
|
|
1085
|
+
* form would otherwise redirect the freshly-authenticated user away
|
|
1086
|
+
* from your origin — a classic phishing + credential-harvesting setup.
|
|
1087
|
+
*
|
|
1088
|
+
* Safe by default — unknown input returns the configured default
|
|
1089
|
+
* path. Hosts opt in to external origins explicitly via
|
|
1090
|
+
* `allowedOrigins`.
|
|
1091
|
+
*
|
|
1092
|
+
* Framework-agnostic: no imports from `next/*`. Safe in any
|
|
1093
|
+
* Edge / Node / middleware runtime.
|
|
1094
|
+
*/
|
|
1095
|
+
interface SafeRedirectOptions {
|
|
1096
|
+
/**
|
|
1097
|
+
* Origins (schema + host + optional port) the redirect is allowed
|
|
1098
|
+
* to land on. Default `[]` — only same-origin relative paths are
|
|
1099
|
+
* allowed. Items are compared case-insensitively after normalizing
|
|
1100
|
+
* the scheme and host.
|
|
1101
|
+
*
|
|
1102
|
+
* Good practice: list only origins that your organization controls
|
|
1103
|
+
* and that are reachable over HTTPS.
|
|
1104
|
+
*
|
|
1105
|
+
* @example
|
|
1106
|
+
* ['https://app.example.com', 'https://admin.example.com']
|
|
1107
|
+
*/
|
|
1108
|
+
allowedOrigins?: string[];
|
|
1109
|
+
/**
|
|
1110
|
+
* Value returned when the input is missing, invalid, or points
|
|
1111
|
+
* outside the allowlist. Default `'/'`.
|
|
1112
|
+
*/
|
|
1113
|
+
defaultPath?: string;
|
|
1114
|
+
/**
|
|
1115
|
+
* Strip the scheme + host from same-origin absolute URLs and
|
|
1116
|
+
* return only the path + query + fragment. Default `true` — keeps
|
|
1117
|
+
* redirect targets as short relative URLs that route through
|
|
1118
|
+
* client-side routing cleanly.
|
|
1119
|
+
*/
|
|
1120
|
+
stripSameOriginHost?: boolean;
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* Returns a safe-to-use redirect path, falling back to
|
|
1124
|
+
* `opts.defaultPath` (default `'/'`) when the input fails validation.
|
|
1125
|
+
*
|
|
1126
|
+
* Accepts:
|
|
1127
|
+
* - empty / nullish → default
|
|
1128
|
+
* - relative paths like `/foo/bar?x=1#frag` (leading `/` required)
|
|
1129
|
+
* - absolute URLs whose origin is in `allowedOrigins`
|
|
1130
|
+
*
|
|
1131
|
+
* Rejects everything else, including:
|
|
1132
|
+
* - schema-relative URLs (`//evil.example`)
|
|
1133
|
+
* - backslash-prefixed paths
|
|
1134
|
+
* - `javascript:`, `data:`, `mailto:`, `vbscript:` etc.
|
|
1135
|
+
* - bare hostnames (`example.com/path`)
|
|
1136
|
+
* - malformed URLs
|
|
1137
|
+
*/
|
|
1138
|
+
declare function validateSafeRedirect(input: string | null | undefined, opts?: SafeRedirectOptions): string;
|
|
1139
|
+
/**
|
|
1140
|
+
* Boolean predicate form. Returns `true` when `input` would pass
|
|
1141
|
+
* `validateSafeRedirect` with the same options.
|
|
1142
|
+
*/
|
|
1143
|
+
declare function isSafeRedirect(input: string | null | undefined, opts?: SafeRedirectOptions): boolean;
|
|
1144
|
+
|
|
1145
|
+
/**
|
|
1146
|
+
* Security-headers helper for Next.js hosts.
|
|
1147
|
+
*
|
|
1148
|
+
* Returns a shape that drops directly into the `headers()` entry in
|
|
1149
|
+
* `next.config.ts` (or the `headers` field of a Route Handler
|
|
1150
|
+
* `NextResponse`). Reasonable defaults for the top-of-OWASP headers
|
|
1151
|
+
* — HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy,
|
|
1152
|
+
* X-XSS-Protection, Permissions-Policy — with knobs for the fields
|
|
1153
|
+
* that typically differ by app (HSTS duration, frame ancestors,
|
|
1154
|
+
* camera/microphone policies).
|
|
1155
|
+
*
|
|
1156
|
+
* Framework-agnostic: no imports from `next/*` so the function works
|
|
1157
|
+
* in Edge / Node / middleware and is trivially unit-testable.
|
|
1158
|
+
*/
|
|
1159
|
+
interface SecurityHeaderEntry {
|
|
1160
|
+
key: string;
|
|
1161
|
+
value: string;
|
|
1162
|
+
}
|
|
1163
|
+
interface BuildSecurityHeadersOptions {
|
|
1164
|
+
/**
|
|
1165
|
+
* HSTS max-age in seconds. Default `31536000` (1 year) — the value
|
|
1166
|
+
* required for inclusion on the HSTS preload list. Set to `0` (or
|
|
1167
|
+
* pass `includeHsts: false`) to opt out entirely.
|
|
1168
|
+
*/
|
|
1169
|
+
hstsMaxAgeSeconds?: number;
|
|
1170
|
+
/** Adds the `includeSubDomains` directive to HSTS. Default `true`. */
|
|
1171
|
+
hstsIncludeSubDomains?: boolean;
|
|
1172
|
+
/**
|
|
1173
|
+
* Adds the `preload` directive to HSTS. Default `false` — only set
|
|
1174
|
+
* to `true` after you've read https://hstspreload.org and are ready
|
|
1175
|
+
* to commit; reversing it requires months of calendar time.
|
|
1176
|
+
*/
|
|
1177
|
+
hstsPreload?: boolean;
|
|
1178
|
+
/** Emit the HSTS header at all. Default `true`. */
|
|
1179
|
+
includeHsts?: boolean;
|
|
1180
|
+
/**
|
|
1181
|
+
* Value for `X-Frame-Options`. Default `'DENY'`. Pass `'SAMEORIGIN'`
|
|
1182
|
+
* if your app is framed by its own subdomain. Set to `null` to
|
|
1183
|
+
* suppress (e.g. when you're already using `frame-ancestors` in
|
|
1184
|
+
* CSP). `SAMEORIGIN` accepts no host parameter; use CSP's
|
|
1185
|
+
* `frame-ancestors` for finer-grained framing policy.
|
|
1186
|
+
*/
|
|
1187
|
+
frameOptions?: 'DENY' | 'SAMEORIGIN' | null;
|
|
1188
|
+
/**
|
|
1189
|
+
* Value for `Referrer-Policy`. Default `'strict-origin-when-cross-origin'`
|
|
1190
|
+
* — same as Next.js's own default, balances analytics with privacy.
|
|
1191
|
+
*/
|
|
1192
|
+
referrerPolicy?: 'no-referrer' | 'no-referrer-when-downgrade' | 'origin' | 'origin-when-cross-origin' | 'same-origin' | 'strict-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url';
|
|
1193
|
+
/**
|
|
1194
|
+
* Permissions-Policy directives. Pass a map of `{ feature: allowlist }`
|
|
1195
|
+
* where the allowlist is either an array of origins (string[]) or
|
|
1196
|
+
* one of the well-known tokens `'self'` / `'*'` / `'()'` (deny).
|
|
1197
|
+
*
|
|
1198
|
+
* Default: camera + microphone allowed for `'self'` only (useful
|
|
1199
|
+
* for the conference track); geolocation + payment denied; every
|
|
1200
|
+
* other common feature left unset (browsers apply their own
|
|
1201
|
+
* conservative defaults).
|
|
1202
|
+
*
|
|
1203
|
+
* Pass `{}` to emit an empty `Permissions-Policy: ` header (rarely
|
|
1204
|
+
* useful). Pass `null` to suppress the header entirely.
|
|
1205
|
+
*/
|
|
1206
|
+
permissionsPolicy?: PermissionsPolicyMap | null;
|
|
1207
|
+
/**
|
|
1208
|
+
* Value for `X-Content-Type-Options`. Default `'nosniff'` — do not
|
|
1209
|
+
* override without a specific reason.
|
|
1210
|
+
*/
|
|
1211
|
+
contentTypeOptions?: string | null;
|
|
1212
|
+
/**
|
|
1213
|
+
* Legacy `X-XSS-Protection`. Default `'0'` (disables the
|
|
1214
|
+
* deprecated IE/Edge filter which had known bypass issues —
|
|
1215
|
+
* recommended by OWASP + Next.js). Set to `'1; mode=block'` only
|
|
1216
|
+
* if you have a specific compliance reason.
|
|
1217
|
+
*/
|
|
1218
|
+
xssProtection?: string | null;
|
|
1219
|
+
/**
|
|
1220
|
+
* Extra headers to merge into the result. Takes precedence over
|
|
1221
|
+
* the computed ones — useful for adding Content-Security-Policy or
|
|
1222
|
+
* Cross-Origin-* headers without forcing a new option for each.
|
|
1223
|
+
*/
|
|
1224
|
+
extraHeaders?: SecurityHeaderEntry[];
|
|
1225
|
+
}
|
|
1226
|
+
type PermissionsPolicyValue = readonly string[] | 'self' | '*' | '()';
|
|
1227
|
+
type PermissionsPolicyMap = Readonly<Record<string, PermissionsPolicyValue>>;
|
|
1228
|
+
declare const DEFAULT_PERMISSIONS_POLICY: PermissionsPolicyMap;
|
|
1229
|
+
/**
|
|
1230
|
+
* Build a reasonable-default set of security response headers in the
|
|
1231
|
+
* exact shape Next.js `headers()` expects.
|
|
1232
|
+
*
|
|
1233
|
+
* @example next.config.ts
|
|
1234
|
+
* ```ts
|
|
1235
|
+
* import { buildSecurityHeaders } from '@scalemule/nextjs/server'
|
|
1236
|
+
*
|
|
1237
|
+
* export default {
|
|
1238
|
+
* async headers() {
|
|
1239
|
+
* return [
|
|
1240
|
+
* { source: '/:path*', headers: buildSecurityHeaders() },
|
|
1241
|
+
* ]
|
|
1242
|
+
* },
|
|
1243
|
+
* }
|
|
1244
|
+
* ```
|
|
1245
|
+
*
|
|
1246
|
+
* @example custom overrides
|
|
1247
|
+
* ```ts
|
|
1248
|
+
* buildSecurityHeaders({
|
|
1249
|
+
* frameOptions: 'SAMEORIGIN',
|
|
1250
|
+
* permissionsPolicy: {
|
|
1251
|
+
* camera: ['https://trusted.example.com'],
|
|
1252
|
+
* microphone: ['https://trusted.example.com'],
|
|
1253
|
+
* },
|
|
1254
|
+
* extraHeaders: [
|
|
1255
|
+
* { key: 'Content-Security-Policy', value: "default-src 'self'" },
|
|
1256
|
+
* ],
|
|
1257
|
+
* })
|
|
1258
|
+
* ```
|
|
1259
|
+
*/
|
|
1260
|
+
declare function buildSecurityHeaders(opts?: BuildSecurityHeadersOptions): SecurityHeaderEntry[];
|
|
1261
|
+
|
|
1262
|
+
export { type AnalyticsRoutesConfig, type AnalyticsTrackingGateConfig, type AuthMiddlewareConfig, type AuthRoutesConfig, type BuildSecurityHeadersOptions, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, DEFAULT_PERMISSIONS_POLICY, type HandlerContext, type HandlerOptions, KNOWN_ACCOUNTS_COOKIE_NAME, type KnownAccountEntry, type MySqlBundle, type NotificationRoutesConfig, OAUTH_STATE_COOKIE_NAME, type OAuthBundle, type PermissionsPolicyMap, type PermissionsPolicyValue, type PostgresBundle, type PushRoutesConfig, type RedisBundle, type S3Bundle, SESSION_COOKIE_NAME, type SafeRedirectOptions, ScaleMuleError, type SecurityHeaderEntry, ServerConfig, type SessionCookieOptions, type SessionData, type SmtpBundle, USER_ID_COOKIE_NAME, apiHandler, appendKnownAccountCookie, buildClientContextHeaders, buildFlagContext, buildSecurityHeaders, clearKnownAccountsCookie, clearOAuthState, clearSession, configureBundles, configureSecrets, createAnalyticsRoutes, createAuthMiddleware, createAuthRoutes, createNotificationRoutes, createPushRoutes, errorCodeToStatus, extractClientContext, extractClientContextFromReq, generateCSRFToken, getAppSecret, getAppSecretOrDefault, getBootstrapFlags, getBundle, getCSRFToken, getKnownAccountsCookieRaw, getKnownAccountsFromRequest, getMySqlBundle, getOAuthBundle, getPostgresBundle, getRedisBundle, getS3Bundle, getSession, getSessionFromRequest, getSmtpBundle, invalidateBundleCache, invalidateSecretCache, isSafeRedirect, normalizeKnownAccountsCookie, prefetchBundles, prefetchSecrets, removeKnownAccountFromCookie, requireAppSecret, requireBundle, requireSession, setOAuthState, unwrap, validateCSRFToken, validateCSRFTokenAsync, validateOAuthState, validateOAuthStateAsync, validateSafeRedirect, withAuth, withCSRFProtection, withCSRFToken, withSession };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { S as ServerConfig } from '../webhook-handler-
|
|
2
|
-
export { a as ScaleMuleServer, V as VideoFailedEvent, b as VideoReadyEvent, c as VideoTranscodedEvent, d as VideoUploadedEvent, W as WebhookEvent, e as WebhookRoutesConfig, f as createServerClient, g as createWebhookHandler, h as createWebhookRoutes, p as parseWebhookEvent, r as registerVideoWebhook, i as resolveGatewayUrl, v as verifyWebhookSignature } from '../webhook-handler-
|
|
3
|
-
import { p as ClientContext, A as ApiError } from '../index-
|
|
1
|
+
import { S as ServerConfig } from '../webhook-handler-BjFbqZuq.js';
|
|
2
|
+
export { a as ScaleMuleServer, V as VideoFailedEvent, b as VideoReadyEvent, c as VideoTranscodedEvent, d as VideoUploadedEvent, W as WebhookEvent, e as WebhookRoutesConfig, f as createServerClient, g as createWebhookHandler, h as createWebhookRoutes, p as parseWebhookEvent, r as registerVideoWebhook, i as resolveGatewayUrl, v as verifyWebhookSignature } from '../webhook-handler-BjFbqZuq.js';
|
|
3
|
+
import { p as ClientContext, A as ApiError } from '../index-BIIUrnPr.js';
|
|
4
4
|
import { NextRequest, NextResponse } from 'next/server';
|
|
5
|
+
import '@scalemule/money';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Client Context Extraction Utilities (Next.js)
|
|
@@ -110,6 +111,12 @@ declare function buildFlagContext(clientContext: Pick<ClientContext, 'ip'> | und
|
|
|
110
111
|
*/
|
|
111
112
|
declare const SESSION_COOKIE_NAME = "sm_session";
|
|
112
113
|
declare const USER_ID_COOKIE_NAME = "sm_user_id";
|
|
114
|
+
/**
|
|
115
|
+
* Known accounts cookie — stores display metadata (email, name, avatar) for
|
|
116
|
+
* accounts that have logged in on this device. NOT httpOnly so client JS can
|
|
117
|
+
* read it to render the account switcher UI. Contains NO tokens or secrets.
|
|
118
|
+
*/
|
|
119
|
+
declare const KNOWN_ACCOUNTS_COOKIE_NAME = "sm_known_accounts";
|
|
113
120
|
interface SessionCookieOptions {
|
|
114
121
|
/** Cookie max age in seconds (default: 7 days) */
|
|
115
122
|
maxAge?: number;
|
|
@@ -181,6 +188,51 @@ declare function getSession(): Promise<SessionData | null>;
|
|
|
181
188
|
* Use this when you need to read cookies from a Request directly.
|
|
182
189
|
*/
|
|
183
190
|
declare function getSessionFromRequest(request: Request): SessionData | null;
|
|
191
|
+
/**
|
|
192
|
+
* Known account entry stored in cookie.
|
|
193
|
+
* Contains display metadata ONLY — no tokens, no secrets.
|
|
194
|
+
*/
|
|
195
|
+
interface KnownAccountEntry {
|
|
196
|
+
userId: string;
|
|
197
|
+
email?: string;
|
|
198
|
+
fullName?: string;
|
|
199
|
+
avatarUrl?: string;
|
|
200
|
+
provider?: string;
|
|
201
|
+
lastActiveAt: string;
|
|
202
|
+
displayLabel?: string;
|
|
203
|
+
colorIndex?: number;
|
|
204
|
+
}
|
|
205
|
+
type AccountSwitcherPrivacy = 'full' | 'masked' | 'minimal';
|
|
206
|
+
/**
|
|
207
|
+
* Add an account to the known accounts cookie.
|
|
208
|
+
* Called after successful login. The cookie is NOT httpOnly so client JS
|
|
209
|
+
* can read it to render the account switcher UI.
|
|
210
|
+
*
|
|
211
|
+
* Appends Set-Cookie headers to an existing Headers object.
|
|
212
|
+
*/
|
|
213
|
+
declare function appendKnownAccountCookie(headers: Headers, account: KnownAccountEntry, existingCookie: string | null, options?: SessionCookieOptions, privacy?: AccountSwitcherPrivacy): void;
|
|
214
|
+
/**
|
|
215
|
+
* Remove a specific account from the known accounts cookie.
|
|
216
|
+
*/
|
|
217
|
+
declare function removeKnownAccountFromCookie(headers: Headers, userId: string, existingCookie: string | null, options?: SessionCookieOptions): void;
|
|
218
|
+
/**
|
|
219
|
+
* Clear the known accounts cookie entirely.
|
|
220
|
+
*/
|
|
221
|
+
declare function clearKnownAccountsCookie(headers: Headers, options?: SessionCookieOptions): void;
|
|
222
|
+
/**
|
|
223
|
+
* Read known accounts from a Request's cookies.
|
|
224
|
+
*/
|
|
225
|
+
declare function getKnownAccountsFromRequest(request: Request): KnownAccountEntry[];
|
|
226
|
+
/**
|
|
227
|
+
* Read the raw known accounts cookie value from a Request.
|
|
228
|
+
*/
|
|
229
|
+
declare function getKnownAccountsCookieRaw(request: Request): string | null;
|
|
230
|
+
/**
|
|
231
|
+
* Normalize all entries in the known accounts cookie to the given privacy level.
|
|
232
|
+
* Returns a Set-Cookie header string if any entries changed, or null if nothing changed.
|
|
233
|
+
* Used on /me requests to migrate legacy full-PII cookies to the configured privacy level.
|
|
234
|
+
*/
|
|
235
|
+
declare function normalizeKnownAccountsCookie(request: Request, privacy: AccountSwitcherPrivacy | undefined, options?: SessionCookieOptions): string | null;
|
|
184
236
|
/**
|
|
185
237
|
* Require authentication - throws Response if not authenticated
|
|
186
238
|
*
|
|
@@ -220,6 +272,19 @@ interface AuthRoutesConfig {
|
|
|
220
272
|
cookies?: SessionCookieOptions;
|
|
221
273
|
/** Enable CSRF validation on state-changing requests (POST/DELETE/PATCH) */
|
|
222
274
|
csrf?: boolean;
|
|
275
|
+
/**
|
|
276
|
+
* Enable the account switcher — remembers which accounts have logged in on
|
|
277
|
+
* this device (metadata only, no tokens). Switching requires re-authentication.
|
|
278
|
+
* Adds routes: switch-account, forget-account, forget-all-accounts, known-accounts.
|
|
279
|
+
*/
|
|
280
|
+
enableAccountSwitcher?: boolean;
|
|
281
|
+
/**
|
|
282
|
+
* Privacy level for account switcher display metadata.
|
|
283
|
+
* - 'full': Store email, name, avatar as-is (default)
|
|
284
|
+
* - 'masked': Mask email, truncate name to initial
|
|
285
|
+
* - 'minimal': No PII — just a colored "Account" label
|
|
286
|
+
*/
|
|
287
|
+
accountSwitcherPrivacy?: 'full' | 'masked' | 'minimal';
|
|
223
288
|
/** Callbacks */
|
|
224
289
|
onLogin?: (user: {
|
|
225
290
|
id: string;
|
|
@@ -1011,4 +1076,187 @@ declare function invalidateBundleCache(key?: string): void;
|
|
|
1011
1076
|
*/
|
|
1012
1077
|
declare function prefetchBundles(keys: string[]): Promise<void>;
|
|
1013
1078
|
|
|
1014
|
-
|
|
1079
|
+
/**
|
|
1080
|
+
* Safe-redirect validation for authentication callback URLs.
|
|
1081
|
+
*
|
|
1082
|
+
* Prevents open-redirect attacks on `returnTo` / `callbackUrl` / `next`
|
|
1083
|
+
* params after login / registration / password-reset flows. An attacker
|
|
1084
|
+
* who can inject a `?returnTo=https://evil.example` into your login
|
|
1085
|
+
* form would otherwise redirect the freshly-authenticated user away
|
|
1086
|
+
* from your origin — a classic phishing + credential-harvesting setup.
|
|
1087
|
+
*
|
|
1088
|
+
* Safe by default — unknown input returns the configured default
|
|
1089
|
+
* path. Hosts opt in to external origins explicitly via
|
|
1090
|
+
* `allowedOrigins`.
|
|
1091
|
+
*
|
|
1092
|
+
* Framework-agnostic: no imports from `next/*`. Safe in any
|
|
1093
|
+
* Edge / Node / middleware runtime.
|
|
1094
|
+
*/
|
|
1095
|
+
interface SafeRedirectOptions {
|
|
1096
|
+
/**
|
|
1097
|
+
* Origins (schema + host + optional port) the redirect is allowed
|
|
1098
|
+
* to land on. Default `[]` — only same-origin relative paths are
|
|
1099
|
+
* allowed. Items are compared case-insensitively after normalizing
|
|
1100
|
+
* the scheme and host.
|
|
1101
|
+
*
|
|
1102
|
+
* Good practice: list only origins that your organization controls
|
|
1103
|
+
* and that are reachable over HTTPS.
|
|
1104
|
+
*
|
|
1105
|
+
* @example
|
|
1106
|
+
* ['https://app.example.com', 'https://admin.example.com']
|
|
1107
|
+
*/
|
|
1108
|
+
allowedOrigins?: string[];
|
|
1109
|
+
/**
|
|
1110
|
+
* Value returned when the input is missing, invalid, or points
|
|
1111
|
+
* outside the allowlist. Default `'/'`.
|
|
1112
|
+
*/
|
|
1113
|
+
defaultPath?: string;
|
|
1114
|
+
/**
|
|
1115
|
+
* Strip the scheme + host from same-origin absolute URLs and
|
|
1116
|
+
* return only the path + query + fragment. Default `true` — keeps
|
|
1117
|
+
* redirect targets as short relative URLs that route through
|
|
1118
|
+
* client-side routing cleanly.
|
|
1119
|
+
*/
|
|
1120
|
+
stripSameOriginHost?: boolean;
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* Returns a safe-to-use redirect path, falling back to
|
|
1124
|
+
* `opts.defaultPath` (default `'/'`) when the input fails validation.
|
|
1125
|
+
*
|
|
1126
|
+
* Accepts:
|
|
1127
|
+
* - empty / nullish → default
|
|
1128
|
+
* - relative paths like `/foo/bar?x=1#frag` (leading `/` required)
|
|
1129
|
+
* - absolute URLs whose origin is in `allowedOrigins`
|
|
1130
|
+
*
|
|
1131
|
+
* Rejects everything else, including:
|
|
1132
|
+
* - schema-relative URLs (`//evil.example`)
|
|
1133
|
+
* - backslash-prefixed paths
|
|
1134
|
+
* - `javascript:`, `data:`, `mailto:`, `vbscript:` etc.
|
|
1135
|
+
* - bare hostnames (`example.com/path`)
|
|
1136
|
+
* - malformed URLs
|
|
1137
|
+
*/
|
|
1138
|
+
declare function validateSafeRedirect(input: string | null | undefined, opts?: SafeRedirectOptions): string;
|
|
1139
|
+
/**
|
|
1140
|
+
* Boolean predicate form. Returns `true` when `input` would pass
|
|
1141
|
+
* `validateSafeRedirect` with the same options.
|
|
1142
|
+
*/
|
|
1143
|
+
declare function isSafeRedirect(input: string | null | undefined, opts?: SafeRedirectOptions): boolean;
|
|
1144
|
+
|
|
1145
|
+
/**
|
|
1146
|
+
* Security-headers helper for Next.js hosts.
|
|
1147
|
+
*
|
|
1148
|
+
* Returns a shape that drops directly into the `headers()` entry in
|
|
1149
|
+
* `next.config.ts` (or the `headers` field of a Route Handler
|
|
1150
|
+
* `NextResponse`). Reasonable defaults for the top-of-OWASP headers
|
|
1151
|
+
* — HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy,
|
|
1152
|
+
* X-XSS-Protection, Permissions-Policy — with knobs for the fields
|
|
1153
|
+
* that typically differ by app (HSTS duration, frame ancestors,
|
|
1154
|
+
* camera/microphone policies).
|
|
1155
|
+
*
|
|
1156
|
+
* Framework-agnostic: no imports from `next/*` so the function works
|
|
1157
|
+
* in Edge / Node / middleware and is trivially unit-testable.
|
|
1158
|
+
*/
|
|
1159
|
+
interface SecurityHeaderEntry {
|
|
1160
|
+
key: string;
|
|
1161
|
+
value: string;
|
|
1162
|
+
}
|
|
1163
|
+
interface BuildSecurityHeadersOptions {
|
|
1164
|
+
/**
|
|
1165
|
+
* HSTS max-age in seconds. Default `31536000` (1 year) — the value
|
|
1166
|
+
* required for inclusion on the HSTS preload list. Set to `0` (or
|
|
1167
|
+
* pass `includeHsts: false`) to opt out entirely.
|
|
1168
|
+
*/
|
|
1169
|
+
hstsMaxAgeSeconds?: number;
|
|
1170
|
+
/** Adds the `includeSubDomains` directive to HSTS. Default `true`. */
|
|
1171
|
+
hstsIncludeSubDomains?: boolean;
|
|
1172
|
+
/**
|
|
1173
|
+
* Adds the `preload` directive to HSTS. Default `false` — only set
|
|
1174
|
+
* to `true` after you've read https://hstspreload.org and are ready
|
|
1175
|
+
* to commit; reversing it requires months of calendar time.
|
|
1176
|
+
*/
|
|
1177
|
+
hstsPreload?: boolean;
|
|
1178
|
+
/** Emit the HSTS header at all. Default `true`. */
|
|
1179
|
+
includeHsts?: boolean;
|
|
1180
|
+
/**
|
|
1181
|
+
* Value for `X-Frame-Options`. Default `'DENY'`. Pass `'SAMEORIGIN'`
|
|
1182
|
+
* if your app is framed by its own subdomain. Set to `null` to
|
|
1183
|
+
* suppress (e.g. when you're already using `frame-ancestors` in
|
|
1184
|
+
* CSP). `SAMEORIGIN` accepts no host parameter; use CSP's
|
|
1185
|
+
* `frame-ancestors` for finer-grained framing policy.
|
|
1186
|
+
*/
|
|
1187
|
+
frameOptions?: 'DENY' | 'SAMEORIGIN' | null;
|
|
1188
|
+
/**
|
|
1189
|
+
* Value for `Referrer-Policy`. Default `'strict-origin-when-cross-origin'`
|
|
1190
|
+
* — same as Next.js's own default, balances analytics with privacy.
|
|
1191
|
+
*/
|
|
1192
|
+
referrerPolicy?: 'no-referrer' | 'no-referrer-when-downgrade' | 'origin' | 'origin-when-cross-origin' | 'same-origin' | 'strict-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url';
|
|
1193
|
+
/**
|
|
1194
|
+
* Permissions-Policy directives. Pass a map of `{ feature: allowlist }`
|
|
1195
|
+
* where the allowlist is either an array of origins (string[]) or
|
|
1196
|
+
* one of the well-known tokens `'self'` / `'*'` / `'()'` (deny).
|
|
1197
|
+
*
|
|
1198
|
+
* Default: camera + microphone allowed for `'self'` only (useful
|
|
1199
|
+
* for the conference track); geolocation + payment denied; every
|
|
1200
|
+
* other common feature left unset (browsers apply their own
|
|
1201
|
+
* conservative defaults).
|
|
1202
|
+
*
|
|
1203
|
+
* Pass `{}` to emit an empty `Permissions-Policy: ` header (rarely
|
|
1204
|
+
* useful). Pass `null` to suppress the header entirely.
|
|
1205
|
+
*/
|
|
1206
|
+
permissionsPolicy?: PermissionsPolicyMap | null;
|
|
1207
|
+
/**
|
|
1208
|
+
* Value for `X-Content-Type-Options`. Default `'nosniff'` — do not
|
|
1209
|
+
* override without a specific reason.
|
|
1210
|
+
*/
|
|
1211
|
+
contentTypeOptions?: string | null;
|
|
1212
|
+
/**
|
|
1213
|
+
* Legacy `X-XSS-Protection`. Default `'0'` (disables the
|
|
1214
|
+
* deprecated IE/Edge filter which had known bypass issues —
|
|
1215
|
+
* recommended by OWASP + Next.js). Set to `'1; mode=block'` only
|
|
1216
|
+
* if you have a specific compliance reason.
|
|
1217
|
+
*/
|
|
1218
|
+
xssProtection?: string | null;
|
|
1219
|
+
/**
|
|
1220
|
+
* Extra headers to merge into the result. Takes precedence over
|
|
1221
|
+
* the computed ones — useful for adding Content-Security-Policy or
|
|
1222
|
+
* Cross-Origin-* headers without forcing a new option for each.
|
|
1223
|
+
*/
|
|
1224
|
+
extraHeaders?: SecurityHeaderEntry[];
|
|
1225
|
+
}
|
|
1226
|
+
type PermissionsPolicyValue = readonly string[] | 'self' | '*' | '()';
|
|
1227
|
+
type PermissionsPolicyMap = Readonly<Record<string, PermissionsPolicyValue>>;
|
|
1228
|
+
declare const DEFAULT_PERMISSIONS_POLICY: PermissionsPolicyMap;
|
|
1229
|
+
/**
|
|
1230
|
+
* Build a reasonable-default set of security response headers in the
|
|
1231
|
+
* exact shape Next.js `headers()` expects.
|
|
1232
|
+
*
|
|
1233
|
+
* @example next.config.ts
|
|
1234
|
+
* ```ts
|
|
1235
|
+
* import { buildSecurityHeaders } from '@scalemule/nextjs/server'
|
|
1236
|
+
*
|
|
1237
|
+
* export default {
|
|
1238
|
+
* async headers() {
|
|
1239
|
+
* return [
|
|
1240
|
+
* { source: '/:path*', headers: buildSecurityHeaders() },
|
|
1241
|
+
* ]
|
|
1242
|
+
* },
|
|
1243
|
+
* }
|
|
1244
|
+
* ```
|
|
1245
|
+
*
|
|
1246
|
+
* @example custom overrides
|
|
1247
|
+
* ```ts
|
|
1248
|
+
* buildSecurityHeaders({
|
|
1249
|
+
* frameOptions: 'SAMEORIGIN',
|
|
1250
|
+
* permissionsPolicy: {
|
|
1251
|
+
* camera: ['https://trusted.example.com'],
|
|
1252
|
+
* microphone: ['https://trusted.example.com'],
|
|
1253
|
+
* },
|
|
1254
|
+
* extraHeaders: [
|
|
1255
|
+
* { key: 'Content-Security-Policy', value: "default-src 'self'" },
|
|
1256
|
+
* ],
|
|
1257
|
+
* })
|
|
1258
|
+
* ```
|
|
1259
|
+
*/
|
|
1260
|
+
declare function buildSecurityHeaders(opts?: BuildSecurityHeadersOptions): SecurityHeaderEntry[];
|
|
1261
|
+
|
|
1262
|
+
export { type AnalyticsRoutesConfig, type AnalyticsTrackingGateConfig, type AuthMiddlewareConfig, type AuthRoutesConfig, type BuildSecurityHeadersOptions, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, DEFAULT_PERMISSIONS_POLICY, type HandlerContext, type HandlerOptions, KNOWN_ACCOUNTS_COOKIE_NAME, type KnownAccountEntry, type MySqlBundle, type NotificationRoutesConfig, OAUTH_STATE_COOKIE_NAME, type OAuthBundle, type PermissionsPolicyMap, type PermissionsPolicyValue, type PostgresBundle, type PushRoutesConfig, type RedisBundle, type S3Bundle, SESSION_COOKIE_NAME, type SafeRedirectOptions, ScaleMuleError, type SecurityHeaderEntry, ServerConfig, type SessionCookieOptions, type SessionData, type SmtpBundle, USER_ID_COOKIE_NAME, apiHandler, appendKnownAccountCookie, buildClientContextHeaders, buildFlagContext, buildSecurityHeaders, clearKnownAccountsCookie, clearOAuthState, clearSession, configureBundles, configureSecrets, createAnalyticsRoutes, createAuthMiddleware, createAuthRoutes, createNotificationRoutes, createPushRoutes, errorCodeToStatus, extractClientContext, extractClientContextFromReq, generateCSRFToken, getAppSecret, getAppSecretOrDefault, getBootstrapFlags, getBundle, getCSRFToken, getKnownAccountsCookieRaw, getKnownAccountsFromRequest, getMySqlBundle, getOAuthBundle, getPostgresBundle, getRedisBundle, getS3Bundle, getSession, getSessionFromRequest, getSmtpBundle, invalidateBundleCache, invalidateSecretCache, isSafeRedirect, normalizeKnownAccountsCookie, prefetchBundles, prefetchSecrets, removeKnownAccountFromCookie, requireAppSecret, requireBundle, requireSession, setOAuthState, unwrap, validateCSRFToken, validateCSRFTokenAsync, validateOAuthState, validateOAuthStateAsync, validateSafeRedirect, withAuth, withCSRFProtection, withCSRFToken, withSession };
|