@oxyhq/core 7.1.1 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +48 -24
  2. package/dist/cjs/.tsbuildinfo +1 -1
  3. package/dist/cjs/HttpService.js +6 -6
  4. package/dist/cjs/i18n/locales/en-US.json +44 -1
  5. package/dist/cjs/i18n/locales/es-ES.json +44 -1
  6. package/dist/cjs/i18n/locales/locales/en-US.json +45 -2
  7. package/dist/cjs/i18n/locales/locales/es-ES.json +45 -2
  8. package/dist/cjs/index.js +19 -16
  9. package/dist/cjs/server/index.js +1 -7
  10. package/dist/cjs/session/accountDialogController.js +1 -1
  11. package/dist/cjs/session/accountProjection.js +1 -1
  12. package/dist/cjs/session/projectSessionState.js +1 -1
  13. package/dist/cjs/session/sessionClientHost.js +1 -2
  14. package/dist/cjs/utils/accountUtils.js +1 -1
  15. package/dist/cjs/utils/oauthPkce.js +142 -0
  16. package/dist/cjs/utils/platform.js +1 -1
  17. package/dist/esm/.tsbuildinfo +1 -1
  18. package/dist/esm/HttpService.js +6 -6
  19. package/dist/esm/i18n/locales/en-US.json +44 -1
  20. package/dist/esm/i18n/locales/es-ES.json +44 -1
  21. package/dist/esm/i18n/locales/locales/en-US.json +45 -2
  22. package/dist/esm/i18n/locales/locales/es-ES.json +45 -2
  23. package/dist/esm/index.js +11 -13
  24. package/dist/esm/server/index.js +0 -5
  25. package/dist/esm/session/accountDialogController.js +1 -1
  26. package/dist/esm/session/accountProjection.js +1 -1
  27. package/dist/esm/session/projectSessionState.js +1 -1
  28. package/dist/esm/session/sessionClientHost.js +1 -2
  29. package/dist/esm/utils/accountUtils.js +1 -1
  30. package/dist/esm/utils/oauthPkce.js +135 -0
  31. package/dist/esm/utils/platform.js +1 -1
  32. package/dist/types/.tsbuildinfo +1 -1
  33. package/dist/types/HttpService.d.ts +1 -1
  34. package/dist/types/index.d.ts +3 -2
  35. package/dist/types/mixins/OxyServices.accounts.d.ts +13 -3
  36. package/dist/types/mixins/OxyServices.connectedApps.d.ts +4 -0
  37. package/dist/types/mixins/OxyServices.devices.d.ts +3 -2
  38. package/dist/types/models/interfaces.d.ts +4 -4
  39. package/dist/types/server/index.d.ts +0 -1
  40. package/dist/types/session/accountDialogController.d.ts +1 -1
  41. package/dist/types/session/accountProjection.d.ts +1 -1
  42. package/dist/types/session/projectSessionState.d.ts +1 -1
  43. package/dist/types/session/refresh.d.ts +4 -8
  44. package/dist/types/session/sessionClientHost.d.ts +1 -2
  45. package/dist/types/utils/accountUtils.d.ts +1 -1
  46. package/dist/types/utils/oauthPkce.d.ts +74 -0
  47. package/dist/types/utils/platform.d.ts +1 -1
  48. package/package.json +3 -3
  49. package/src/HttpService.ts +6 -6
  50. package/src/i18n/locales/en-US.json +45 -2
  51. package/src/i18n/locales/es-ES.json +45 -2
  52. package/src/index.ts +23 -16
  53. package/src/mixins/OxyServices.accounts.ts +12 -0
  54. package/src/mixins/OxyServices.connectedApps.ts +4 -0
  55. package/src/mixins/OxyServices.devices.ts +6 -5
  56. package/src/mixins/__tests__/accounts.test.ts +1 -1
  57. package/src/models/interfaces.ts +7 -5
  58. package/src/server/index.ts +0 -6
  59. package/src/session/accountDialogController.ts +1 -1
  60. package/src/session/accountProjection.ts +1 -1
  61. package/src/session/projectSessionState.ts +1 -1
  62. package/src/session/refresh.ts +4 -8
  63. package/src/session/sessionClientHost.ts +1 -2
  64. package/src/utils/__tests__/coldBoot.test.ts +55 -65
  65. package/src/utils/__tests__/oauthPkce.test.ts +154 -0
  66. package/src/utils/accountUtils.ts +1 -1
  67. package/src/utils/oauthPkce.ts +189 -0
  68. package/src/utils/platform.ts +1 -1
  69. package/dist/cjs/utils/ssoBounce.js +0 -24
  70. package/dist/esm/utils/ssoBounce.js +0 -21
  71. package/dist/types/utils/ssoBounce.d.ts +0 -21
  72. package/src/utils/ssoBounce.ts +0 -22
