@oxyhq/core 12.11.1 → 13.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -2
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/index.js +4 -13
- package/dist/cjs/mixins/OxyServices.deviceBoot.js +0 -31
- package/dist/cjs/mixins/OxyServices.user.js +50 -44
- package/dist/cjs/server/index.js +9 -6
- package/dist/cjs/server/rateLimit.js +3 -0
- package/dist/cjs/server/securityHeaders.js +234 -0
- package/dist/cjs/session/accountDialogController.js +6 -8
- package/dist/cjs/utils/apiUtils.js +40 -10
- package/dist/cjs/utils/oauthPkce.js +1 -5
- package/dist/cjs/utils/officialOrigins.js +3 -73
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/index.js +3 -4
- package/dist/esm/mixins/OxyServices.deviceBoot.js +1 -32
- package/dist/esm/mixins/OxyServices.user.js +51 -45
- package/dist/esm/server/index.js +4 -1
- package/dist/esm/server/rateLimit.js +3 -0
- package/dist/esm/server/securityHeaders.js +224 -0
- package/dist/esm/session/accountDialogController.js +6 -8
- package/dist/esm/utils/apiUtils.js +39 -10
- package/dist/esm/utils/oauthPkce.js +0 -4
- package/dist/esm/utils/officialOrigins.js +3 -68
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/index.d.ts +5 -7
- package/dist/types/mixins/OxyServices.auth.d.ts +1 -12
- package/dist/types/mixins/OxyServices.deviceBoot.d.ts +1 -5
- package/dist/types/mixins/OxyServices.user.d.ts +27 -6
- package/dist/types/server/index.d.ts +3 -1
- package/dist/types/server/securityHeaders.d.ts +154 -0
- package/dist/types/session/accountDialogController.d.ts +9 -15
- package/dist/types/utils/apiUtils.d.ts +48 -6
- package/dist/types/utils/oauthPkce.d.ts +11 -7
- package/dist/types/utils/officialOrigins.d.ts +3 -13
- package/package.json +10 -5
- package/src/index.ts +9 -14
- package/src/mixins/OxyServices.auth.ts +6 -13
- package/src/mixins/OxyServices.deviceBoot.ts +0 -47
- package/src/mixins/OxyServices.user.ts +60 -49
- package/src/mixins/__tests__/commonsSignIn.test.ts +9 -2
- package/src/mixins/__tests__/followGraphPagination.test.ts +250 -0
- package/src/server/__tests__/securityHeaders.test.ts +244 -0
- package/src/server/index.ts +17 -8
- package/src/server/rateLimit.ts +3 -0
- package/src/server/securityHeaders.ts +304 -0
- package/src/session/__tests__/accountDialogController.test.ts +3 -5
- package/src/session/accountDialogController.ts +12 -18
- package/src/utils/__tests__/officialOrigins.test.ts +0 -57
- package/src/utils/apiUtils.ts +64 -15
- package/src/utils/oauthPkce.ts +11 -9
- package/src/utils/officialOrigins.ts +3 -70
- package/dist/cjs/session/hubSync.js +0 -55
- package/dist/esm/session/hubSync.js +0 -51
- package/dist/types/session/hubSync.d.ts +0 -20
- package/src/session/__tests__/hubSync.test.ts +0 -51
- package/src/session/hubSync.ts +0 -79
package/dist/types/index.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export { getCommonsApprovalBlockingReason, parseCommonsApprovalExpiresAt, } from
|
|
|
26
26
|
export { selectCommonsDelivery, pushTargetsFromDelivery, commonsDeliveryPlatform } from './utils/commonsDelivery';
|
|
27
27
|
export type { CommonsDeliveryFacts, CommonsDeliveryPlatform, CommonsDeliveryRoute, } from './utils/commonsDelivery';
|
|
28
28
|
export type { ServiceTokenResponse } from './mixins/OxyServices.auth';
|
|
29
|
-
export type { CommonsSignInHandle, CommonsSignInStatus, CommonsSignInPurpose, CommonsOAuthContext, CommonsApprovalInfo, CommonsApprovalSubjectAccount, CommonsSignInActionResult,
|
|
29
|
+
export type { CommonsSignInHandle, CommonsSignInStatus, CommonsSignInPurpose, CommonsOAuthContext, CommonsApprovalInfo, CommonsApprovalSubjectAccount, CommonsSignInActionResult, CommonsOAuthFinalizeResult, CommonsDeliveryResult, } from './mixins/OxyServices.auth';
|
|
30
30
|
export type { PushTokenPlatform, RegisterPushTokenInput, } from './mixins/OxyServices.notifications';
|
|
31
31
|
export type { ServiceApp, ServiceActingAsVerification } from './mixins/OxyServices.utility';
|
|
32
32
|
export type { ContactDiscoveryMatch, ContactDiscoveryResponse, } from './mixins/OxyServices.contacts';
|
|
@@ -80,8 +80,8 @@ export { HttpStatus, getErrorStatus, getErrorMessage, isAlreadyRegisteredError,
|
|
|
80
80
|
export { DEFAULT_CIRCUIT_BREAKER_CONFIG, createCircuitBreakerState, calculateBackoffInterval, recordFailure, recordSuccess, shouldAllowRequest, delay, withRetry, } from './shared/utils/networkUtils';
|
|
81
81
|
export type { CircuitBreakerState, CircuitBreakerConfig } from './shared/utils/networkUtils';
|
|
82
82
|
export { translate } from './i18n';
|
|
83
|
-
export { buildSearchParams, buildUrl, buildPaginationParams, safeJsonParse, } from './utils/apiUtils';
|
|
84
|
-
export type { PaginationParams, ApiResponse, ErrorResponse, } from './utils/apiUtils';
|
|
83
|
+
export { buildQueryParams, buildSearchParams, buildUrl, buildPaginationParams, safeJsonParse, } from './utils/apiUtils';
|
|
84
|
+
export type { PaginationParams, FollowGraphParams, FollowGraphSort, ApiResponse, ErrorResponse, } from './utils/apiUtils';
|
|
85
85
|
export { ErrorCodes, createApiError, handleHttpError, validateRequiredFields, } from './utils/errorUtils';
|
|
86
86
|
export { retryAsync } from './utils/asyncUtils';
|
|
87
87
|
export { EMAIL_REGEX, USERNAME_REGEX, PASSWORD_REGEX, isValidEmail, isValidUsername, isValidPassword, isValidDisplayName, DISPLAY_NAME_ALLOWED_SCRIPTS, DISPLAY_NAME_DISALLOWED_SOURCE, DISPLAY_NAME_ORPHANED_MARK_SOURCE, isRequiredString, isRequiredNumber, isRequiredBoolean, isValidArray, isValidObject, isValidUUID, isValidURL, isValidDate, isValidFileSize, isValidFileType, sanitizeString, sanitizeHTML, isValidObjectId, validateAndSanitizeUserInput, } from './utils/validationUtils';
|
|
@@ -96,11 +96,9 @@ export { CENTRAL_IDP_APEX } from './utils/authWebUrl';
|
|
|
96
96
|
export { isOxyRpOrigin } from './utils/webauthnOrigin';
|
|
97
97
|
export { runColdBoot } from './utils/coldBoot';
|
|
98
98
|
export type { ColdBootStep, ColdBootStepResult, ColdBootSession, ColdBootSkip, ColdBootOutcome, RunColdBootOptions, } from './utils/coldBoot';
|
|
99
|
-
export { buildOAuthAuthorizeUrl, computeCodeChallenge, generateOAuthState, generatePkcePair, DEFAULT_OAUTH_SCOPE, OXY_AUTHORIZE_URL, OXY_OAUTH_STATE_STORAGE_KEY, OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY, OXY_OAUTH_REDIRECT_URI_STORAGE_KEY,
|
|
99
|
+
export { buildOAuthAuthorizeUrl, computeCodeChallenge, generateOAuthState, generatePkcePair, DEFAULT_OAUTH_SCOPE, OXY_AUTHORIZE_URL, OXY_OAUTH_STATE_STORAGE_KEY, OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY, OXY_OAUTH_REDIRECT_URI_STORAGE_KEY, OXY_OAUTH_RETURN_PATH_STORAGE_KEY, normalizeOAuthRedirectUri, canonicalizeOAuthRedirectUri, persistOAuthHandshake, readOAuthHandshake, clearOAuthHandshake, persistOAuthReturnPath, consumeOAuthReturnPath, } from './utils/oauthPkce';
|
|
100
100
|
export type { PkcePair, BuildOAuthAuthorizeUrlParams } from './utils/oauthPkce';
|
|
101
|
-
export {
|
|
102
|
-
export { syncHubAfterSignIn, redeemHubTicketOnHub, } from './session/hubSync';
|
|
103
|
-
export type { SyncHubAfterSignInOptions } from './session/hubSync';
|
|
101
|
+
export { isLoopbackOrigin, isOfficialWebOrigin, isAllowedDeviceJoinOrigin, } from './utils/officialOrigins';
|
|
104
102
|
export { SessionClient } from './session/SessionClient';
|
|
105
103
|
export type { TokenTransport, SessionClientHost, SessionClientOptions, DeviceCredential, SessionStateOrigin } from './session/SessionClient';
|
|
106
104
|
export type { SocketIOFactory, MinimalSocket } from './session/socketLoader';
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Supports password-based login (email/username) and public key challenge-response.
|
|
5
5
|
*/
|
|
6
6
|
import type { User } from '../models/interfaces';
|
|
7
|
-
import type { LoginResult, LoginSessionResult } from '@oxyhq/contracts';
|
|
7
|
+
import type { LoginResult, LoginSessionResult, CommonsDenyReason } from '@oxyhq/contracts';
|
|
8
8
|
import type { SessionLoginResponse } from '../models/session';
|
|
9
9
|
import type { OxyServicesBase } from '../OxyServices.base';
|
|
10
10
|
import type { PublicApplication } from './OxyServices.connectedApps';
|
|
@@ -226,17 +226,6 @@ export interface CommonsApprovalInfo {
|
|
|
226
226
|
export interface CommonsSignInActionResult {
|
|
227
227
|
success: boolean;
|
|
228
228
|
}
|
|
229
|
-
/**
|
|
230
|
-
* Why the approver denied a "Sign in with Oxy" request. A CLOSED set — the deny
|
|
231
|
-
* endpoint is unauthenticated, so it accepts no free-form text:
|
|
232
|
-
*
|
|
233
|
-
* - `'declined'` the approver rejected a request they recognised ("Not now").
|
|
234
|
-
* - `'not_me'` the approver did not start the request ("This wasn't me").
|
|
235
|
-
* The only value that records the denial as suspicious rather
|
|
236
|
-
* than an ordinary cancel, so a UI may only offer it where the
|
|
237
|
-
* user genuinely said so.
|
|
238
|
-
*/
|
|
239
|
-
export type CommonsDenyReason = 'declined' | 'not_me';
|
|
240
229
|
/**
|
|
241
230
|
* Result of finalizing an approved, OAuth-bound "Sign in with Oxy" request.
|
|
242
231
|
*
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* the cold boot / re-mint handler own persistence and `setTokens`, so the same
|
|
14
14
|
* primitive can be reused from either without double-planting.
|
|
15
15
|
*/
|
|
16
|
-
import { type DeviceTokenMintResponse
|
|
16
|
+
import { type DeviceTokenMintResponse } from '@oxyhq/contracts';
|
|
17
17
|
import type { OxyServicesBase } from '../OxyServices.base';
|
|
18
18
|
/**
|
|
19
19
|
* The server's `401 account_not_on_device` for a PINNED mint: the requested
|
|
@@ -71,10 +71,6 @@ export declare function OxyServicesDeviceBootMixin<T extends typeof OxyServicesB
|
|
|
71
71
|
mintFromDeviceSecret(deviceId: string, deviceSecret: string, options?: {
|
|
72
72
|
accountId?: string;
|
|
73
73
|
}): Promise<DeviceTokenMintResponse>;
|
|
74
|
-
/** Mint a one-time hub sync ticket (bearer required). */
|
|
75
|
-
issueHubTicket(returnOrigin: string): Promise<DeviceHubTicketIssueResponse>;
|
|
76
|
-
/** Redeem a hub sync ticket for a fresh device secret (public). */
|
|
77
|
-
redeemHubTicket(ticket: string, returnOrigin: string): Promise<DeviceHubTicketRedeemResponse>;
|
|
78
74
|
httpService: import("../HttpService").HttpService;
|
|
79
75
|
cloudURL: string;
|
|
80
76
|
config: import("../OxyServices.base").OxyConfig;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import type { User, Notification, NotificationPreferences, UserPreferences, SearchProfilesResponse, PrivacySettings } from '../models/interfaces';
|
|
5
5
|
import type { UserNameResponse, UserProfileUpdate, RecommendationRequest, RecommendationItem, ThemePreference } from '@oxyhq/contracts';
|
|
6
6
|
import type { OxyServicesBase } from '../OxyServices.base';
|
|
7
|
-
import { type PaginationParams } from '../utils/apiUtils';
|
|
7
|
+
import { type PaginationParams, type FollowGraphParams } from '../utils/apiUtils';
|
|
8
8
|
/**
|
|
9
9
|
* Response of the single follow/unfollow toggle route
|
|
10
10
|
* (`POST /users/:id/follow` and `DELETE /users/:id/follow`). The route reports a
|
|
@@ -329,6 +329,22 @@ export declare function OxyServicesUserMixin<T extends typeof OxyServicesBase>(B
|
|
|
329
329
|
deleteAccount(confirmText: string): Promise<{
|
|
330
330
|
message: string;
|
|
331
331
|
}>;
|
|
332
|
+
/**
|
|
333
|
+
* Invalidate every cached read a follow/unfollow write invalidates.
|
|
334
|
+
*
|
|
335
|
+
* Shared by the four mutation entry points (`followUser`, `unfollowUser`,
|
|
336
|
+
* `followUsers`, `unfollowUsers`) so they can never drift on which caches a
|
|
337
|
+
* write busts.
|
|
338
|
+
*
|
|
339
|
+
* The follower/following/mutuals LISTS are cleared by PREFIX rather than by
|
|
340
|
+
* exact key. Those reads are paginated and ordered, so one logical list is
|
|
341
|
+
* spread across many content-addressed keys
|
|
342
|
+
* (`GET:/users/<id>/followers:{"limit":"20","offset":"40","sort":"oldest"}`);
|
|
343
|
+
* an exact-key clear would only bust whichever page/sort variant happened to
|
|
344
|
+
* be read last and would leave every other page stale. `clearCacheByPrefix`
|
|
345
|
+
* deletes all of them, and all identity-scoped variants of each.
|
|
346
|
+
*/
|
|
347
|
+
invalidateFollowGraphCaches(targetUserIds: string[]): void;
|
|
332
348
|
/**
|
|
333
349
|
* Follow a user.
|
|
334
350
|
*
|
|
@@ -386,17 +402,22 @@ export declare function OxyServicesUserMixin<T extends typeof OxyServicesBase>(B
|
|
|
386
402
|
*/
|
|
387
403
|
getFollowStatuses(userIds: string[]): Promise<Record<string, boolean>>;
|
|
388
404
|
/**
|
|
389
|
-
* Get user followers
|
|
405
|
+
* Get user followers.
|
|
406
|
+
*
|
|
407
|
+
* `sort` orders the underlying follow edges — `recent` (newest first, the
|
|
408
|
+
* server default) or `oldest`. Because the response is cached and the cache
|
|
409
|
+
* key is content-addressed on the query params, each `limit`/`offset`/`sort`
|
|
410
|
+
* combination is its own entry.
|
|
390
411
|
*/
|
|
391
|
-
getUserFollowers(userId: string, pagination?:
|
|
412
|
+
getUserFollowers(userId: string, pagination?: FollowGraphParams): Promise<{
|
|
392
413
|
followers: User[];
|
|
393
414
|
total: number;
|
|
394
415
|
hasMore: boolean;
|
|
395
416
|
}>;
|
|
396
417
|
/**
|
|
397
|
-
* Get user following
|
|
418
|
+
* Get user following. `sort` behaves as in {@link getUserFollowers}.
|
|
398
419
|
*/
|
|
399
|
-
getUserFollowing(userId: string, pagination?:
|
|
420
|
+
getUserFollowing(userId: string, pagination?: FollowGraphParams): Promise<{
|
|
400
421
|
following: User[];
|
|
401
422
|
total: number;
|
|
402
423
|
hasMore: boolean;
|
|
@@ -405,7 +426,7 @@ export declare function OxyServicesUserMixin<T extends typeof OxyServicesBase>(B
|
|
|
405
426
|
* Get user mutuals ("followers you know" — users the authenticated viewer
|
|
406
427
|
* follows who also follow `userId`). The viewer is derived server-side from auth.
|
|
407
428
|
*/
|
|
408
|
-
getUserMutuals(userId: string, pagination?:
|
|
429
|
+
getUserMutuals(userId: string, pagination?: FollowGraphParams): Promise<{
|
|
409
430
|
mutuals: User[];
|
|
410
431
|
total: number;
|
|
411
432
|
hasMore: boolean;
|
|
@@ -22,6 +22,8 @@ export { assertSafePublicUrl, isBlockedIp, safeFetch, SsrfRejection, UpstreamErr
|
|
|
22
22
|
export type { SafeFetchOptions, SafeFetchResult, SsrfCheckFail, SsrfCheckOk, SsrfCheckResult, } from './safeFetch';
|
|
23
23
|
export { createOxyCors } from './cors';
|
|
24
24
|
export type { OxyCorsOptions } from './cors';
|
|
25
|
+
export { buildOxyCspDirectives, buildOxyPagesHeaders, createOxySecurityHeaders, formatOxyCspPolicy, OXY_CSP_BASELINE, } from './securityHeaders';
|
|
26
|
+
export type { OxyCspDirective, OxyCspExtensions, OxyPagesHeadersOptions, OxySecurityHeadersOptions, } from './securityHeaders';
|
|
25
27
|
export { verifySecret } from './verifySecret';
|
|
26
28
|
export { registrableApex } from '../utils/registrableApex';
|
|
27
|
-
export {
|
|
29
|
+
export { isOfficialWebOrigin } from '../utils/officialOrigins';
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared security headers (Helmet + Content-Security-Policy) for Oxy backends.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS
|
|
5
|
+
* ---------------
|
|
6
|
+
* A CSP only governs an origin that serves DOCUMENTS; on a JSON API it governs
|
|
7
|
+
* no browsing context. The Oxy origins that serve HTML through Cloudflare have
|
|
8
|
+
* so far either hand-written their own policy or shipped none at all, and two
|
|
9
|
+
* bugs follow from that:
|
|
10
|
+
*
|
|
11
|
+
* 1. THE CLOUDFLARE INSIGHTS BEACON IS BLOCKED BY A HAND-WRITTEN POLICY.
|
|
12
|
+
* Cloudflare injects `<script src="https://static.cloudflareinsights.com/
|
|
13
|
+
* beacon.min.js/...">` into HTML it proxies. No application code loads it,
|
|
14
|
+
* so it cannot be allowlisted from the app side any other way, and an
|
|
15
|
+
* origin whose policy says `script-src 'self'` logs
|
|
16
|
+
* `Loading the script 'https://static.cloudflareinsights.com/beacon.min.js'
|
|
17
|
+
* violates the following Content Security Policy directive: "script-src
|
|
18
|
+
* 'self'"` and collects nothing. The beacon needs BOTH hosts, and they are
|
|
19
|
+
* different halves of the same feature: `static.cloudflareinsights.com`
|
|
20
|
+
* serves the script (`script-src`), `cloudflareinsights.com` receives the
|
|
21
|
+
* measurements (`connect-src`). Allowing only the script leaves the beacon
|
|
22
|
+
* loading but unable to report, which looks fixed and is not. Verified in
|
|
23
|
+
* production 2026-07-29: `mention.earth` serves HTML behind Cloudflare with
|
|
24
|
+
* the beacon injected and `script-src 'self'` — blocked; `oxy.so` had
|
|
25
|
+
* already allowlisted the same two hosts in its own static `_headers`,
|
|
26
|
+
* independently, which is the divergence this baseline exists to end.
|
|
27
|
+
*
|
|
28
|
+
* 2. AN EXPLICIT DIRECTIVE SILENTLY REPLACES HELMET'S DEFAULT.
|
|
29
|
+
* Writing `scriptSrc: ['https://example.com']` drops `'self'` — the page's
|
|
30
|
+
* own bundle stops loading (or, worse, only some lazily-loaded chunk does,
|
|
31
|
+
* so it ships). This helper makes that structurally impossible: callers can
|
|
32
|
+
* only ADD sources to the Oxy baseline, never replace a directive, and they
|
|
33
|
+
* cannot pass their own `contentSecurityPolicy` through to Helmet at all
|
|
34
|
+
* (the option is typed `never`).
|
|
35
|
+
*
|
|
36
|
+
* WHAT IT PROVIDES
|
|
37
|
+
* ----------------
|
|
38
|
+
* `createOxySecurityHeaders(options)` returns the Helmet middleware with the
|
|
39
|
+
* Oxy-wide CSP baseline applied, plus per-app extensions merged (and deduped)
|
|
40
|
+
* into it. Everything Helmet does that is NOT the CSP (HSTS, frameguard,
|
|
41
|
+
* referrer policy, CORP/COOP, …) is passed straight through, so an app keeps
|
|
42
|
+
* full control of those.
|
|
43
|
+
*
|
|
44
|
+
* `buildOxyCspDirectives(extensions)` is the same resolution as a pure
|
|
45
|
+
* function, for the Oxy document origins that are NOT Express — a Next.js
|
|
46
|
+
* `headers()`, a Cloudflare Pages `_headers` generator — so one policy can
|
|
47
|
+
* cover them without a second implementation.
|
|
48
|
+
*
|
|
49
|
+
* SCOPE: mount this on backends that serve HTML. A JSON-only API gains nothing
|
|
50
|
+
* from a source-list CSP; harden those with the non-CSP headers instead
|
|
51
|
+
* (`hsts`, `noSniff`, `frameguard`, CORP) rather than adding directives that
|
|
52
|
+
* apply to no document.
|
|
53
|
+
*
|
|
54
|
+
* Node/Express-only: exported solely from `@oxyhq/core/server`.
|
|
55
|
+
*/
|
|
56
|
+
import type { RequestHandler } from 'express';
|
|
57
|
+
import { type HelmetOptions } from 'helmet';
|
|
58
|
+
/** The CSP directives an Oxy app may extend, in Helmet's camelCase spelling. */
|
|
59
|
+
export type OxyCspDirective = 'baseUri' | 'connectSrc' | 'defaultSrc' | 'fontSrc' | 'formAction' | 'frameAncestors' | 'frameSrc' | 'imgSrc' | 'manifestSrc' | 'mediaSrc' | 'objectSrc' | 'scriptSrc' | 'scriptSrcAttr' | 'scriptSrcElem' | 'styleSrc' | 'styleSrcElem' | 'workerSrc';
|
|
60
|
+
/**
|
|
61
|
+
* Per-app ADDITIONS to the Oxy baseline, keyed by directive. Values are merged
|
|
62
|
+
* into the baseline and deduped — they never replace it, so `'self'` (and the
|
|
63
|
+
* Cloudflare beacon hosts) cannot be lost. Extending a directive the baseline
|
|
64
|
+
* does not define seeds it with `'self'` first, for the same reason.
|
|
65
|
+
*/
|
|
66
|
+
export type OxyCspExtensions = Partial<Record<OxyCspDirective, readonly string[]>>;
|
|
67
|
+
/**
|
|
68
|
+
* The Oxy-wide CSP baseline. Deliberately the floor every Oxy origin needs, not
|
|
69
|
+
* a superset of what any one app allows — permissive sources an individual app
|
|
70
|
+
* wants (`https:` images, `blob:` media, embed hosts, LiveKit) are that app's
|
|
71
|
+
* extension, so each widening stays visible at its call site.
|
|
72
|
+
*
|
|
73
|
+
* `style-src` carries `'unsafe-inline'` because react-native-web injects its
|
|
74
|
+
* stylesheet as inline `<style>` at runtime; without it every Oxy web app
|
|
75
|
+
* renders unstyled.
|
|
76
|
+
*/
|
|
77
|
+
export declare const OXY_CSP_BASELINE: Readonly<Partial<Record<OxyCspDirective, readonly string[]>>>;
|
|
78
|
+
/**
|
|
79
|
+
* Resolve the effective CSP directives: the Oxy baseline, with each app
|
|
80
|
+
* extension merged in and deduped.
|
|
81
|
+
*
|
|
82
|
+
* Merge rules:
|
|
83
|
+
* - A baseline directive is EXTENDED, never replaced — `'self'` and the
|
|
84
|
+
* Cloudflare beacon hosts always survive.
|
|
85
|
+
* - A directive absent from the baseline is seeded with `'self'`, so adding
|
|
86
|
+
* (say) an embed host to `frame-src` cannot lock the origin out of itself.
|
|
87
|
+
* - A directive whose baseline is exactly `'none'` is CLOSED: extending it
|
|
88
|
+
* drops the sentinel, because `'none'` alongside any other source is
|
|
89
|
+
* meaningless per the CSP spec. This is how an app that must be framable
|
|
90
|
+
* opts back in with `frameAncestors: ["'self'"]`.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* buildOxyCspDirectives({ frameSrc: ['https://player.vimeo.com'] });
|
|
95
|
+
* // → { ..., 'frame-src': ["'self'", 'https://player.vimeo.com'], ... }
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
export declare function buildOxyCspDirectives(extensions?: OxyCspExtensions): Record<string, string[]>;
|
|
99
|
+
/**
|
|
100
|
+
* Serialize resolved CSP directives into the single-line header value browsers
|
|
101
|
+
* and Cloudflare `_headers` expect. Valueless directives (e.g.
|
|
102
|
+
* `upgrade-insecure-requests`) emit the name alone.
|
|
103
|
+
*/
|
|
104
|
+
export declare function formatOxyCspPolicy(directives: Record<string, string[]>): string;
|
|
105
|
+
export interface OxyPagesHeadersOptions {
|
|
106
|
+
/** Per-app additions merged into {@link OXY_CSP_BASELINE}. */
|
|
107
|
+
csp?: OxyCspExtensions;
|
|
108
|
+
/**
|
|
109
|
+
* Emit `Strict-Transport-Security` (default `true`). Cloudflare Pages serves
|
|
110
|
+
* HTTPS only, so static deploys should keep this on.
|
|
111
|
+
*/
|
|
112
|
+
hsts?: boolean;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Build a Cloudflare Pages `_headers` block for an Oxy HTML origin. Uses the
|
|
116
|
+
* same CSP resolution as {@link createOxySecurityHeaders} plus the non-CSP
|
|
117
|
+
* hardening headers Helmet would add on an Express HTML backend.
|
|
118
|
+
*/
|
|
119
|
+
export declare function buildOxyPagesHeaders(options?: OxyPagesHeadersOptions): string;
|
|
120
|
+
export interface OxySecurityHeadersOptions {
|
|
121
|
+
/**
|
|
122
|
+
* Per-app additions to the Oxy CSP baseline. Merged, deduped, never
|
|
123
|
+
* replacing — see {@link buildOxyCspDirectives}.
|
|
124
|
+
*/
|
|
125
|
+
csp?: OxyCspExtensions;
|
|
126
|
+
/**
|
|
127
|
+
* Everything Helmet does that is not the CSP: `hsts`, `frameguard`,
|
|
128
|
+
* `referrerPolicy`, `crossOriginResourcePolicy`, … Passed straight through.
|
|
129
|
+
*
|
|
130
|
+
* `contentSecurityPolicy` is typed `never` on purpose: the CSP is owned by
|
|
131
|
+
* this helper so the baseline cannot be replaced (nor the `'self'` guarantee
|
|
132
|
+
* bypassed) by an app that hands Helmet its own directive block. Extend it
|
|
133
|
+
* through `csp` instead.
|
|
134
|
+
*/
|
|
135
|
+
helmet?: HelmetOptions & {
|
|
136
|
+
contentSecurityPolicy?: never;
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Build the shared Oxy security-headers middleware: Helmet with the Oxy CSP
|
|
141
|
+
* baseline plus this app's extensions.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```ts
|
|
145
|
+
* app.use(createOxySecurityHeaders({
|
|
146
|
+
* csp: {
|
|
147
|
+
* connectSrc: ['https://api.example.com', 'wss://api.example.com'],
|
|
148
|
+
* frameSrc: ['https://player.vimeo.com'],
|
|
149
|
+
* },
|
|
150
|
+
* helmet: { crossOriginResourcePolicy: { policy: 'cross-origin' } },
|
|
151
|
+
* }));
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
export declare function createOxySecurityHeaders(options?: OxySecurityHeadersOptions): RequestHandler;
|
|
@@ -231,21 +231,17 @@ export interface AccountDialogControllerOptions {
|
|
|
231
231
|
* `SessionClient.registerAndActivate` (registration + activation only — no
|
|
232
232
|
* provider-side durable persist/hydration).
|
|
233
233
|
*
|
|
234
|
-
* This is the SIGN-IN commit:
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
* origin legitimately needs that. An account SWITCH does NOT — see
|
|
238
|
-
* {@link commitSwitchedSession}.
|
|
234
|
+
* This is the SIGN-IN commit: registers the session into the host's device
|
|
235
|
+
* set with durable persist + profile hydration. An account SWITCH uses
|
|
236
|
+
* {@link commitSwitchedSession} instead — see below.
|
|
239
237
|
*/
|
|
240
238
|
commitSession?: (session: SessionLoginResponse) => Promise<void>;
|
|
241
239
|
/**
|
|
242
240
|
* Commit a minted graph SWITCH session into the host's session set — same
|
|
243
241
|
* device-first registration + durable persist + profile hydration as
|
|
244
|
-
* {@link commitSession}, but IN-PLACE: it must NOT
|
|
245
|
-
*
|
|
246
|
-
* device
|
|
247
|
-
* re-syncing is redundant and a full-page redirect on switch is the exact
|
|
248
|
-
* regression this separation prevents. Cross-tab/app propagation of the switch
|
|
242
|
+
* {@link commitSession}, but IN-PLACE: it must NOT re-run sign-in side effects
|
|
243
|
+
* that belong only to a fresh authorization (for example, a redundant full
|
|
244
|
+
* device-set reconcile on switch). Cross-tab/app propagation of the switch
|
|
249
245
|
* still happens instantly via the server's device-scoped `session_state` /
|
|
250
246
|
* `session_accounts_changed` socket broadcast — no navigation required.
|
|
251
247
|
*
|
|
@@ -600,11 +596,9 @@ export declare class AccountDialogController {
|
|
|
600
596
|
* consumer's commit funnel (durable persist + hydration); falls back to
|
|
601
597
|
* `SessionClient.registerAndActivate` (registration + activation only).
|
|
602
598
|
*
|
|
603
|
-
* A SWITCH (`opts.fromSwitch`) uses the IN-PLACE `commitSwitchedSession` funnel
|
|
604
|
-
*
|
|
605
|
-
*
|
|
606
|
-
* switch funnel is not wired it falls back to the sign-in funnel, then to
|
|
607
|
-
* `registerAndActivate`.
|
|
599
|
+
* A SWITCH (`opts.fromSwitch`) uses the IN-PLACE `commitSwitchedSession` funnel;
|
|
600
|
+
* a SIGN-IN uses `commitSession`. When the switch funnel is not wired it falls
|
|
601
|
+
* back to the sign-in funnel, then to `registerAndActivate`.
|
|
608
602
|
*/
|
|
609
603
|
private commitAuthorizedSession;
|
|
610
604
|
private failSignIn;
|
|
@@ -2,18 +2,39 @@
|
|
|
2
2
|
* Utility functions for common API patterns
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
|
-
* Build
|
|
5
|
+
* Build a plain query-parameter record from an object, stringifying values and
|
|
6
|
+
* dropping `undefined`/`null` entries.
|
|
7
|
+
*
|
|
8
|
+
* This is the shape `OxyServices.makeRequest` expects for a GET's `params`:
|
|
9
|
+
* `HttpService` inspects it with `Object.keys(...)` (both to decide whether to
|
|
10
|
+
* append a query string and to build the request's cache key), and
|
|
11
|
+
* `Object.keys(new URLSearchParams({ limit: '20' }))` is `[]` — a
|
|
12
|
+
* `URLSearchParams` exposes its entries through iterator methods, never as own
|
|
13
|
+
* enumerable properties. Passing one to `makeRequest` therefore silently drops
|
|
14
|
+
* the whole query string. Always hand `makeRequest` a plain record.
|
|
15
|
+
*
|
|
16
|
+
* Generic over the input object rather than taking `Record<string, unknown>`,
|
|
17
|
+
* because a TypeScript `interface` (`PaginationParams`, `FollowGraphParams`, …)
|
|
18
|
+
* has no implicit index signature and so is not assignable to that type.
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildQueryParams<T extends object>(params: T): Record<string, string>;
|
|
21
|
+
/**
|
|
22
|
+
* Build URL search parameters from an object.
|
|
23
|
+
*
|
|
24
|
+
* For building a URL string only — see {@link buildQueryParams} for the shape
|
|
25
|
+
* `makeRequest` needs.
|
|
26
|
+
*
|
|
6
27
|
* @param params Object with parameter key-value pairs
|
|
7
28
|
* @returns URLSearchParams instance
|
|
8
29
|
*/
|
|
9
|
-
export declare function buildSearchParams(params:
|
|
30
|
+
export declare function buildSearchParams<T extends object>(params: T): URLSearchParams;
|
|
10
31
|
/**
|
|
11
32
|
* Build URL with search parameters
|
|
12
33
|
* @param baseUrl Base URL
|
|
13
34
|
* @param params Object with parameter key-value pairs
|
|
14
35
|
* @returns Complete URL with search parameters
|
|
15
36
|
*/
|
|
16
|
-
export declare function buildUrl(baseUrl: string, params?:
|
|
37
|
+
export declare function buildUrl<T extends object>(baseUrl: string, params?: T): string;
|
|
17
38
|
/**
|
|
18
39
|
* Common pagination parameters
|
|
19
40
|
*/
|
|
@@ -22,11 +43,32 @@ export interface PaginationParams {
|
|
|
22
43
|
offset?: number;
|
|
23
44
|
}
|
|
24
45
|
/**
|
|
25
|
-
*
|
|
46
|
+
* Ordering for the follow-graph list endpoints (`/users/:id/followers`,
|
|
47
|
+
* `/users/:id/following`, `/users/:id/mutuals`).
|
|
48
|
+
*
|
|
49
|
+
* - `recent` — newest follow edge first (the server default).
|
|
50
|
+
* - `oldest` — oldest follow edge first.
|
|
51
|
+
*/
|
|
52
|
+
export type FollowGraphSort = 'recent' | 'oldest';
|
|
53
|
+
/**
|
|
54
|
+
* Pagination plus the follow-graph ordering.
|
|
55
|
+
*
|
|
56
|
+
* Kept separate from {@link PaginationParams}, which is shared by endpoints
|
|
57
|
+
* that have no `sort` at all.
|
|
58
|
+
*/
|
|
59
|
+
export interface FollowGraphParams extends PaginationParams {
|
|
60
|
+
sort?: FollowGraphSort;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Build pagination query parameters.
|
|
64
|
+
*
|
|
65
|
+
* Returns a plain record — NOT a `URLSearchParams` — because that is the only
|
|
66
|
+
* shape `makeRequest`/`HttpService` can read. See {@link buildQueryParams}.
|
|
67
|
+
*
|
|
26
68
|
* @param params Pagination parameters
|
|
27
|
-
* @returns
|
|
69
|
+
* @returns Query record with pagination
|
|
28
70
|
*/
|
|
29
|
-
export declare function buildPaginationParams(params: PaginationParams):
|
|
71
|
+
export declare function buildPaginationParams(params: PaginationParams): Record<string, string>;
|
|
30
72
|
/**
|
|
31
73
|
* Common API response wrapper
|
|
32
74
|
*/
|
|
@@ -43,10 +43,18 @@ export interface BuildOAuthAuthorizeUrlParams {
|
|
|
43
43
|
/** The PKCE `codeChallenge` from {@link generatePkcePair}. */
|
|
44
44
|
codeChallenge: string;
|
|
45
45
|
/**
|
|
46
|
-
* OAuth `prompt` parameter
|
|
47
|
-
*
|
|
46
|
+
* OAuth `prompt` parameter — `login` forces a fresh authentication even when
|
|
47
|
+
* the IdP already has a session, `consent` forces the consent screen even for
|
|
48
|
+
* an already-granted scope. Both are ordinary OAuth/OIDC and are here for
|
|
49
|
+
* third-party relying parties building their own authorize link.
|
|
50
|
+
*
|
|
51
|
+
* `'none'` is deliberately NOT in this union. It is the silent-SSO value, and
|
|
52
|
+
* it was the only value this SDK ever sent — from the cold-boot cross-origin
|
|
53
|
+
* restore deleted in #691 phase 7b. Accepting it again would hand consumers a
|
|
54
|
+
* one-line rebuild of the automatic, gesture-less full-page bounce to the IdP
|
|
55
|
+
* that the popup transport exists to eliminate.
|
|
48
56
|
*/
|
|
49
|
-
prompt?: '
|
|
57
|
+
prompt?: 'login' | 'consent';
|
|
50
58
|
/**
|
|
51
59
|
* How the IdP should deliver the authorization response. Omitted (the
|
|
52
60
|
* default) means the ordinary top-level redirect back to `redirectUri`.
|
|
@@ -95,10 +103,6 @@ export declare const OXY_OAUTH_STATE_STORAGE_KEY = "oxy_oauth_state";
|
|
|
95
103
|
export declare const OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY = "oxy_oauth_code_verifier";
|
|
96
104
|
/** `sessionStorage` key — the exact `redirect_uri` sent on the authorize request. */
|
|
97
105
|
export declare const OXY_OAUTH_REDIRECT_URI_STORAGE_KEY = "oxy.oauth_redirect_uri";
|
|
98
|
-
/** `sessionStorage` key — at most one silent OAuth attempt per navigation. */
|
|
99
|
-
export declare const OXY_SILENT_OAUTH_ATTEMPTED_KEY = "oxy.silent_oauth_attempted";
|
|
100
|
-
/** `sessionStorage` key — blocks further cross-origin auto-restore in this tab. */
|
|
101
|
-
export declare const OXY_CROSS_ORIGIN_RESTORE_ATTEMPTED_KEY = "oxy.cross_origin_restore_attempted";
|
|
102
106
|
/**
|
|
103
107
|
* `sessionStorage` key for the in-app path to return to after an authorize
|
|
104
108
|
* round trip. See {@link persistOAuthReturnPath}.
|
|
@@ -1,23 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Official first-party web origin allowlist — shared by
|
|
3
|
-
*
|
|
2
|
+
* Official first-party web origin allowlist — shared by OAuth redirect
|
|
3
|
+
* validation and the server-side trusted-origin checks.
|
|
4
4
|
*/
|
|
5
|
-
export declare function buildIdpHubOrigin(): string;
|
|
6
|
-
/** Whether the current web origin is the central IdP hub (`auth.oxy.so`). */
|
|
7
|
-
export declare function isIdpHubOrigin(): boolean;
|
|
8
5
|
/**
|
|
9
6
|
* Whether an origin is a loopback / local-dev origin (`localhost`, `127.0.0.1`,
|
|
10
|
-
* or `[::1]` on any port, http or https).
|
|
11
|
-
* hosted IdP for cross-origin session restore.
|
|
7
|
+
* or `[::1]` on any port, http or https).
|
|
12
8
|
*/
|
|
13
9
|
export declare function isLoopbackOrigin(origin: string): boolean;
|
|
14
10
|
/** Whether an origin belongs to the official Oxy web ecosystem. */
|
|
15
11
|
export declare function isOfficialWebOrigin(origin: string): boolean;
|
|
16
|
-
/** Normalize and validate a return URL against official origins. Returns origin only. */
|
|
17
|
-
export declare function normalizeOfficialReturnOrigin(raw: string): string | null;
|
|
18
|
-
/** Validate a hub-sync return URL; returns the full normalized URL string. */
|
|
19
|
-
export declare function parseHubSyncReturnUrl(raw: string | null): string | null;
|
|
20
|
-
/** Build auth.oxy.so/sync redirect URL with a one-time hub ticket. */
|
|
21
|
-
export declare function buildHubSyncUrl(ticket: string, returnUrl?: string): string;
|
|
22
12
|
/** @deprecated Use {@link isOfficialWebOrigin}. */
|
|
23
13
|
export declare const isAllowedDeviceJoinOrigin: typeof isOfficialWebOrigin;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "13.2.0",
|
|
4
4
|
"description": "OxyHQ SDK Foundation — API client, authentication, cryptographic identity, and shared utilities",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -115,8 +115,8 @@
|
|
|
115
115
|
"dependencies": {
|
|
116
116
|
"@noble/ciphers": "^1.3.0",
|
|
117
117
|
"@noble/hashes": "^1.8.0",
|
|
118
|
-
"@oxyhq/contracts": "^0.
|
|
119
|
-
"@oxyhq/protocol": "^0.1.
|
|
118
|
+
"@oxyhq/contracts": "^0.19.0",
|
|
119
|
+
"@oxyhq/protocol": "^0.1.6",
|
|
120
120
|
"bip39": "^3.1.0",
|
|
121
121
|
"buffer": "^6.0.3",
|
|
122
122
|
"elliptic": "^6.6.1",
|
|
@@ -131,7 +131,8 @@
|
|
|
131
131
|
"expo-crypto": "*",
|
|
132
132
|
"expo-secure-store": "*",
|
|
133
133
|
"express": "^4.0.0",
|
|
134
|
-
"express-rate-limit": "^7.0.0"
|
|
134
|
+
"express-rate-limit": "^7.0.0",
|
|
135
|
+
"helmet": "^8.0.0"
|
|
135
136
|
},
|
|
136
137
|
"peerDependenciesMeta": {
|
|
137
138
|
"@react-native-async-storage/async-storage": {
|
|
@@ -145,6 +146,9 @@
|
|
|
145
146
|
},
|
|
146
147
|
"express": {
|
|
147
148
|
"optional": true
|
|
149
|
+
},
|
|
150
|
+
"helmet": {
|
|
151
|
+
"optional": true
|
|
148
152
|
}
|
|
149
153
|
},
|
|
150
154
|
"devDependencies": {
|
|
@@ -160,6 +164,7 @@
|
|
|
160
164
|
"express-rate-limit": "^8.6.0",
|
|
161
165
|
"regexpu-core": "^6.4.0",
|
|
162
166
|
"release-it": "^19.0.6",
|
|
163
|
-
"typescript": "^5.9.2"
|
|
167
|
+
"typescript": "^5.9.2",
|
|
168
|
+
"helmet": "^8.3.0"
|
|
164
169
|
}
|
|
165
170
|
}
|
package/src/index.ts
CHANGED
|
@@ -59,10 +59,15 @@ export type {
|
|
|
59
59
|
CommonsApprovalInfo,
|
|
60
60
|
CommonsApprovalSubjectAccount,
|
|
61
61
|
CommonsSignInActionResult,
|
|
62
|
-
CommonsDenyReason,
|
|
63
62
|
CommonsOAuthFinalizeResult,
|
|
64
63
|
CommonsDeliveryResult,
|
|
65
64
|
} from './mixins/OxyServices.auth';
|
|
65
|
+
// `denyCommonsSignIn`'s reason parameter is typed by `CommonsDenyReason`, which
|
|
66
|
+
// is a WIRE contract shared with the API (the request schema of
|
|
67
|
+
// `POST /auth/session/deny/:authorizeCode` and the persisted
|
|
68
|
+
// `AuthSession.deniedReason` enum read the same declaration). It lives in
|
|
69
|
+
// `@oxyhq/contracts` and is NOT re-exported here: consumers import API contract
|
|
70
|
+
// types directly from `@oxyhq/contracts`, per the package boundary rule.
|
|
66
71
|
// Push-token registration (Expo push tokens — never raw APNs/FCM device tokens).
|
|
67
72
|
export type {
|
|
68
73
|
PushTokenPlatform,
|
|
@@ -463,6 +468,7 @@ export { translate } from './i18n';
|
|
|
463
468
|
// API request / URL helpers
|
|
464
469
|
// ---------------------------------------------------------------------------
|
|
465
470
|
export {
|
|
471
|
+
buildQueryParams,
|
|
466
472
|
buildSearchParams,
|
|
467
473
|
buildUrl,
|
|
468
474
|
buildPaginationParams,
|
|
@@ -470,6 +476,8 @@ export {
|
|
|
470
476
|
} from './utils/apiUtils';
|
|
471
477
|
export type {
|
|
472
478
|
PaginationParams,
|
|
479
|
+
FollowGraphParams,
|
|
480
|
+
FollowGraphSort,
|
|
473
481
|
ApiResponse,
|
|
474
482
|
ErrorResponse,
|
|
475
483
|
} from './utils/apiUtils';
|
|
@@ -601,8 +609,6 @@ export {
|
|
|
601
609
|
OXY_OAUTH_STATE_STORAGE_KEY,
|
|
602
610
|
OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY,
|
|
603
611
|
OXY_OAUTH_REDIRECT_URI_STORAGE_KEY,
|
|
604
|
-
OXY_SILENT_OAUTH_ATTEMPTED_KEY,
|
|
605
|
-
OXY_CROSS_ORIGIN_RESTORE_ATTEMPTED_KEY,
|
|
606
612
|
OXY_OAUTH_RETURN_PATH_STORAGE_KEY,
|
|
607
613
|
normalizeOAuthRedirectUri,
|
|
608
614
|
canonicalizeOAuthRedirectUri,
|
|
@@ -615,22 +621,11 @@ export {
|
|
|
615
621
|
export type { PkcePair, BuildOAuthAuthorizeUrlParams } from './utils/oauthPkce';
|
|
616
622
|
|
|
617
623
|
export {
|
|
618
|
-
buildIdpHubOrigin,
|
|
619
|
-
buildHubSyncUrl,
|
|
620
|
-
isIdpHubOrigin,
|
|
621
624
|
isLoopbackOrigin,
|
|
622
625
|
isOfficialWebOrigin,
|
|
623
626
|
isAllowedDeviceJoinOrigin,
|
|
624
|
-
normalizeOfficialReturnOrigin,
|
|
625
|
-
parseHubSyncReturnUrl,
|
|
626
627
|
} from './utils/officialOrigins';
|
|
627
628
|
|
|
628
|
-
export {
|
|
629
|
-
syncHubAfterSignIn,
|
|
630
|
-
redeemHubTicketOnHub,
|
|
631
|
-
} from './session/hubSync';
|
|
632
|
-
export type { SyncHubAfterSignInOptions } from './session/hubSync';
|
|
633
|
-
|
|
634
629
|
// ---------------------------------------------------------------------------
|
|
635
630
|
// Session sync (device-scoped multi-account session client)
|
|
636
631
|
// ---------------------------------------------------------------------------
|
|
@@ -4,7 +4,12 @@
|
|
|
4
4
|
* Supports password-based login (email/username) and public key challenge-response.
|
|
5
5
|
*/
|
|
6
6
|
import type { User } from '../models/interfaces';
|
|
7
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
UserNameResponse,
|
|
9
|
+
LoginResult,
|
|
10
|
+
LoginSessionResult,
|
|
11
|
+
CommonsDenyReason,
|
|
12
|
+
} from '@oxyhq/contracts';
|
|
8
13
|
import { loginResultSchema, safeParseContract } from '@oxyhq/contracts';
|
|
9
14
|
import type { SessionLoginResponse } from '../models/session';
|
|
10
15
|
import type { OxyServicesBase } from '../OxyServices.base';
|
|
@@ -336,18 +341,6 @@ export interface CommonsSignInActionResult {
|
|
|
336
341
|
success: boolean;
|
|
337
342
|
}
|
|
338
343
|
|
|
339
|
-
/**
|
|
340
|
-
* Why the approver denied a "Sign in with Oxy" request. A CLOSED set — the deny
|
|
341
|
-
* endpoint is unauthenticated, so it accepts no free-form text:
|
|
342
|
-
*
|
|
343
|
-
* - `'declined'` the approver rejected a request they recognised ("Not now").
|
|
344
|
-
* - `'not_me'` the approver did not start the request ("This wasn't me").
|
|
345
|
-
* The only value that records the denial as suspicious rather
|
|
346
|
-
* than an ordinary cancel, so a UI may only offer it where the
|
|
347
|
-
* user genuinely said so.
|
|
348
|
-
*/
|
|
349
|
-
export type CommonsDenyReason = 'declined' | 'not_me';
|
|
350
|
-
|
|
351
344
|
/**
|
|
352
345
|
* Result of finalizing an approved, OAuth-bound "Sign in with Oxy" request.
|
|
353
346
|
*
|