@oxyhq/core 3.10.1 → 3.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/AuthManager.js +9 -2
- package/dist/cjs/HttpService.js +27 -9
- package/dist/cjs/OxyServices.base.js +3 -2
- package/dist/cjs/crypto/canonicalJson.js +107 -0
- package/dist/cjs/crypto/keyManager.js +67 -8
- package/dist/cjs/crypto/signatureService.js +189 -0
- package/dist/cjs/index.js +30 -4
- package/dist/cjs/mixins/OxyServices.assets.js +16 -1
- package/dist/cjs/mixins/OxyServices.auth.js +190 -1
- package/dist/cjs/mixins/OxyServices.civic.js +611 -0
- package/dist/cjs/mixins/OxyServices.identity.js +291 -0
- package/dist/cjs/mixins/OxyServices.sso.js +28 -1
- package/dist/cjs/mixins/OxyServices.user.js +1 -0
- package/dist/cjs/mixins/index.js +6 -0
- package/dist/cjs/server/cors.js +20 -21
- package/dist/cjs/server/rateLimit.js +32 -8
- package/dist/cjs/utils/profileLinks.js +52 -0
- package/dist/cjs/utils/ssoReturn.js +1 -1
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/AuthManager.js +9 -2
- package/dist/esm/HttpService.js +27 -9
- package/dist/esm/OxyServices.base.js +3 -2
- package/dist/esm/crypto/canonicalJson.js +104 -0
- package/dist/esm/crypto/keyManager.js +67 -8
- package/dist/esm/crypto/signatureService.js +187 -0
- package/dist/esm/index.js +19 -1
- package/dist/esm/mixins/OxyServices.assets.js +16 -1
- package/dist/esm/mixins/OxyServices.auth.js +190 -1
- package/dist/esm/mixins/OxyServices.civic.js +605 -0
- package/dist/esm/mixins/OxyServices.identity.js +287 -0
- package/dist/esm/mixins/OxyServices.sso.js +28 -1
- package/dist/esm/mixins/OxyServices.user.js +1 -0
- package/dist/esm/mixins/index.js +6 -0
- package/dist/esm/server/cors.js +20 -21
- package/dist/esm/server/rateLimit.js +32 -8
- package/dist/esm/utils/profileLinks.js +49 -0
- package/dist/esm/utils/ssoReturn.js +1 -1
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/HttpService.d.ts +3 -0
- package/dist/types/OxyServices.d.ts +2 -2
- package/dist/types/crypto/canonicalJson.d.ts +44 -0
- package/dist/types/crypto/keyManager.d.ts +7 -0
- package/dist/types/crypto/signatureService.d.ts +112 -0
- package/dist/types/index.d.ts +10 -2
- package/dist/types/mixins/OxyServices.auth.d.ts +136 -0
- package/dist/types/mixins/OxyServices.civic.d.ts +512 -0
- package/dist/types/mixins/OxyServices.identity.d.ts +249 -0
- package/dist/types/mixins/OxyServices.sso.d.ts +4 -1
- package/dist/types/mixins/index.d.ts +3 -1
- package/dist/types/models/interfaces.d.ts +3 -0
- package/dist/types/server/cors.d.ts +5 -5
- package/dist/types/utils/profileLinks.d.ts +36 -0
- package/dist/types/utils/ssoReturn.d.ts +1 -1
- package/package.json +2 -2
- package/src/AuthManager.ts +8 -2
- package/src/HttpService.ts +36 -8
- package/src/OxyServices.base.ts +3 -2
- package/src/OxyServices.ts +1 -1
- package/src/__tests__/authManager.security.test.ts +31 -0
- package/src/__tests__/httpServiceCsrf.test.ts +75 -0
- package/src/crypto/__tests__/canonicalJson.test.ts +116 -0
- package/src/crypto/__tests__/keyManager.atomicity.test.ts +41 -2
- package/src/crypto/__tests__/signChallengeShared.test.ts +64 -0
- package/src/crypto/__tests__/signedRecord.test.ts +345 -0
- package/src/crypto/canonicalJson.ts +120 -0
- package/src/crypto/keyManager.ts +62 -12
- package/src/crypto/signatureService.ts +225 -0
- package/src/index.ts +55 -2
- package/src/mixins/OxyServices.assets.ts +16 -1
- package/src/mixins/OxyServices.auth.ts +309 -1
- package/src/mixins/OxyServices.civic.ts +956 -0
- package/src/mixins/OxyServices.identity.ts +445 -0
- package/src/mixins/OxyServices.sso.ts +30 -1
- package/src/mixins/OxyServices.user.ts +1 -0
- package/src/mixins/__tests__/OxyServices.civic.test.ts +1097 -0
- package/src/mixins/__tests__/OxyServices.identity.test.ts +364 -0
- package/src/mixins/__tests__/assetCredentials.test.ts +47 -0
- package/src/mixins/__tests__/commonsSignIn.test.ts +277 -0
- package/src/mixins/__tests__/serviceAuth.test.ts +19 -0
- package/src/mixins/__tests__/sso.test.ts +31 -0
- package/src/mixins/index.ts +8 -0
- package/src/models/interfaces.ts +3 -0
- package/src/server/__tests__/cors.test.ts +5 -1
- package/src/server/__tests__/rateLimit.test.ts +116 -0
- package/src/server/cors.ts +25 -20
- package/src/server/rateLimit.ts +39 -8
- package/src/utils/__tests__/consumeSsoReturn.test.ts +1 -1
- package/src/utils/__tests__/profileLinks.test.ts +126 -0
- package/src/utils/__tests__/ssoReturn.test.ts +1 -1
- package/src/utils/profileLinks.ts +74 -0
- package/src/utils/ssoReturn.ts +2 -2
|
@@ -25,6 +25,7 @@ export interface RequestOptions {
|
|
|
25
25
|
timeout?: number;
|
|
26
26
|
signal?: AbortSignal;
|
|
27
27
|
headers?: Record<string, string>;
|
|
28
|
+
responseType?: 'blob';
|
|
28
29
|
}
|
|
29
30
|
interface RequestConfig extends RequestOptions {
|
|
30
31
|
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
@@ -183,6 +184,8 @@ export declare class HttpService {
|
|
|
183
184
|
* Build full URL with query params
|
|
184
185
|
*/
|
|
185
186
|
private buildURL;
|
|
187
|
+
private getCredentialsMode;
|
|
188
|
+
private shouldSendCredentials;
|
|
186
189
|
/**
|
|
187
190
|
* Fetch CSRF token from server (with deduplication)
|
|
188
191
|
* Required for state-changing requests (POST, PUT, PATCH, DELETE)
|
|
@@ -122,7 +122,7 @@ export interface OxyServices extends InstanceType<ReturnType<typeof composeOxySe
|
|
|
122
122
|
waitForIframeAuth(iframe: HTMLIFrameElement, timeout: number, expectedOrigin: string): Promise<SessionLoginResponse | null>;
|
|
123
123
|
signInWithRedirect(options?: RedirectAuthOptions): void;
|
|
124
124
|
signUpWithRedirect(options?: RedirectAuthOptions): void;
|
|
125
|
-
exchangeSsoCode(code: string): Promise<SessionLoginResponse>;
|
|
125
|
+
exchangeSsoCode(code: string, state?: string): Promise<SessionLoginResponse>;
|
|
126
126
|
generateSsoState(): string;
|
|
127
127
|
auth(options?: {
|
|
128
128
|
debug?: boolean;
|
|
@@ -153,7 +153,7 @@ export declare const OXY_CLOUD_URL = "https://cloud.oxy.so";
|
|
|
153
153
|
/**
|
|
154
154
|
* Export the default Oxy API URL (for documentation)
|
|
155
155
|
*/
|
|
156
|
-
export declare const OXY_API_URL:
|
|
156
|
+
export declare const OXY_API_URL: string;
|
|
157
157
|
/**
|
|
158
158
|
* Pre-configured client instance for easy import
|
|
159
159
|
* Uses OXY_API_URL as baseURL and OXY_CLOUD_URL as cloudURL
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical JSON (RFC 8785 / JCS-style) serialization.
|
|
3
|
+
*
|
|
4
|
+
* `canonicalize(value)` produces a deterministic string for any JSON-compatible
|
|
5
|
+
* value so that a client which SIGNS a record and a server which VERIFIES it
|
|
6
|
+
* agree byte-for-byte on the signing input — regardless of the order in which
|
|
7
|
+
* object keys happen to be written, how the value was deserialized, or which
|
|
8
|
+
* runtime built it.
|
|
9
|
+
*
|
|
10
|
+
* This is the load-bearing primitive for the self-sovereign identity layer's
|
|
11
|
+
* signed records (`SignatureService.signRecord` + the API's record-verify path):
|
|
12
|
+
* both sides import THIS function from `@oxyhq/core`, so cross-implementation
|
|
13
|
+
* number/string formatting differences cannot cause a verify mismatch.
|
|
14
|
+
*
|
|
15
|
+
* Rules (the JSON Canonicalization Scheme subset we need):
|
|
16
|
+
* - Objects: keys are sorted (ascending, by UTF-16 code unit — the default
|
|
17
|
+
* `Array.prototype.sort` order) and serialized recursively. Properties whose
|
|
18
|
+
* value is `undefined`, a function, or a symbol are OMITTED (matching
|
|
19
|
+
* `JSON.stringify` object semantics).
|
|
20
|
+
* - Arrays: element order is PRESERVED; `undefined`/function/symbol elements
|
|
21
|
+
* serialize to `null` (matching `JSON.stringify` array semantics).
|
|
22
|
+
* - `null`, booleans, strings, and finite numbers serialize via the standard
|
|
23
|
+
* JSON representation.
|
|
24
|
+
* - Values exposing a `toJSON()` method (e.g. `Date`) are replaced by its
|
|
25
|
+
* result first, then serialized — so a `Date` and its ISO-string equivalent
|
|
26
|
+
* canonicalize identically (the wire always carries the string form).
|
|
27
|
+
* - Non-finite numbers (`NaN`, `Infinity`) and `bigint` are not part of the
|
|
28
|
+
* JSON data model and throw, rather than silently producing `null`.
|
|
29
|
+
*
|
|
30
|
+
* Platform-agnostic — zero dependencies, no `require()`, no react/react-native/
|
|
31
|
+
* expo. Safe in the dual CJS + ESM build.
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* Produce the canonical JSON string for `value`.
|
|
35
|
+
*
|
|
36
|
+
* Deterministic: two structurally-equal values yield identical strings even if
|
|
37
|
+
* their object keys were written in different orders. Use this — never an
|
|
38
|
+
* ad-hoc `JSON.stringify` of a hand-sorted object — as the signing input for
|
|
39
|
+
* signed records, so client signing and server verification cannot drift.
|
|
40
|
+
*
|
|
41
|
+
* @throws if `value` (or any nested member used as the top-level/primitive)
|
|
42
|
+
* contains a non-finite number or a `bigint`, which have no JSON form.
|
|
43
|
+
*/
|
|
44
|
+
export declare function canonicalize(value: unknown): string;
|
|
@@ -186,6 +186,13 @@ export declare class KeyManager {
|
|
|
186
186
|
* @internal
|
|
187
187
|
*/
|
|
188
188
|
private static _persistIdentityAtomic;
|
|
189
|
+
/**
|
|
190
|
+
* Restore the backup slot to a previously-snapshotted state. Best-effort so
|
|
191
|
+
* the original persistence error remains the one surfaced to the caller.
|
|
192
|
+
*
|
|
193
|
+
* @internal
|
|
194
|
+
*/
|
|
195
|
+
private static _rollbackBackup;
|
|
189
196
|
/**
|
|
190
197
|
* Restore the primary slot to a previously-snapshotted (privA, pubA) pair,
|
|
191
198
|
* or delete it entirely if there was no prior identity. Best-effort: every
|
|
@@ -4,6 +4,48 @@
|
|
|
4
4
|
* Handles signing and verification of messages using ECDSA secp256k1.
|
|
5
5
|
* Used for authenticating requests and proving identity ownership.
|
|
6
6
|
*/
|
|
7
|
+
import type { SignedRecordEnvelope } from '@oxyhq/contracts';
|
|
8
|
+
/**
|
|
9
|
+
* The signing-input portion of a {@link SignedRecordEnvelope}: every field
|
|
10
|
+
* EXCEPT the `publicKey` and `signature`. Both the client (when signing) and
|
|
11
|
+
* the server (when verifying) canonicalize exactly these fields, so they agree
|
|
12
|
+
* on the bytes that the signature covers.
|
|
13
|
+
*
|
|
14
|
+
* The v2 chain fields (`seq`/`prev`/`collection`/`rkey`) are optional: a v1
|
|
15
|
+
* envelope omits them and is signed over only the base fields; a v2 envelope
|
|
16
|
+
* carries them and includes them in the signed bytes.
|
|
17
|
+
*/
|
|
18
|
+
export type SignedRecordSigningFields = Pick<SignedRecordEnvelope, 'version' | 'type' | 'subject' | 'issuer' | 'record' | 'issuedAt'> & Partial<Pick<SignedRecordEnvelope, 'seq' | 'prev' | 'collection' | 'rkey'>>;
|
|
19
|
+
/**
|
|
20
|
+
* Compute the canonical signing input for a signed-record envelope.
|
|
21
|
+
*
|
|
22
|
+
* This is the single definition of "what the signature covers". `@oxyhq/core`
|
|
23
|
+
* (client signing) and `@oxyhq/api` (server verification) both call this, so a
|
|
24
|
+
* record signed by a client and verified by the server cannot drift.
|
|
25
|
+
*
|
|
26
|
+
* - **v1**: the canonical JSON of `{version, type, subject, issuer, record,
|
|
27
|
+
* issuedAt}` — BYTE-IDENTICAL to the original scheme, so every signature
|
|
28
|
+
* already in production keeps verifying.
|
|
29
|
+
* - **v2**: the canonical JSON additionally includes the hash-chain fields
|
|
30
|
+
* `{seq, prev, collection, rkey}`. Because {@link canonicalize} sorts keys,
|
|
31
|
+
* the on-the-wire field order is irrelevant; the resulting canonical key
|
|
32
|
+
* order is `collection, issuedAt, issuer, prev, record, rkey, seq, subject,
|
|
33
|
+
* type, version`. `prev` is `null` at genesis (serialized as `null`, not
|
|
34
|
+
* omitted), so it is always part of the signed bytes.
|
|
35
|
+
*/
|
|
36
|
+
export declare function signedRecordSigningInput(fields: SignedRecordSigningFields): string;
|
|
37
|
+
/**
|
|
38
|
+
* Compute the `recordId` (content address) of a signed record: the SHA-256 hex
|
|
39
|
+
* digest of its canonical {@link signedRecordSigningInput}.
|
|
40
|
+
*
|
|
41
|
+
* Deterministic and stable across runtimes (it reuses the same canonicalization
|
|
42
|
+
* + SHA-256 the signature itself is built on). The recordId is what `prev`
|
|
43
|
+
* references in the per-subject hash chain, so `@oxyhq/core` (client) and
|
|
44
|
+
* `@oxyhq/api` (server) MUST compute it identically — both call this function.
|
|
45
|
+
* It is taken over the SIGNING input (excluding `publicKey`/`signature`), so it
|
|
46
|
+
* is a pure content address of the record's meaning, independent of who signed.
|
|
47
|
+
*/
|
|
48
|
+
export declare function computeRecordId(fields: SignedRecordSigningFields): Promise<string>;
|
|
7
49
|
export interface SignedMessage {
|
|
8
50
|
message: string;
|
|
9
51
|
signature: string;
|
|
@@ -63,6 +105,22 @@ export declare class SignatureService {
|
|
|
63
105
|
* Used for challenge-response authentication
|
|
64
106
|
*/
|
|
65
107
|
static signChallenge(challenge: string): Promise<AuthChallenge>;
|
|
108
|
+
/**
|
|
109
|
+
* Create a signed authentication challenge response using the SHARED identity
|
|
110
|
+
* key (the cross-app `group.so.oxy.shared` keychain key), not the primary
|
|
111
|
+
* device key.
|
|
112
|
+
*
|
|
113
|
+
* Mirrors {@link signChallenge} exactly — same message format
|
|
114
|
+
* (`auth:${publicKey}:${challenge}:${timestamp}`) so the server verification
|
|
115
|
+
* path is unchanged — but sources the shared public/private key from
|
|
116
|
+
* `KeyManager` and signs with `signWithKey`. Used by "Sign in with Oxy"
|
|
117
|
+
* same-device shared-keychain SSO (Mechanism A): a sibling native app proves
|
|
118
|
+
* control of the shared identity to mint its own session.
|
|
119
|
+
*
|
|
120
|
+
* Throws if no shared identity exists (native-only; the shared keychain is
|
|
121
|
+
* unavailable on web).
|
|
122
|
+
*/
|
|
123
|
+
static signChallengeWithSharedKey(challenge: string): Promise<AuthChallenge>;
|
|
66
124
|
/**
|
|
67
125
|
* Verify a challenge response
|
|
68
126
|
*/
|
|
@@ -86,5 +144,59 @@ export declare class SignatureService {
|
|
|
86
144
|
publicKey: string;
|
|
87
145
|
timestamp: number;
|
|
88
146
|
}>;
|
|
147
|
+
/**
|
|
148
|
+
* Build a signed-record envelope for a self-issued identity/profile record.
|
|
149
|
+
*
|
|
150
|
+
* The envelope is self-issued: `issuer` equals `subject` (the signer's DID).
|
|
151
|
+
* The signature covers the canonical JSON of every field EXCEPT `publicKey`
|
|
152
|
+
* and `signature` (see {@link signedRecordSigningInput}); `alg` is
|
|
153
|
+
* `ES256K-DER-SHA256` (secp256k1 over the SHA-256 of the canonical bytes,
|
|
154
|
+
* DER-encoded), the same scheme this service uses everywhere else.
|
|
155
|
+
*
|
|
156
|
+
* Requires a stored identity (native secure storage); throws if none exists.
|
|
157
|
+
*
|
|
158
|
+
* @param type - The record category (`'identity'` or `'profile'`).
|
|
159
|
+
* @param subject - The subject DID the record is about (also the issuer).
|
|
160
|
+
* @param record - The arbitrary record payload to attest to.
|
|
161
|
+
*/
|
|
162
|
+
static signRecord(type: SignedRecordEnvelope['type'], subject: string, record: Record<string, unknown>): Promise<SignedRecordEnvelope>;
|
|
163
|
+
/**
|
|
164
|
+
* Build a signed-record envelope (v2) carrying the per-subject hash-chain
|
|
165
|
+
* fields.
|
|
166
|
+
*
|
|
167
|
+
* Identical to {@link signRecord} (self-issued: `issuer === subject`; same
|
|
168
|
+
* `ES256K-DER-SHA256` scheme over {@link signedRecordSigningInput}) but
|
|
169
|
+
* `version` is `2` and the signed bytes additionally cover the chain fields:
|
|
170
|
+
*
|
|
171
|
+
* @param type - The record category.
|
|
172
|
+
* @param subject - The subject DID the record is about (also the issuer).
|
|
173
|
+
* @param record - The arbitrary record payload to attest to.
|
|
174
|
+
* @param chain - The hash-chain coordinates:
|
|
175
|
+
* - `seq` — strictly-increasing sequence number for this subject's chain.
|
|
176
|
+
* - `prev` — the `recordId` of the previous record, or `null` at genesis.
|
|
177
|
+
* - `collection` + `rkey` — the AtProto-style record key.
|
|
178
|
+
*
|
|
179
|
+
* The caller is responsible for fetching the current chain head (so `seq` /
|
|
180
|
+
* `prev` are correct) before signing. Requires a stored identity; throws if
|
|
181
|
+
* none exists.
|
|
182
|
+
*/
|
|
183
|
+
static signRecordV2(type: SignedRecordEnvelope['type'], subject: string, record: Record<string, unknown>, chain: {
|
|
184
|
+
seq: number;
|
|
185
|
+
prev: string | null;
|
|
186
|
+
collection: string;
|
|
187
|
+
rkey: string;
|
|
188
|
+
}): Promise<SignedRecordEnvelope>;
|
|
189
|
+
/**
|
|
190
|
+
* Verify a signed-record envelope: recompute the canonical signing input from
|
|
191
|
+
* the envelope's own fields and check the signature against the envelope's
|
|
192
|
+
* `publicKey`.
|
|
193
|
+
*
|
|
194
|
+
* Note: this confirms the signature is internally consistent with the
|
|
195
|
+
* embedded `publicKey`. It does NOT establish that `publicKey` is an
|
|
196
|
+
* authorized verification method for `subject` — that authorization check is
|
|
197
|
+
* the server's responsibility (it asserts the key is a current verification
|
|
198
|
+
* method on the subject's DID).
|
|
199
|
+
*/
|
|
200
|
+
static verifyRecord(envelope: SignedRecordEnvelope): Promise<boolean>;
|
|
89
201
|
}
|
|
90
202
|
export default SignatureService;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export type { SilentAuthOptions } from './mixins/OxyServices.silent';
|
|
|
30
30
|
export type { RedirectAuthOptions } from './mixins/OxyServices.redirect';
|
|
31
31
|
export { ServiceCredentialMismatchError } from './mixins/OxyServices.auth';
|
|
32
32
|
export type { ServiceTokenResponse } from './mixins/OxyServices.auth';
|
|
33
|
+
export type { CommonsSignInHandle, CommonsSignInStatus, CommonsApprovalInfo, CommonsSignInActionResult, } from './mixins/OxyServices.auth';
|
|
33
34
|
export type { ServiceApp, ServiceActingAsVerification } from './mixins/OxyServices.utility';
|
|
34
35
|
export type { CreateManagedAccountInput, ManagedAccountManager, ManagedAccount, } from './mixins/OxyServices.managedAccounts';
|
|
35
36
|
export type { ContactDiscoveryMatch, ContactDiscoveryResponse, } from './mixins/OxyServices.contacts';
|
|
@@ -38,9 +39,15 @@ export { OxyAppDataIdentifierError } from './mixins/OxyServices.appData';
|
|
|
38
39
|
export { getNormalizedUserId, normalizeUserIdentity, normalizeUserIdentityOrNull, } from './utils/userIdentity';
|
|
39
40
|
export { getCanonicalUserHandle, getNormalizedUserHandle, } from './utils/userHandle';
|
|
40
41
|
export type { CanonicalUserHandleInput, UserHandleInput } from './utils/userHandle';
|
|
42
|
+
export { normalizeProfileLinks } from './utils/profileLinks';
|
|
43
|
+
export type { ProfileLink, ProfileLinkMetadata } from './utils/profileLinks';
|
|
41
44
|
export type { Application, PublicApplication, ApplicationMember, ApplicationCredential, ApplicationRole, ApplicationType, ApplicationStatus, ApplicationMemberStatus, ApplicationCredentialType, ApplicationCredentialStatus, ApplicationEnvironment, CreateApplicationInput, UpdateApplicationInput, InviteApplicationMemberInput, UpdateApplicationMemberInput, TransferApplicationOwnershipInput, CreateApplicationCredentialInput, ApplicationCredentialWithSecret, RotateApplicationCredentialResult, ApplicationUsagePeriod, ApplicationUsageSummary, ApplicationUsageByDay, ApplicationUsageByEndpoint, ApplicationUsageStats, ApplicationSuccessResult, } from './mixins/OxyServices.applications';
|
|
42
45
|
export type { Workspace, WorkspaceMember, WorkspaceRole, WorkspaceType, WorkspaceStatus, WorkspaceMemberStatus, CreateWorkspaceInput, UpdateWorkspaceInput, InviteWorkspaceMemberInput, UpdateWorkspaceMemberInput, TransferWorkspaceOwnershipInput, WorkspaceSuccessResult, } from './mixins/OxyServices.workspaces';
|
|
43
46
|
export type { ReputationCategory, TrustTier, ReputationTransactionStatus, ReputationTargetEntityType, ReputationDisputeStatus, ReputationInfluenceContext, ReputationTransaction, ReputationBalanceBreakdown, ReputationInfluence, ReputationReliability, ReputationBalance, ReputationDispute, ReputationRule, ReputationLeaderboardEntry, ReputationInfluenceResult, ReverseReputationTransactionResult, AwardReputationInput, CreateReputationDisputeInput, ResolveReputationDisputeInput, UpsertReputationRuleInput, ReverseReputationTransactionInput, } from './mixins/OxyServices.reputation';
|
|
47
|
+
export { buildUserDid } from './mixins/OxyServices.identity';
|
|
48
|
+
export type { IdentityRecordType, UnlinkableAuthMethodType, LinkAuthMethodResult, PublishRecordResult, VerifyRecordResult, VerifyDomainResult, RemoveDomainResult, } from './mixins/OxyServices.identity';
|
|
49
|
+
export { parseIdPayload, parseAttestPayload, verifyPublicCardAttestation, } from './mixins/OxyServices.civic';
|
|
50
|
+
export type { CivicCardResult, IdCardRef, AttestQrPayload, ParsedAttestPayload, SubmitRealLifeAttestationInput, DenyValidationResult, VouchForPersonInput, WithdrawVouchResult, IssueCredentialInput, RevokeCredentialResult, } from './mixins/OxyServices.civic';
|
|
44
51
|
export { SessionSyncRequiredError, AuthenticationFailedError, ensureValidToken, isAuthenticationError, withAuthErrorHandling, authenticatedApiCall, } from './utils/authHelpers';
|
|
45
52
|
export type { HandleApiErrorOptions } from './utils/authHelpers';
|
|
46
53
|
export { mergeSessions, normalizeAndSortSessions, sessionsArraysEqual, } from './utils/sessionUtils';
|
|
@@ -48,8 +55,9 @@ export type { ClientSession, StorageKeys, MinimalUserData, SessionLoginResponse,
|
|
|
48
55
|
export type { RefreshAllResponse, RefreshAllAccount, RefreshAllAccountUser, RefreshCookieResponse, } from './models/interfaces';
|
|
49
56
|
export { KeyManager, IdentityAlreadyExistsError, IdentityPersistError, } from './crypto/keyManager';
|
|
50
57
|
export type { KeyPair } from './crypto/keyManager';
|
|
51
|
-
export { SignatureService } from './crypto/signatureService';
|
|
52
|
-
export type { SignedMessage, AuthChallenge } from './crypto/signatureService';
|
|
58
|
+
export { SignatureService, signedRecordSigningInput, computeRecordId } from './crypto/signatureService';
|
|
59
|
+
export type { SignedMessage, AuthChallenge, SignedRecordSigningFields } from './crypto/signatureService';
|
|
60
|
+
export { canonicalize } from './crypto/canonicalJson';
|
|
53
61
|
export { RecoveryPhraseService } from './crypto/recoveryPhrase';
|
|
54
62
|
export type { RecoveryPhraseResult } from './crypto/recoveryPhrase';
|
|
55
63
|
export { DeviceManager } from './utils/deviceManager';
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import type { User, RefreshAllResponse, RefreshCookieResponse } from '../models/interfaces';
|
|
7
7
|
import type { SessionLoginResponse } from '../models/session';
|
|
8
8
|
import type { OxyServicesBase } from '../OxyServices.base';
|
|
9
|
+
import type { PublicApplication } from './OxyServices.applications';
|
|
9
10
|
export interface ChallengeResponse {
|
|
10
11
|
challenge: string;
|
|
11
12
|
expiresAt: string;
|
|
@@ -48,6 +49,60 @@ export interface PublicKeyCheckResponse {
|
|
|
48
49
|
registered: boolean;
|
|
49
50
|
message: string;
|
|
50
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Handle returned by {@link OxyServicesAuthMixin.startCommonsSignIn} for a
|
|
54
|
+
* relying-party app initiating a "Sign in with Oxy" flow.
|
|
55
|
+
*
|
|
56
|
+
* `sessionToken` is the SECRET, high-entropy device-flow credential — it stays
|
|
57
|
+
* on the initiating client, is exchanged once via `claimSessionByToken`, and is
|
|
58
|
+
* NEVER placed in the QR/deep-link. `authorizeCode` is the PUBLIC handle carried
|
|
59
|
+
* in `qrPayload`; the approver (Commons) resolves it via
|
|
60
|
+
* {@link OxyServicesAuthMixin.getCommonsApprovalInfo}.
|
|
61
|
+
*/
|
|
62
|
+
export interface CommonsSignInHandle {
|
|
63
|
+
/** Secret device-flow token (held by the initiator; exchanged via `claimSessionByToken`). */
|
|
64
|
+
sessionToken: string;
|
|
65
|
+
/** Public, single-use authorize code carried in the QR / deep-link. */
|
|
66
|
+
authorizeCode: string;
|
|
67
|
+
/** Ready-to-render deep-link / universal-link string (`oxycommons://approve?...`). */
|
|
68
|
+
qrPayload: string;
|
|
69
|
+
/** Server-authoritative expiry (epoch milliseconds). */
|
|
70
|
+
expiresAt: number;
|
|
71
|
+
/** Session lifecycle status as reported by the server (e.g. `'pending'`). */
|
|
72
|
+
status: string;
|
|
73
|
+
}
|
|
74
|
+
/** Poll result for a "Sign in with Oxy" device-flow session (`GET /auth/session/status`). */
|
|
75
|
+
export interface CommonsSignInStatus {
|
|
76
|
+
/** True once an approver has authorized the session. */
|
|
77
|
+
authorized: boolean;
|
|
78
|
+
/** The authorized session id (present once `authorized`). */
|
|
79
|
+
sessionId?: string;
|
|
80
|
+
/** The approving identity's public key (present once `authorized`). */
|
|
81
|
+
publicKey?: string;
|
|
82
|
+
/** Lifecycle status (`'pending'` | `'authorized'` | `'cancelled'` | `'expired'`). */
|
|
83
|
+
status?: string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Server-resolved approval context shown by the approver (Commons) before
|
|
87
|
+
* authorizing — the TRUSTED identity of the requesting app, resolved from the
|
|
88
|
+
* `authorizeCode` server-side (never from the QR string).
|
|
89
|
+
*/
|
|
90
|
+
export interface CommonsApprovalInfo {
|
|
91
|
+
/** Sanitized, display-safe identity of the requesting application. */
|
|
92
|
+
application: PublicApplication;
|
|
93
|
+
/** OAuth scopes the application is requesting. */
|
|
94
|
+
scopes: string[];
|
|
95
|
+
/** The origin the session is bound to (the RP web origin), when applicable. */
|
|
96
|
+
boundOrigin?: string;
|
|
97
|
+
/** Server-authoritative expiry (epoch milliseconds). */
|
|
98
|
+
expiresAt: number;
|
|
99
|
+
/** Session lifecycle status. */
|
|
100
|
+
status: string;
|
|
101
|
+
}
|
|
102
|
+
/** Result of approving / denying a "Sign in with Oxy" request. */
|
|
103
|
+
export interface CommonsSignInActionResult {
|
|
104
|
+
success: boolean;
|
|
105
|
+
}
|
|
51
106
|
export interface ServiceTokenResponse {
|
|
52
107
|
token: string;
|
|
53
108
|
expiresIn: number;
|
|
@@ -275,6 +330,87 @@ export declare function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(B
|
|
|
275
330
|
expiresAt: string;
|
|
276
331
|
user: User;
|
|
277
332
|
}>;
|
|
333
|
+
/**
|
|
334
|
+
* MECHANISM A — same-device shared-keychain SSO.
|
|
335
|
+
*
|
|
336
|
+
* Native-only. If this device holds a shared identity (the cross-app
|
|
337
|
+
* `group.so.oxy.shared` keychain key), prove control of it and mint a
|
|
338
|
+
* session: `requestChallenge(sharedPublicKey)` → `signChallengeWithSharedKey`
|
|
339
|
+
* → `verifyChallenge` (which plants the tokens). Returns `null` on web or
|
|
340
|
+
* when no shared identity is present — never throws for the absent-identity
|
|
341
|
+
* case, so a cold-boot caller can fall through to the next step.
|
|
342
|
+
*
|
|
343
|
+
* The cold-boot wiring that CALLS this lives in `OxyContext`
|
|
344
|
+
* (`@oxyhq/services`); this method just performs the exchange.
|
|
345
|
+
*/
|
|
346
|
+
signInWithSharedIdentity(opts?: {
|
|
347
|
+
deviceName?: string;
|
|
348
|
+
deviceFingerprint?: string;
|
|
349
|
+
}): Promise<SessionLoginResponse | null>;
|
|
350
|
+
/**
|
|
351
|
+
* MECHANISM B (relying party) — begin a "Sign in with Oxy" handoff.
|
|
352
|
+
*
|
|
353
|
+
* Generates a secret device-flow `sessionToken` client-side (it never
|
|
354
|
+
* appears in the QR), registers it with `POST /auth/session/create`, and
|
|
355
|
+
* returns the server-issued public `authorizeCode` + ready-to-render
|
|
356
|
+
* `qrPayload`. Render the QR (web) / open the deep-link (same-device); the
|
|
357
|
+
* approver resolves the code and authorizes. Then poll with
|
|
358
|
+
* {@link pollCommonsSignIn} and, on `authorized`, exchange the
|
|
359
|
+
* `sessionToken` via the existing `claimSessionByToken`.
|
|
360
|
+
*
|
|
361
|
+
* @param params.clientId - The RP's registered OAuth client id
|
|
362
|
+
* (ApplicationCredential publicKey); required so the server can resolve the
|
|
363
|
+
* requesting application's identity.
|
|
364
|
+
*/
|
|
365
|
+
startCommonsSignIn(params: {
|
|
366
|
+
clientId: string;
|
|
367
|
+
}): Promise<CommonsSignInHandle>;
|
|
368
|
+
/**
|
|
369
|
+
* MECHANISM B (relying party) — poll a device-flow session for approval.
|
|
370
|
+
*
|
|
371
|
+
* Backstop for the auth socket. On `authorized` (with a `sessionId`), the
|
|
372
|
+
* caller exchanges the secret `sessionToken` via the existing
|
|
373
|
+
* `claimSessionByToken` to mint the first access token.
|
|
374
|
+
*
|
|
375
|
+
* @param sessionToken - The secret token from {@link startCommonsSignIn}.
|
|
376
|
+
*/
|
|
377
|
+
pollCommonsSignIn(sessionToken: string): Promise<CommonsSignInStatus>;
|
|
378
|
+
/**
|
|
379
|
+
* MECHANISM B (approver / Commons) — resolve the TRUSTED identity of a
|
|
380
|
+
* sign-in request from its public `authorizeCode`.
|
|
381
|
+
*
|
|
382
|
+
* The returned `application` is resolved server-side and is the only safe
|
|
383
|
+
* thing to display in the approval UI — NEVER trust the app/name/origin
|
|
384
|
+
* strings carried in the QR payload. Public (no auth required).
|
|
385
|
+
*
|
|
386
|
+
* @param authorizeCode - The public code scanned from the QR / deep-link.
|
|
387
|
+
*/
|
|
388
|
+
getCommonsApprovalInfo(authorizeCode: string): Promise<CommonsApprovalInfo>;
|
|
389
|
+
/**
|
|
390
|
+
* MECHANISM B (approver / Commons) — approve a sign-in request by signing a
|
|
391
|
+
* fresh challenge with the PRIMARY local identity key.
|
|
392
|
+
*
|
|
393
|
+
* Commons holds the user's identity as its primary key (not the shared
|
|
394
|
+
* key), so this uses `signChallenge`. The signed-but-cookieless authorize
|
|
395
|
+
* endpoint resolves the user from the verified signer — the RP that started
|
|
396
|
+
* the flow then claims its session. Native-only (requires a local identity).
|
|
397
|
+
*
|
|
398
|
+
* @param params.authorizeCode - The public code being approved.
|
|
399
|
+
* @param params.deviceName - Optional human-readable device label.
|
|
400
|
+
* @param params.deviceFingerprint - Optional device fingerprint.
|
|
401
|
+
*/
|
|
402
|
+
approveCommonsSignIn(params: {
|
|
403
|
+
authorizeCode: string;
|
|
404
|
+
deviceName?: string;
|
|
405
|
+
deviceFingerprint?: string;
|
|
406
|
+
}): Promise<CommonsSignInActionResult>;
|
|
407
|
+
/**
|
|
408
|
+
* MECHANISM B (approver / Commons) — deny a sign-in request, cancelling the
|
|
409
|
+
* device-flow session so the RP stops waiting.
|
|
410
|
+
*
|
|
411
|
+
* @param authorizeCode - The public code being denied.
|
|
412
|
+
*/
|
|
413
|
+
denyCommonsSignIn(authorizeCode: string): Promise<CommonsSignInActionResult>;
|
|
278
414
|
/**
|
|
279
415
|
* Refresh every device-local refresh-cookie slot in a single round trip
|
|
280
416
|
* (Google-style multi-account rebuild).
|