@@ -85,7 +85,7 @@ export declare class HttpService {
85
85
  private cacheSizeWarningSilentUntil;
86
86
  /**
87
87
  * Fan-out listeners notified on EVERY access-token change on this instance:
88
- * explicit `setTokens`, `clearTokens`, an AuthManager-owned refresh, and the
88
+ * explicit `setTokens`, `clearTokens`, a refresh-handler rotation, and the
89
89
  * internal 401-driven clear. This is a Set so multiple independent observers
90
90
  * can mirror token state without clobbering each other.
91
91
  *
@@ -53,7 +53,7 @@ export { RecoveryPhraseService } from './crypto/recoveryPhrase';
53
53
  export type { RecoveryPhraseResult } from './crypto/recoveryPhrase';
54
54
  export { DeviceManager } from './utils/deviceManager';
55
55
  export type { DeviceFingerprint, StoredDeviceInfo } from './utils/deviceManager';
56
- export type { OxyConfig, PrivacySettings, NotificationPreferences, UserPreferences, User, LoginResponse, Notification, Wallet, Transaction, BlockedUser, RestrictedUser, TransferFundsRequest, PurchaseRequest, WithdrawalRequest, TransactionResponse, PaginationInfo, SearchProfilesResponse, ApiError, PaymentMethod, PaymentRequest, PaymentResponse, AnalyticsData, FollowerDetails, ContentViewer, FileMetadata, FileUploadResponse, FileListResponse, FileUpdateRequest, FileDeleteResponse, RNFileDescriptor, AssetUploadInput, FileVisibility, AssetLink, AssetMetadata, AssetVariant, Asset, AssetInitRequest, AssetInitResponse, AssetCompleteRequest, AssetLinkRequest, AssetUnlinkRequest, AssetUrlResponse, AssetDeleteSummary, AssetUpdateVisibilityRequest, AssetUpdateVisibilityResponse, ServiceAssetMetadata, ServiceAssetMetadataBySha, AccountStorageCategoryUsage, AccountStorageUsageResponse, SecurityEventType, SecurityEventSeverity, SecurityActivity, SecurityActivityResponse, AssetUploadProgress, DeviceSession, DeviceSessionsResponse, DeviceSessionLogoutResponse, UpdateDeviceNameResponse, } from './models/interfaces';
56
+ export type { OxyConfig, PrivacySettings, NotificationPreferences, UserPreferences, User, LoginResponse, Notification, Wallet, Transaction, BlockedUser, RestrictedUser, TransferFundsRequest, PurchaseRequest, WithdrawalRequest, TransactionResponse, PaginationInfo, SearchProfilesResponse, ApiError, PaymentMethod, PaymentRequest, PaymentResponse, AnalyticsData, FollowerDetails, ContentViewer, FileMetadata, FileUploadResponse, FileListResponse, FileUpdateRequest, FileDeleteResponse, RNFileDescriptor, AssetUploadInput, FileVisibility, AssetLink, AssetMetadata, AssetVariant, Asset, AssetInitRequest, AssetInitResponse, AssetCompleteRequest, AssetLinkRequest, AssetUnlinkRequest, AssetUrlResponse, AssetDeleteSummary, AssetUpdateVisibilityRequest, AssetUpdateVisibilityResponse, ServiceAssetMetadata, ServiceAssetMetadataBySha, AccountStorageCategoryUsage, AccountStorageUsageResponse, SecurityEventType, SecurityEventSeverity, SecurityActivity, SecurityActivityResponse, AssetUploadProgress, DeviceLinkedSession, DeviceLinkedSessionsResponse, DeviceLinkedSessionLogoutResponse, UpdateDeviceNameResponse, } from './models/interfaces';
57
57
  export { SECURITY_EVENT_SEVERITY_MAP } from './models/interfaces';
58
58
  export { TopicType, TopicSource } from './models/Topic';
59
59
  export type { TopicData, TopicTranslation } from './models/Topic';
@@ -81,9 +81,10 @@ export { buildAccountsArray, createQuickAccount, getAccountDisplayName, getAccou
81
81
  export type { QuickAccount, DisplayNameUserShape } from './utils/accountUtils';
82
82
  export { registrableApex } from './utils/registrableApex';
83
83
  export { CENTRAL_IDP_APEX } from './utils/authWebUrl';
84
- export { SSO_CALLBACK_PATH } from './utils/ssoBounce';
85
84
  export { runColdBoot } from './utils/coldBoot';
86
85
  export type { ColdBootStep, ColdBootStepResult, ColdBootSession, ColdBootSkip, ColdBootOutcome, RunColdBootOptions, } from './utils/coldBoot';
86
+ export { buildOAuthAuthorizeUrl, computeCodeChallenge, generateOAuthState, generatePkcePair, DEFAULT_OAUTH_SCOPE, OXY_AUTHORIZE_URL, } from './utils/oauthPkce';
87
+ export type { PkcePair, BuildOAuthAuthorizeUrlParams } from './utils/oauthPkce';
87
88
  export { SessionClient } from './session/SessionClient';
88
89
  export type { TokenTransport, SessionClientHost, SessionClientOptions } from './session/SessionClient';
89
90
  export type { SocketIOFactory, MinimalSocket } from './session/socketLoader';
@@ -250,6 +250,10 @@ export interface Application {
250
250
  name: string;
251
251
  description?: string;
252
252
  websiteUrl?: string;
253
+ /** Public privacy-policy URL, rendered as a legal link on the OAuth consent screen. */
254
+ privacyPolicyUrl?: string;
255
+ /** Public terms-of-service URL, rendered as a legal link on the OAuth consent screen. */
256
+ termsUrl?: string;
253
257
  icon?: string;
254
258
  type: ApplicationType;
255
259
  status: ApplicationStatus;
@@ -306,6 +310,10 @@ export interface CreateApplicationInput {
306
310
  name: string;
307
311
  description?: string;
308
312
  websiteUrl?: string;
313
+ /** Public privacy-policy URL (absolute `https://`). Shown on the OAuth consent screen. */
314
+ privacyPolicyUrl?: string;
315
+ /** Public terms-of-service URL (absolute `https://`). Shown on the OAuth consent screen. */
316
+ termsUrl?: string;
309
317
  icon?: string;
310
318
  redirectUris?: string[];
311
319
  scopes?: string[];
@@ -320,6 +328,10 @@ export interface UpdateApplicationInput {
320
328
  name?: string;
321
329
  description?: string;
322
330
  websiteUrl?: string;
331
+ /** Public privacy-policy URL (absolute `https://`, or `''` to clear). Shown on the OAuth consent screen. */
332
+ privacyPolicyUrl?: string;
333
+ /** Public terms-of-service URL (absolute `https://`, or `''` to clear). Shown on the OAuth consent screen. */
334
+ termsUrl?: string;
323
335
  icon?: string;
324
336
  redirectUris?: string[];
325
337
  scopes?: string[];
@@ -689,9 +701,7 @@ export declare function OxyServicesAccountsMixin<T extends typeof OxyServicesBas
689
701
  handleError(error: unknown): Error;
690
702
  healthCheck(): Promise<{
691
703
  status: string;
692
- users
693
- /** Aggregate totals for an application over the requested period. */
694
- ? /** Aggregate totals for an application over the requested period. */: number;
704
+ users?: number;
695
705
  timestamp?: string;
696
706
  [key: string]: any;
697
707
  }>;
@@ -37,6 +37,10 @@ export interface PublicApplication {
37
37
  icon?: string;
38
38
  /** Optional public website/homepage URL for the application. */
39
39
  websiteUrl?: string;
40
+ /** Optional public privacy-policy URL, rendered as a legal link on the consent screen. */
41
+ privacyPolicyUrl?: string;
42
+ /** Optional public terms-of-service URL, rendered as a legal link on the consent screen. */
43
+ termsUrl?: string;
40
44
  /** Application classification (set by Oxy platform staff). */
41
45
  type: ApplicationType;
42
46
  /** Whether the application is an officially endorsed Oxy application. */
@@ -2,6 +2,7 @@
2
2
  * Device Methods Mixin
3
3
  */
4
4
  import type { OxyServicesBase } from '../OxyServices.base';
5
+ import type { DeviceLinkedSession, DeviceLinkedSessionLogoutResponse } from '../models/interfaces';
5
6
  export declare function OxyServicesDevicesMixin<T extends typeof OxyServicesBase>(Base: T): {
6
7
  new (...args: any[]): {
7
8
  /**
@@ -26,7 +27,7 @@ export declare function OxyServicesDevicesMixin<T extends typeof OxyServicesBase
26
27
  * @param sessionId - The session ID
27
28
  * @returns Array of device sessions
28
29
  */
29
- getDeviceSessions(sessionId: string): Promise<any[]>;
30
+ getDeviceSessions(sessionId: string): Promise<DeviceLinkedSession[]>;
30
31
  /**
31
32
  * Logout all device sessions
32
33
  * @param sessionId - The session ID
@@ -34,7 +35,7 @@ export declare function OxyServicesDevicesMixin<T extends typeof OxyServicesBase
34
35
  * @param excludeCurrent - Whether to exclude the current session
35
36
  * @returns Logout result
36
37
  */
37
- logoutAllDeviceSessions(sessionId: string, deviceId?: string, excludeCurrent?: boolean): Promise<any>;
38
+ logoutAllDeviceSessions(sessionId: string, deviceId?: string, excludeCurrent?: boolean): Promise<DeviceLinkedSessionLogoutResponse>;
38
39
  /**
39
40
  * Update device name
40
41
  * @param sessionId - The session ID
@@ -545,7 +545,7 @@ export interface AssetUploadProgress {
545
545
  status: 'uploading' | 'processing' | 'complete' | 'error';
546
546
  error?: string;
547
547
  }
548
- export interface DeviceSession {
548
+ export interface DeviceLinkedSession {
549
549
  sessionId: string;
550
550
  deviceId: string;
551
551
  deviceName: string;
@@ -556,11 +556,11 @@ export interface DeviceSession {
556
556
  user?: User;
557
557
  createdAt?: string;
558
558
  }
559
- export interface DeviceSessionsResponse {
559
+ export interface DeviceLinkedSessionsResponse {
560
560
  deviceId: string;
561
- sessions: DeviceSession[];
561
+ sessions: DeviceLinkedSession[];
562
562
  }
563
- export interface DeviceSessionLogoutResponse {
563
+ export interface DeviceLinkedSessionLogoutResponse {
564
564
  message: string;
565
565
  deviceId: string;
566
566
  sessionsTerminated: number;
@@ -24,4 +24,3 @@ export { createOxyCors } from './cors';
24
24
  export type { OxyCorsOptions } from './cors';
25
25
  export { verifySecret } from './verifySecret';
26
26
  export { registrableApex } from '../utils/registrableApex';
27
- export { SSO_CALLBACK_PATH } from '../utils/ssoBounce';
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * A framework-agnostic state machine + subscribe/getSnapshot store (the same
5
5
  * pattern {@link SessionClient} uses — no React, no RN) that both
6
- * `@oxyhq/services` (RN `OxyProvider`) and `@oxyhq/auth` (web `WebOxyProvider`)
6
+ * every `OxyProvider` platform variant (Expo/RN and RN-Web)
7
7
  * bind to via `useSyncExternalStore`, so the account chooser is ONE
8
8
  * implementation across the ecosystem instead of the five drifting copies it
9
9
  * replaces.
@@ -5,7 +5,7 @@
5
5
  * merging the device's server-authoritative session set (`DeviceSessionState`
6
6
  * from {@link SessionClient}) with the caller's account graph (`AccountNode[]`
7
7
  * from `oxyServices.listAccounts()`), deduped by `accountId`. This lives in
8
- * `@oxyhq/core` so `@oxyhq/services` (RN) and `@oxyhq/auth` (web) — and
8
+ * `@oxyhq/core` so every `@oxyhq/services` platform variant — and
9
9
  * `auth.oxy.so` — all render the SAME list from the SAME logic and cannot
10
10
  * diverge.
11
11
  *
@@ -4,7 +4,7 @@ import type { User } from '../models/interfaces';
4
4
  /**
5
5
  * Pure projection helpers: `DeviceSessionState` (the device-scoped
6
6
  * multi-account session-sync state produced by `SessionClient`) -> the
7
- * shapes consumers (`@oxyhq/services`, `@oxyhq/auth`) render today
7
+ * shapes `@oxyhq/services` consumers render today
8
8
  * (`ClientSession[]`, an active session id, an active `User`).
9
9
  *
10
10
  * No I/O. The caller fetches profiles via
@@ -1,11 +1,8 @@
1
1
  /**
2
2
  * Unified token refresh — THE single refresh implementation for web + native.
3
3
  *
4
- * Before device-first, refresh was duplicated: `@oxyhq/auth`'s
5
- * `session/tokenRefresh.ts` (per-apex `/auth/silent` iframe) and
6
- * `@oxyhq/services`'s `inSessionTokenRefresh.ts` (native shared-key). This
7
- * module replaces both with ONE persisted-refresh-token rotation shared by
8
- * every consumer:
4
+ * ONE persisted-refresh-token rotation shared by every consumer (it replaced
5
+ * the pre-device-first per-platform duplicates):
9
6
  *
10
7
  * - `refreshPersistedSession` — arm 1 rotates the stored refresh-token family
11
8
  * (`POST /auth/refresh-token`), planting + persisting the rotated pair; arm 2
@@ -16,9 +13,8 @@
16
13
  * - `createAuthRefreshHandler` / `installAuthRefreshHandler` wire arm 1+2 into
17
14
  * `HttpService.setAuthRefreshHandler`, keeping that layer's single-flight
18
15
  * dedup + cooldown (this module does NOT reimplement them).
19
- * - `startTokenRefreshScheduler` — a proactive scheduler (lifted from the
20
- * better of the two prior duplicates, `@oxyhq/auth`'s `tokenRefresh.ts`),
21
- * decoupled from any React / auth-sdk type: refreshes ~60s before `exp`,
16
+ * - `startTokenRefreshScheduler` — a proactive scheduler decoupled from any
17
+ * React type: refreshes ~60s before `exp`,
22
18
  * re-arms on token change + web tab-focus, `.unref?.()`s its timer in Node.
23
19
  *
24
20
  * Framework-free; no module-level mutable state.
@@ -6,8 +6,7 @@ import type { SessionClientHost } from './SessionClient';
6
6
  * `SessionClient` is host-agnostic: it only needs a REST + token surface.
7
7
  * `OxyServices` already exposes all of that except `getCurrentAccountId`,
8
8
  * which has no direct equivalent — the adapter holds a mutable ref set by
9
- * the caller (`OxyContext` in `@oxyhq/services`, `WebOxyProvider` in
10
- * `@oxyhq/auth`) via `setCurrentAccountId`.
9
+ * the caller (`OxyContext` in `@oxyhq/services`) via `setCurrentAccountId`.
11
10
  *
12
11
  * Shared here (rather than duplicated per consumer) because it is entirely
13
12
  * platform-agnostic: every method it calls exists identically on
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Shared account types and pure helper functions.
3
- * Used by both @oxyhq/services (React Native) and @oxyhq/auth (Web) account stores.
3
+ * Used by the @oxyhq/services account stores (Expo/RN and RN-Web).
4
4
  */
5
5
  export interface QuickAccount {
6
6
  sessionId: string;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * OAuth 2.0 Authorization Code + PKCE helpers for "Sign in with Oxy" third-party
3
+ * sign-in.
4
+ *
5
+ * Third-party Relying Parties (SPAs, static sites, and native apps that are NOT
6
+ * Oxy first-party) authenticate through the standard OAuth flow against
7
+ * `auth.oxy.so/authorize` — never FedCM, SSO bounces, or Oxy session cookies.
8
+ * Public clients (no secret) prove possession of the authorization code with
9
+ * PKCE (RFC 7636, S256): the RP generates a random `code_verifier`, sends its
10
+ * `code_challenge = BASE64URL(SHA-256(code_verifier))` on the authorize
11
+ * redirect, and later replays the raw verifier on the token exchange.
12
+ *
13
+ * All cross-platform crypto (random bytes, SHA-256) is delegated to the shared
14
+ * `@oxyhq/protocol` platform loaders — the exact primitives the rest of core's
15
+ * crypto already uses — so these helpers run identically on web, Node, and
16
+ * React Native. No `require()`, so the ESM build stays bundler-clean.
17
+ */
18
+ /** The central Oxy IdP authorization endpoint used by default. */
19
+ export declare const OXY_AUTHORIZE_URL = "https://auth.oxy.so/authorize";
20
+ /** Default OAuth scope requested for a "Sign in with Oxy" third-party flow. */
21
+ export declare const DEFAULT_OAUTH_SCOPE = "openid profile";
22
+ /** A generated PKCE verifier/challenge pair. */
23
+ export interface PkcePair {
24
+ /** The high-entropy secret replayed on the token exchange (kept client-side). */
25
+ codeVerifier: string;
26
+ /** `BASE64URL(SHA-256(codeVerifier))` — sent on the authorize redirect. */
27
+ codeChallenge: string;
28
+ /** The PKCE transformation method. Always `S256`. */
29
+ method: 'S256';
30
+ }
31
+ /** Parameters for {@link buildOAuthAuthorizeUrl}. */
32
+ export interface BuildOAuthAuthorizeUrlParams {
33
+ /** Authorize endpoint; defaults to {@link OXY_AUTHORIZE_URL}. */
34
+ authorizeBaseUrl?: string;
35
+ /** The registered `ApplicationCredential` public key (`oxy_dk_…`). */
36
+ clientId: string;
37
+ /** Exact registered redirect URI to return the authorization code to. */
38
+ redirectUri: string;
39
+ /** Requested scope; defaults to {@link DEFAULT_OAUTH_SCOPE}. */
40
+ scope?: string;
41
+ /** Opaque CSRF token from {@link generateOAuthState}. */
42
+ state: string;
43
+ /** The PKCE `codeChallenge` from {@link generatePkcePair}. */
44
+ codeChallenge: string;
45
+ }
46
+ /**
47
+ * Compute the PKCE S256 `code_challenge` for a given verifier:
48
+ * `BASE64URL(SHA-256(ASCII(codeVerifier)))` (RFC 7636 §4.2). The verifier is
49
+ * base64url (ASCII), so its UTF-8 and ASCII byte encodings are identical.
50
+ *
51
+ * Reuses `@oxyhq/protocol`'s cross-platform {@link sha256} (which returns
52
+ * lowercase hex); the digest bytes are recovered and re-encoded as base64url.
53
+ */
54
+ export declare function computeCodeChallenge(codeVerifier: string): Promise<string>;
55
+ /**
56
+ * Generate a fresh PKCE verifier/challenge pair for an OAuth authorization-code
57
+ * flow. The verifier is 64 random bytes as base64url (86 chars, within RFC 7636
58
+ * §4.1's 43–128 range and drawn only from the unreserved set); the challenge is
59
+ * its S256 transform.
60
+ */
61
+ export declare function generatePkcePair(): Promise<PkcePair>;
62
+ /**
63
+ * Generate an opaque, single-use OAuth `state` token (32 random bytes as
64
+ * base64url) for CSRF protection across the authorize redirect.
65
+ */
66
+ export declare function generateOAuthState(): Promise<string>;
67
+ /**
68
+ * Build the `auth.oxy.so/authorize` redirect URL for an OAuth authorization-code
69
+ * + PKCE (S256) flow. Built via the WHATWG `URL` API so a custom
70
+ * `authorizeBaseUrl` that already carries a query string keeps its existing
71
+ * params (the OAuth params are merged in, not clobbered by a naive `?` concat).
72
+ * All values are percent-encoded by `URL.searchParams`.
73
+ */
74
+ export declare function buildOAuthAuthorizeUrl(params: BuildOAuthAuthorizeUrlParams): string;
@@ -39,7 +39,7 @@ export declare function setPlatformOS(os: PlatformOS): void;
39
39
  *
40
40
  * Native defines a global `window` but no `document`, so the DOM probe — not a
41
41
  * bare `window` check — is the reliable discriminator. This is the single
42
- * source of truth consumed by `@oxyhq/services` and `@oxyhq/auth` (both dropped
42
+ * source of truth consumed by `@oxyhq/services` (which dropped
43
43
  * their local copies), so every consumer shares the exact same predicate.
44
44
  *
45
45
  * NOTE: this is a live runtime probe (not the cached `getPlatformOS()` verdict)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/core",
3
- "version": "7.1.1",
3
+ "version": "8.0.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",
@@ -94,8 +94,8 @@
94
94
  }
95
95
  },
96
96
  "dependencies": {
97
- "@oxyhq/contracts": "^0.10.0",
98
- "@oxyhq/protocol": "^0.1.1",
97
+ "@oxyhq/contracts": "^0.11.0",
98
+ "@oxyhq/protocol": "^0.1.2",
99
99
  "bip39": "^3.1.0",
100
100
  "buffer": "^6.0.3",
101
101
  "elliptic": "^6.6.1",
@@ -128,7 +128,7 @@ const CSRF_FETCH_RETRY_DELAY_MS = 500;
128
128
  /**
129
129
  * Cooldown (ms) applied after a failed access-token refresh before another
130
130
  * refresh is attempted. Prevents a refresh storm (and server hammering) when
131
- * the AuthManager's refresh handler is failing — every in-flight request that
131
+ * the auth refresh handler is failing — every in-flight request that
132
132
  * hits a 401 would otherwise trigger its own refresh.
133
133
  */
134
134
  const TOKEN_REFRESH_COOLDOWN_MS = 15000;
@@ -244,7 +244,7 @@ export class HttpService {
244
244
 
245
245
  /**
246
246
  * Fan-out listeners notified on EVERY access-token change on this instance:
247
- * explicit `setTokens`, `clearTokens`, an AuthManager-owned refresh, and the
247
+ * explicit `setTokens`, `clearTokens`, a refresh-handler rotation, and the
248
248
  * internal 401-driven clear. This is a Set so multiple independent observers
249
249
  * can mirror token state without clobbering each other.
250
250
  *
@@ -527,9 +527,9 @@ export class HttpService {
527
527
 
528
528
  // Handle response
529
529
  if (!response.ok) {
530
- // On 401, delegate refresh to AuthManager and retry once before
531
- // giving up. HttpService deliberately does not know any session
532
- // routes; the AuthManager is the single session authority.
530
+ // On 401, delegate to the installed auth refresh handler and retry
531
+ // once before giving up. HttpService deliberately does not know any
532
+ // session routes; the refresh handler owns session rotation.
533
533
  if (response.status === 401 && !config._isAuthRetry && !config.skipAuth) {
534
534
  const refreshed = await this.refreshAccessToken('response-401');
535
535
  if (refreshed) {
@@ -1045,7 +1045,7 @@ export class HttpService {
1045
1045
  this.tokenStore.setTokens(newToken);
1046
1046
  this.notifyTokenChange();
1047
1047
  }
1048
- this.logger.debug('Token refreshed via AuthManager');
1048
+ this.logger.debug('Token refreshed via the auth refresh handler');
1049
1049
  return newToken;
1050
1050
  })
1051
1051
  .catch((error) => {
@@ -384,7 +384,11 @@
384
384
  "subtitle": "Add another account to switch between them quickly"
385
385
  },
386
386
  "label": "Account switcher",
387
- "searchPlaceholder": "Search accounts"
387
+ "searchPlaceholder": "Search accounts",
388
+ "scanTitle": "Scan to sign in",
389
+ "scanSubtitle": "Open the Oxy app on your phone and scan this code",
390
+ "scanWithOxy": "Scan with the Oxy app to continue",
391
+ "scanQr": "Scan QR code"
388
392
  },
389
393
  "reputation": {
390
394
  "faq": {
@@ -1905,5 +1909,44 @@
1905
1909
  "earlyAdopter": "Early Adopter",
1906
1910
  "earlyAdopterDesc": "Been part of the community from the start"
1907
1911
  }
1912
+ },
1913
+ "consent": {
1914
+ "title": "Continue to {{app}}",
1915
+ "subtitle": "Use your Oxy account to sign in to {{app}}. Review what this connection means before you continue.",
1916
+ "provenance": {
1917
+ "title": "Who is requesting access",
1918
+ "official": "Official Oxy application",
1919
+ "developer": "Published by {{developer}}",
1920
+ "thirdParty": "Third-party application"
1921
+ },
1922
+ "permissions": {
1923
+ "title": "Permissions requested",
1924
+ "basic": "Sign you in and read your basic profile"
1925
+ },
1926
+ "scopes": {
1927
+ "openid": "Confirm your identity",
1928
+ "profile": "Read your basic profile",
1929
+ "email": "Read your email address",
1930
+ "offlineAccess": "Keep you signed in when you're away",
1931
+ "userRead": "Read your basic profile",
1932
+ "filesRead": "Read your files",
1933
+ "filesWrite": "Upload and modify your files",
1934
+ "filesDelete": "Delete your files",
1935
+ "webhooksReceive": "Receive webhooks",
1936
+ "chatCompletions": "Use AI chat on your behalf",
1937
+ "modelsRead": "List available AI models",
1938
+ "federationWrite": "Act across federated services"
1939
+ },
1940
+ "account": {
1941
+ "title": "Signing in as"
1942
+ },
1943
+ "links": {
1944
+ "website": "Website",
1945
+ "privacy": "Privacy Policy",
1946
+ "terms": "Terms of Service"
1947
+ },
1948
+ "allow": "Continue to {{app}}",
1949
+ "deny": "Cancel",
1950
+ "disclaimer": "By continuing, {{app}} will be able to sign in with your Oxy account. You can manage connected apps anytime in your Oxy account settings."
1908
1951
  }
1909
- }
1952
+ }
@@ -581,7 +581,11 @@
581
581
  "subtitle": "Añade otra cuenta para cambiar rápidamente entre ellas"
582
582
  },
583
583
  "label": "Selector de cuentas",
584
- "searchPlaceholder": "Buscar cuentas"
584
+ "searchPlaceholder": "Buscar cuentas",
585
+ "scanTitle": "Escanea para iniciar sesión",
586
+ "scanSubtitle": "Abre la app de Oxy en tu móvil y escanea este código",
587
+ "scanWithOxy": "Escanea con la app de Oxy para continuar",
588
+ "scanQr": "Escanear código QR"
585
589
  },
586
590
  "feedback": {
587
591
  "type": {
@@ -1905,5 +1909,44 @@
1905
1909
  "earlyAdopter": "Pionero",
1906
1910
  "earlyAdopterDesc": "Has formado parte de la comunidad desde el principio"
1907
1911
  }
1912
+ },
1913
+ "consent": {
1914
+ "title": "Continuar a {{app}}",
1915
+ "subtitle": "Usa tu cuenta de Oxy para iniciar sesión en {{app}}. Revisa qué implica esta conexión antes de continuar.",
1916
+ "provenance": {
1917
+ "title": "Quién solicita acceso",
1918
+ "official": "Aplicación oficial de Oxy",
1919
+ "developer": "Publicada por {{developer}}",
1920
+ "thirdParty": "Aplicación de terceros"
1921
+ },
1922
+ "permissions": {
1923
+ "title": "Permisos solicitados",
1924
+ "basic": "Iniciar sesión y leer tu perfil básico"
1925
+ },
1926
+ "scopes": {
1927
+ "openid": "Confirmar tu identidad",
1928
+ "profile": "Leer tu perfil básico",
1929
+ "email": "Leer tu dirección de correo",
1930
+ "offlineAccess": "Mantener tu sesión iniciada cuando no estés",
1931
+ "userRead": "Leer tu perfil básico",
1932
+ "filesRead": "Leer tus archivos",
1933
+ "filesWrite": "Subir y modificar tus archivos",
1934
+ "filesDelete": "Eliminar tus archivos",
1935
+ "webhooksReceive": "Recibir webhooks",
1936
+ "chatCompletions": "Usar el chat con IA en tu nombre",
1937
+ "modelsRead": "Ver los modelos de IA disponibles",
1938
+ "federationWrite": "Actuar en servicios federados"
1939
+ },
1940
+ "account": {
1941
+ "title": "Iniciando sesión como"
1942
+ },
1943
+ "links": {
1944
+ "website": "Sitio web",
1945
+ "privacy": "Política de privacidad",
1946
+ "terms": "Términos del servicio"
1947
+ },
1948
+ "allow": "Continuar a {{app}}",
1949
+ "deny": "Cancelar",
1950
+ "disclaimer": "Al continuar, {{app}} podrá iniciar sesión con tu cuenta de Oxy. Puedes gestionar las apps conectadas cuando quieras en los ajustes de tu cuenta de Oxy."
1908
1951
  }
1909
- }
1952
+ }
package/src/index.ts CHANGED
@@ -304,9 +304,9 @@ export type {
304
304
  SecurityActivity,
305
305
  SecurityActivityResponse,
306
306
  AssetUploadProgress,
307
- DeviceSession,
308
- DeviceSessionsResponse,
309
- DeviceSessionLogoutResponse,
307
+ DeviceLinkedSession,
308
+ DeviceLinkedSessionsResponse,
309
+ DeviceLinkedSessionLogoutResponse,
310
310
  UpdateDeviceNameResponse,
311
311
  } from './models/interfaces';
312
312
  export { SECURITY_EVENT_SEVERITY_MAP } from './models/interfaces';
@@ -494,20 +494,13 @@ export type { QuickAccount, DisplayNameUserShape } from './utils/accountUtils';
494
494
  // ---------------------------------------------------------------------------
495
495
  // Registrable-domain + central-IdP-apex helpers.
496
496
  //
497
- // The client SSO-bounce / silent-iframe / FedCM machinery was removed in the
498
- // device-first cutover (wave 2, ecosystem-wide bump complete). `registrableApex`
499
- // (eTLD+1) and `CENTRAL_IDP_APEX` are still genuinely used: `registrableApex`
500
- // via the `@oxyhq/core/server` re-export consumed by
501
- // `packages/api/src/utils/sameSite.ts` for same-site origin checks, and
502
- // `CENTRAL_IDP_APEX` by `server/cors.ts`'s `createOxyCors` (auto-allows
503
- // `*.oxy.so`). `SSO_CALLBACK_PATH` has no remaining importer outside this
504
- // module as of wave 2 — kept exported for now rather than removed here (an
505
- // export deletion is a logic change, out of scope for a comment sweep); flag
506
- // for a follow-up dead-export cleanup pass.
497
+ // `registrableApex` (eTLD+1) is consumed via the `@oxyhq/core/server`
498
+ // re-export by `packages/api/src/utils/sameSite.ts` for same-site origin
499
+ // checks; `CENTRAL_IDP_APEX` by `server/cors.ts`'s `createOxyCors` (auto-allows
500
+ // `*.oxy.so`).
507
501
  // ---------------------------------------------------------------------------
508
502
  export { registrableApex } from './utils/registrableApex';
509
503
  export { CENTRAL_IDP_APEX } from './utils/authWebUrl';
510
- export { SSO_CALLBACK_PATH } from './utils/ssoBounce';
511
504
 
512
505
  export { runColdBoot } from './utils/coldBoot';
513
506
  export type {
@@ -519,6 +512,20 @@ export type {
519
512
  RunColdBootOptions,
520
513
  } from './utils/coldBoot';
521
514
 
515
+ // ---------------------------------------------------------------------------
516
+ // OAuth 2.0 Authorization Code + PKCE helpers ("Sign in with Oxy" third party).
517
+ // Standard OAuth against auth.oxy.so/authorize — no FedCM/cookies/SSO bounce.
518
+ // ---------------------------------------------------------------------------
519
+ export {
520
+ buildOAuthAuthorizeUrl,
521
+ computeCodeChallenge,
522
+ generateOAuthState,
523
+ generatePkcePair,
524
+ DEFAULT_OAUTH_SCOPE,
525
+ OXY_AUTHORIZE_URL,
526
+ } from './utils/oauthPkce';
527
+ export type { PkcePair, BuildOAuthAuthorizeUrlParams } from './utils/oauthPkce';
528
+
522
529
  // ---------------------------------------------------------------------------
523
530
  // Session sync (device-scoped multi-account session client)
524
531
  // ---------------------------------------------------------------------------
@@ -531,7 +538,7 @@ export type { SocketIOFactory, MinimalSocket } from './session/socketLoader';
531
538
 
532
539
  // Shared SessionClient integration layer: the host adapter, the pure
533
540
  // DeviceSessionState projection helpers, and the client factory are defined
534
- // ONCE here so `@oxyhq/services` and `@oxyhq/auth` both reuse them instead of
541
+ // ONCE here so every `@oxyhq/services` platform variant reuses them instead of
535
542
  // duplicating a local copy. Each consumer supplies its own `TokenTransport`
536
543
  // (native vs. web mint strategies differ) to `createSessionClient`.
537
544
  export { createSessionClientHost } from './session/sessionClientHost';
@@ -546,7 +553,7 @@ export {
546
553
  // Unified account-list projection (THE single source of truth for the account
547
554
  // chooser: device sign-ins ∪ account graph, deduped by accountId). Pure +
548
555
  // I/O-free — the caller hydrates profiles via `getUsersByIds`. Shared by
549
- // `@oxyhq/services`, `@oxyhq/auth`, and auth.oxy.so so the list can't diverge.
556
+ // `@oxyhq/services` and auth.oxy.so so the list can't diverge.
550
557
  export {
551
558
  projectSwitchableAccounts,
552
559
  switchableAccountIds,
@@ -284,6 +284,10 @@ export interface Application {
284
284
  name: string;
285
285
  description?: string;
286
286
  websiteUrl?: string;
287
+ /** Public privacy-policy URL, rendered as a legal link on the OAuth consent screen. */
288
+ privacyPolicyUrl?: string;
289
+ /** Public terms-of-service URL, rendered as a legal link on the OAuth consent screen. */
290
+ termsUrl?: string;
287
291
  icon?: string;
288
292
  type: ApplicationType;
289
293
  status: ApplicationStatus;
@@ -342,6 +346,10 @@ export interface CreateApplicationInput {
342
346
  name: string;
343
347
  description?: string;
344
348
  websiteUrl?: string;
349
+ /** Public privacy-policy URL (absolute `https://`). Shown on the OAuth consent screen. */
350
+ privacyPolicyUrl?: string;
351
+ /** Public terms-of-service URL (absolute `https://`). Shown on the OAuth consent screen. */
352
+ termsUrl?: string;
345
353
  icon?: string;
346
354
  redirectUris?: string[];
347
355
  scopes?: string[];
@@ -357,6 +365,10 @@ export interface UpdateApplicationInput {
357
365
  name?: string;
358
366
  description?: string;
359
367
  websiteUrl?: string;
368
+ /** Public privacy-policy URL (absolute `https://`, or `''` to clear). Shown on the OAuth consent screen. */
369
+ privacyPolicyUrl?: string;
370
+ /** Public terms-of-service URL (absolute `https://`, or `''` to clear). Shown on the OAuth consent screen. */
371
+ termsUrl?: string;
360
372
  icon?: string;
361
373
  redirectUris?: string[];
362
374
  scopes?: string[];
@@ -41,6 +41,10 @@ export interface PublicApplication {
41
41
  icon?: string;
42
42
  /** Optional public website/homepage URL for the application. */
43
43
  websiteUrl?: string;
44
+ /** Optional public privacy-policy URL, rendered as a legal link on the consent screen. */
45
+ privacyPolicyUrl?: string;
46
+ /** Optional public terms-of-service URL, rendered as a legal link on the consent screen. */
47
+ termsUrl?: string;
44
48
  /** Application classification (set by Oxy platform staff). */
45
49
  type: ApplicationType;
46
50
  /** Whether the application is an officially endorsed Oxy application. */