@oxy.so/core 1.16.0 → 1.18.1
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/HttpService.js +30 -1
- package/dist/cjs/crypto/identityLink.js +18 -0
- package/dist/cjs/i18n/locales/ar-SA.json +78 -3
- package/dist/cjs/i18n/locales/ca-ES.json +78 -3
- package/dist/cjs/i18n/locales/de-DE.json +78 -3
- package/dist/cjs/i18n/locales/en-US.json +75 -28
- package/dist/cjs/i18n/locales/es-ES.json +75 -28
- package/dist/cjs/i18n/locales/fr-FR.json +78 -3
- package/dist/cjs/i18n/locales/it-IT.json +78 -3
- package/dist/cjs/i18n/locales/ja-JP.json +78 -3
- package/dist/cjs/i18n/locales/ko-KR.json +78 -3
- package/dist/cjs/i18n/locales/locales/ar-SA.json +78 -3
- package/dist/cjs/i18n/locales/locales/ca-ES.json +78 -3
- package/dist/cjs/i18n/locales/locales/de-DE.json +78 -3
- package/dist/cjs/i18n/locales/locales/en-US.json +75 -28
- package/dist/cjs/i18n/locales/locales/es-ES.json +75 -28
- package/dist/cjs/i18n/locales/locales/fr-FR.json +78 -3
- package/dist/cjs/i18n/locales/locales/it-IT.json +78 -3
- package/dist/cjs/i18n/locales/locales/ja-JP.json +78 -3
- package/dist/cjs/i18n/locales/locales/ko-KR.json +78 -3
- package/dist/cjs/i18n/locales/locales/pt-PT.json +78 -3
- package/dist/cjs/i18n/locales/locales/zh-CN.json +78 -3
- package/dist/cjs/i18n/locales/pt-PT.json +78 -3
- package/dist/cjs/i18n/locales/zh-CN.json +78 -3
- package/dist/cjs/index.js +9 -42
- package/dist/cjs/mixins/OxyServices.auth.js +110 -17
- package/dist/cjs/mixins/OxyServices.identity.js +128 -8
- package/dist/cjs/mixins/OxyServices.user.js +42 -7
- package/dist/cjs/utils/authWebUrl.js +4 -5
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/HttpService.js +30 -1
- package/dist/esm/crypto/identityLink.js +15 -0
- package/dist/esm/i18n/locales/ar-SA.json +78 -3
- package/dist/esm/i18n/locales/ca-ES.json +78 -3
- package/dist/esm/i18n/locales/de-DE.json +78 -3
- package/dist/esm/i18n/locales/en-US.json +75 -28
- package/dist/esm/i18n/locales/es-ES.json +75 -28
- package/dist/esm/i18n/locales/fr-FR.json +78 -3
- package/dist/esm/i18n/locales/it-IT.json +78 -3
- package/dist/esm/i18n/locales/ja-JP.json +78 -3
- package/dist/esm/i18n/locales/ko-KR.json +78 -3
- package/dist/esm/i18n/locales/locales/ar-SA.json +78 -3
- package/dist/esm/i18n/locales/locales/ca-ES.json +78 -3
- package/dist/esm/i18n/locales/locales/de-DE.json +78 -3
- package/dist/esm/i18n/locales/locales/en-US.json +75 -28
- package/dist/esm/i18n/locales/locales/es-ES.json +75 -28
- package/dist/esm/i18n/locales/locales/fr-FR.json +78 -3
- package/dist/esm/i18n/locales/locales/it-IT.json +78 -3
- package/dist/esm/i18n/locales/locales/ja-JP.json +78 -3
- package/dist/esm/i18n/locales/locales/ko-KR.json +78 -3
- package/dist/esm/i18n/locales/locales/pt-PT.json +78 -3
- package/dist/esm/i18n/locales/locales/zh-CN.json +78 -3
- package/dist/esm/i18n/locales/pt-PT.json +78 -3
- package/dist/esm/i18n/locales/zh-CN.json +78 -3
- package/dist/esm/index.js +4 -4
- package/dist/esm/mixins/OxyServices.auth.js +110 -17
- package/dist/esm/mixins/OxyServices.identity.js +128 -8
- package/dist/esm/mixins/OxyServices.user.js +42 -7
- package/dist/esm/utils/authWebUrl.js +4 -5
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/HttpService.d.ts +21 -0
- package/dist/types/crypto/identityLink.d.ts +8 -0
- package/dist/types/index.d.ts +2 -5
- package/dist/types/mixins/OxyServices.auth.d.ts +68 -25
- package/dist/types/mixins/OxyServices.identity.d.ts +34 -34
- package/dist/types/mixins/OxyServices.user.d.ts +29 -6
- package/dist/types/models/interfaces.d.ts +67 -13
- package/dist/types/server/securityHeaders.d.ts +3 -3
- package/dist/types/utils/authWebUrl.d.ts +4 -5
- package/package.json +3 -3
- package/src/HttpService.ts +35 -1
- package/src/__tests__/serviceIdentityWhenAnonymous.test.ts +159 -0
- package/src/crypto/__tests__/identityLink.test.ts +18 -0
- package/src/crypto/__tests__/identityProof.test.ts +6 -6
- package/src/crypto/identityLink.ts +18 -0
- package/src/i18n/locales/ar-SA.json +78 -3
- package/src/i18n/locales/ca-ES.json +78 -3
- package/src/i18n/locales/de-DE.json +78 -3
- package/src/i18n/locales/en-US.json +75 -28
- package/src/i18n/locales/es-ES.json +75 -28
- package/src/i18n/locales/fr-FR.json +78 -3
- package/src/i18n/locales/it-IT.json +78 -3
- package/src/i18n/locales/ja-JP.json +78 -3
- package/src/i18n/locales/ko-KR.json +78 -3
- package/src/i18n/locales/pt-PT.json +78 -3
- package/src/i18n/locales/zh-CN.json +78 -3
- package/src/index.ts +6 -48
- package/src/mixins/OxyServices.auth.ts +127 -24
- package/src/mixins/OxyServices.identity.ts +126 -8
- package/src/mixins/OxyServices.user.ts +51 -10
- package/src/mixins/__tests__/identityLink.test.ts +89 -0
- package/src/mixins/__tests__/notifications.read.test.ts +82 -0
- package/src/mixins/__tests__/preSessionSkipAuth.test.ts +1 -1
- package/src/mixins/__tests__/webauthnAuth.test.ts +75 -1
- package/src/models/interfaces.ts +67 -12
- package/src/server/securityHeaders.ts +3 -3
- package/src/utils/authWebUrl.ts +4 -5
- package/dist/cjs/crypto/identityMove.js +0 -142
- package/dist/cjs/crypto/webIdentityCarrier.js +0 -325
- package/dist/esm/crypto/identityMove.js +0 -128
- package/dist/esm/crypto/webIdentityCarrier.js +0 -303
- package/dist/types/crypto/identityMove.d.ts +0 -75
- package/dist/types/crypto/webIdentityCarrier.d.ts +0 -150
- package/src/crypto/__tests__/identityMove.test.ts +0 -146
- package/src/crypto/__tests__/webIdentityCarrier.test.ts +0 -169
- package/src/crypto/__tests__/webIdentityRecoveryMaterial.test.ts +0 -117
- package/src/crypto/identityMove.ts +0 -167
- package/src/crypto/webIdentityCarrier.ts +0 -394
|
@@ -17,6 +17,11 @@ import type { DeviceSecretMintOutcome } from './session/refresh';
|
|
|
17
17
|
export type AuthRefreshReason = 'preflight' | 'response-401';
|
|
18
18
|
export type AuthRefreshHandler = (reason: AuthRefreshReason) => Promise<string | null>;
|
|
19
19
|
export type AccessTokenProvider = () => string | null;
|
|
20
|
+
/**
|
|
21
|
+
* Supplies a bearer for a request made while this client holds NO user session
|
|
22
|
+
* — `null` to send it anonymous. See {@link HttpService.setAnonymousAuthProvider}.
|
|
23
|
+
*/
|
|
24
|
+
export type AnonymousAuthProvider = () => Promise<string | null>;
|
|
20
25
|
/**
|
|
21
26
|
* A low-level authenticated request whose response body remains unread.
|
|
22
27
|
*
|
|
@@ -162,6 +167,7 @@ export declare class HttpService {
|
|
|
162
167
|
private noFresherTokenThan;
|
|
163
168
|
private authRefreshHandler;
|
|
164
169
|
private accessTokenProvider;
|
|
170
|
+
private anonymousAuthProvider;
|
|
165
171
|
private deviceSecretMintInFlight;
|
|
166
172
|
/**
|
|
167
173
|
* Bumped by every {@link endSession}. A re-mint captures it before its first
|
|
@@ -324,6 +330,13 @@ export declare class HttpService {
|
|
|
324
330
|
* Get auth header with automatic token refresh
|
|
325
331
|
*/
|
|
326
332
|
private getAuthHeader;
|
|
333
|
+
/**
|
|
334
|
+
* The bearer for a request that has no user session behind it, from the
|
|
335
|
+
* {@link AnonymousAuthProvider} if one is installed. Never throws: a provider
|
|
336
|
+
* that fails sends the request anonymous, exactly as it went before any
|
|
337
|
+
* provider existed.
|
|
338
|
+
*/
|
|
339
|
+
private getAnonymousAuthHeader;
|
|
327
340
|
refreshAccessToken(reason: AuthRefreshReason): Promise<string | null>;
|
|
328
341
|
/**
|
|
329
342
|
* Report that the refresh/mint endpoint answered 429 Too Many Requests, so the
|
|
@@ -442,6 +455,14 @@ export declare class HttpService {
|
|
|
442
455
|
hasSessionEnded(): boolean;
|
|
443
456
|
setAuthRefreshHandler(handler: AuthRefreshHandler | null): void;
|
|
444
457
|
setAccessTokenProvider(provider: AccessTokenProvider | null): void;
|
|
458
|
+
/**
|
|
459
|
+
* Authenticate requests that carry no user session with the bearer this
|
|
460
|
+
* provider returns — in practice a backend's own service token (see
|
|
461
|
+
* `OxyConfig.serviceIdentity`). A user session always wins: the provider is
|
|
462
|
+
* consulted only when there is no access token, and never for `skipAuth`
|
|
463
|
+
* requests. `null` removes it.
|
|
464
|
+
*/
|
|
465
|
+
setAnonymousAuthProvider(provider: AnonymousAuthProvider | null): void;
|
|
445
466
|
clearTokens(): void;
|
|
446
467
|
/**
|
|
447
468
|
* Subscribe to access-token changes on this instance.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The code both screens show while Commons is being linked to a passkey
|
|
3
|
+
* account (ADR 0029 D3): 6 digits derived from the link request and the key
|
|
4
|
+
* Commons signed with. auth.oxy.so derives it from the key the API relayed,
|
|
5
|
+
* Commons from its own; the person checks they match before the passkey is
|
|
6
|
+
* asserted, so a photographed QR cannot slip another key in.
|
|
7
|
+
*/
|
|
8
|
+
export declare function deriveIdentityLinkCode(linkId: string, publicKey: string): string;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -72,14 +72,11 @@ export { hkdfSha256 } from './crypto/kdf';
|
|
|
72
72
|
export { encryptAead, decryptAead, AEAD_KEY_LENGTH, AEAD_NONCE_LENGTH, } from './crypto/aead';
|
|
73
73
|
export type { AeadResult } from './crypto/aead';
|
|
74
74
|
export { deriveSharedSecret } from './crypto/ecdh';
|
|
75
|
-
export { WEB_IDENTITY_PRF_INPUT, WebIdentityUnlockError, WEB_IDENTITY_PRF_OUTPUT_LENGTH, addWrap, deriveIdentityFromMnemonic, deriveIdentityFromPrivateKey, deriveIdentityFromRecoveryMaterial, deriveKeyEncryptionKey, generateDataKey, generateWebIdentity, isUsablePrfOutput, markWrapVerified, normalizeMnemonic, openWebIdentity, parseRecoveryMaterial, removeWrap, sealWebIdentity, unlockWebIdentity, unwrapDataKey, wipeBytes, wipeOpenedIdentity, } from './crypto/webIdentityCarrier';
|
|
76
|
-
export type { OpenedMnemonicIdentity, OpenedRawKeyIdentity, OpenedWebIdentity, WebIdentityRecoveryMaterial, WebIdentityUnlockFailure, WrapInput, } from './crypto/webIdentityCarrier';
|
|
77
75
|
export { digestIdentityPayload, signIdentityProof } from './crypto/identityProof';
|
|
78
|
-
export {
|
|
79
|
-
export type { MoveReceiptClaims } from './crypto/identityMove';
|
|
76
|
+
export { deriveIdentityLinkCode } from './crypto/identityLink';
|
|
80
77
|
export { DeviceManager } from './utils/deviceManager';
|
|
81
78
|
export type { DeviceFingerprint, StoredDeviceInfo } from './utils/deviceManager';
|
|
82
|
-
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, BatchFileAccessEntry, BatchFileAccessResponse, AssetDeleteSummary, AssetUpdateVisibilityRequest, AssetUpdateVisibilityResponse, ServiceAssetMetadata, ServiceAssetMetadataBySha, AccountStorageCategoryUsage, AccountStorageUsageResponse, SecurityEventType, SecurityEventSeverity, SecurityActivity, SecurityActivityResponse, AssetUploadProgress, DeviceLinkedSession, DeviceLinkedSessionsResponse, DeviceLinkedSessionLogoutResponse, UpdateDeviceNameResponse, } from './models/interfaces';
|
|
79
|
+
export type { OxyConfig, PrivacySettings, NotificationPreferences, UserPreferences, User, LoginResponse, Notification, NotificationActor, NotificationPage, 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, BatchFileAccessEntry, BatchFileAccessResponse, AssetDeleteSummary, AssetUpdateVisibilityRequest, AssetUpdateVisibilityResponse, ServiceAssetMetadata, ServiceAssetMetadataBySha, AccountStorageCategoryUsage, AccountStorageUsageResponse, SecurityEventType, SecurityEventSeverity, SecurityActivity, SecurityActivityResponse, AssetUploadProgress, DeviceLinkedSession, DeviceLinkedSessionsResponse, DeviceLinkedSessionLogoutResponse, UpdateDeviceNameResponse, } from './models/interfaces';
|
|
83
80
|
export { SECURITY_EVENT_SEVERITY_MAP } from './models/interfaces';
|
|
84
81
|
export { TopicType, TopicSource } from './models/Topic';
|
|
85
82
|
export type { TopicData, TopicTranslation, TopicListResult } from './models/Topic';
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { User } from '../models/interfaces';
|
|
7
7
|
import type { LoginResult, CommonsDenyReason } from '@oxy.so/contracts';
|
|
8
|
-
import { type
|
|
8
|
+
import { type EmailVerificationConfirmResponse, type EmailVerificationStartRequest, type EmailVerificationStartResponse } from '@oxy.so/contracts';
|
|
9
9
|
import type { SessionLoginResponse } from '../models/session';
|
|
10
10
|
import type { OxyServicesBase } from '../OxyServices.base';
|
|
11
11
|
import type { PublicApplication } from './OxyServices.connectedApps';
|
|
@@ -322,6 +322,11 @@ interface ServiceTokenCacheEntry {
|
|
|
322
322
|
export declare class ServiceCredentialMismatchError extends Error {
|
|
323
323
|
constructor();
|
|
324
324
|
}
|
|
325
|
+
/**
|
|
326
|
+
* How long a failed service-token mint sends session-less requests anonymous
|
|
327
|
+
* before the next attempt, under `serviceIdentity: 'when-anonymous'`.
|
|
328
|
+
*/
|
|
329
|
+
export declare const ANONYMOUS_SERVICE_TOKEN_RETRY_MS = 30000;
|
|
325
330
|
export declare function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(Base: T): {
|
|
326
331
|
new (...args: any[]): {
|
|
327
332
|
/**
|
|
@@ -346,6 +351,34 @@ export declare function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(B
|
|
|
346
351
|
_serviceApiKey: string | null;
|
|
347
352
|
/** @internal Raw apiSecret stored by configureServiceAuth() for use by getServiceToken() */
|
|
348
353
|
_serviceApiSecret: string | null;
|
|
354
|
+
/**
|
|
355
|
+
* Before this instant, a request without a user session does not try to
|
|
356
|
+
* mint a service token and goes out anonymous. Set after a failed mint; see
|
|
357
|
+
* {@link _serviceTokenForAnonymousRequest}.
|
|
358
|
+
*
|
|
359
|
+
* @internal
|
|
360
|
+
*/
|
|
361
|
+
_anonymousServiceTokenRetryAt: number;
|
|
362
|
+
/**
|
|
363
|
+
* The bearer for a request with no user session, under `serviceIdentity:
|
|
364
|
+
* 'when-anonymous'`: this process's service token, or `null` to send the
|
|
365
|
+
* request anonymous as before.
|
|
366
|
+
*
|
|
367
|
+
* `null`, never a throw, whenever there is no token to offer:
|
|
368
|
+
* - no key pair and no attestable workload (a local checkout) — asking
|
|
369
|
+
* costs nothing and there is nothing to ask for;
|
|
370
|
+
* - the mint failed. The request still goes out, anonymous, and so does
|
|
371
|
+
* every request for the next {@link ANONYMOUS_SERVICE_TOKEN_RETRY_MS}, so
|
|
372
|
+
* an Oxy that is refusing attestation (seen for ~2.5 minutes after a
|
|
373
|
+
* deploy on 2026-09-25) is asked once per window rather than once per
|
|
374
|
+
* read, and no read waits on a mint that just failed.
|
|
375
|
+
*
|
|
376
|
+
* The token itself comes from {@link getServiceToken}: cached for its
|
|
377
|
+
* lifetime and single-flight, so concurrent reads share one mint.
|
|
378
|
+
*
|
|
379
|
+
* @internal
|
|
380
|
+
*/
|
|
381
|
+
_serviceTokenForAnonymousRequest(): Promise<string | null>;
|
|
349
382
|
/**
|
|
350
383
|
* Hash an apiKey into a stable Map cache key. Uses Node's SHA-256 — service
|
|
351
384
|
* tokens are only ever issued by a Node host (the SDK on web/RN never has
|
|
@@ -790,50 +823,60 @@ export declare function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(B
|
|
|
790
823
|
message: string;
|
|
791
824
|
}>;
|
|
792
825
|
/**
|
|
793
|
-
*
|
|
826
|
+
* Send a 6-digit code to an email (ADR 0029 D3): the recovery email of a
|
|
827
|
+
* new passkey account (`purpose: 'signup'`), or — named by username or
|
|
828
|
+
* email — the recovery email of an account being recovered
|
|
829
|
+
* (`purpose: 'recovery'`). The answer is the same whether or not an
|
|
830
|
+
* account exists; nothing is revealed about who has one.
|
|
794
831
|
*/
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
832
|
+
startEmailVerification(request: EmailVerificationStartRequest): Promise<EmailVerificationStartResponse>;
|
|
833
|
+
/**
|
|
834
|
+
* Confirm the code {@link startEmailVerification} sent. Resolves to a
|
|
835
|
+
* short-lived one-use ticket: a sign-up passes it (with the email) to
|
|
836
|
+
* {@link webauthnRegisterVerify}; a recovery passes it as `recoveryTicket`
|
|
837
|
+
* to {@link webauthnRegisterOptions} and {@link webauthnRegisterVerify}.
|
|
838
|
+
*/
|
|
839
|
+
confirmEmailVerification(verificationId: string, code: string): Promise<EmailVerificationConfirmResponse>;
|
|
799
840
|
/**
|
|
800
841
|
* Begin a WebAuthn / passkey REGISTRATION ceremony. Requests the
|
|
801
842
|
* `PublicKeyCredentialCreationOptions` the browser's `navigator.credentials
|
|
802
843
|
* .create()` (or `@simplewebauthn/browser`'s `startRegistration`) needs.
|
|
803
844
|
*
|
|
804
|
-
* With a bearer token planted this
|
|
805
|
-
*
|
|
806
|
-
*
|
|
807
|
-
*
|
|
808
|
-
* as `unknown` for the caller
|
|
845
|
+
* With a bearer token planted this adds a passkey to the signed-in account.
|
|
846
|
+
* Without one, `username` is a prospective sign-up's handle, and
|
|
847
|
+
* `recoveryTicket` a recovery's new passkey for the account it names. The
|
|
848
|
+
* returned options are OPAQUE — Oxy does not own their shape (the browser /
|
|
849
|
+
* `@simplewebauthn` does), so they pass through as `unknown` for the caller
|
|
850
|
+
* to hand straight to the ceremony.
|
|
809
851
|
*/
|
|
810
|
-
webauthnRegisterOptions(
|
|
852
|
+
webauthnRegisterOptions(request?: {
|
|
853
|
+
username?: string;
|
|
854
|
+
recoveryTicket?: string;
|
|
855
|
+
}): Promise<unknown>;
|
|
811
856
|
/**
|
|
812
857
|
* Finish a WebAuthn / passkey REGISTRATION ceremony. Forwards the opaque
|
|
813
858
|
* browser `RegistrationResponseJSON` (`response`) alongside the Oxy envelope
|
|
814
859
|
* (desired `username` for signup + the device-session naming fields).
|
|
815
860
|
*
|
|
816
|
-
*
|
|
817
|
-
* - **
|
|
818
|
-
*
|
|
819
|
-
*
|
|
861
|
+
* Server branches, disambiguated by the response shape:
|
|
862
|
+
* - **Sign-up** (no bearer; `username`, `email`, `emailTicket`) and
|
|
863
|
+
* **recovery** (no bearer; `recoveryTicket`): the account is created, or
|
|
864
|
+
* gains the passkey, and a session is minted — the response carries
|
|
865
|
+
* `sessionId`, is the SAME {@link LoginResult} contract as
|
|
866
|
+
* `POST /auth/verify`, and its access token is planted here.
|
|
820
867
|
* - **Link** (bearer present): the passkey is attached to the signed-in
|
|
821
868
|
* account and the server returns `{ success, message }` with no session,
|
|
822
869
|
* which is returned verbatim (no token planting).
|
|
823
870
|
*/
|
|
824
871
|
webauthnRegisterVerify(response: unknown, envelope?: {
|
|
825
872
|
username?: string;
|
|
873
|
+
/** Sign-up: the recovery email, confirmed with `emailTicket`. */
|
|
874
|
+
email?: string;
|
|
875
|
+
emailTicket?: string;
|
|
876
|
+
/** Recovery: the ticket {@link confirmEmailVerification} returned. */
|
|
877
|
+
recoveryTicket?: string;
|
|
826
878
|
deviceName?: string;
|
|
827
879
|
deviceFingerprint?: string;
|
|
828
|
-
/**
|
|
829
|
-
* Sign-up only: the root created on the holder before this call, sealed
|
|
830
|
-
* under the passkey being registered, plus its `enroll_identity` proof
|
|
831
|
-
* (ADR 0024 D4). The account is created WITH it or not at all.
|
|
832
|
-
*/
|
|
833
|
-
identity?: {
|
|
834
|
-
envelope: WebIdentityEnvelope;
|
|
835
|
-
proof: IdentityProof;
|
|
836
|
-
};
|
|
837
880
|
}): Promise<{
|
|
838
881
|
success: true;
|
|
839
882
|
message: string;
|
|
@@ -1,29 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Identity Methods Mixin (self-sovereign identity layer)
|
|
3
|
-
*
|
|
4
|
-
* Provides typed access to Oxy's AtProto/Bluesky-flavoured identity &
|
|
5
|
-
* portability layer:
|
|
6
|
-
* - DID resolution (`did:web:oxy.so:u:<userId>`, derived on demand by the API).
|
|
7
|
-
* - The auth-method ↔ DID verification-method mapping (`GET /auth/methods`) and
|
|
8
|
-
* passkey removal. A root is linked by the holder flows (ADR 0024) and never
|
|
9
|
-
* unlinked; it is replaced by rotation.
|
|
10
|
-
* - Signed records: clients sign an envelope with their own cryptographic key
|
|
11
|
-
* (`SignatureService.signRecord` + the shared `canonicalize`) and publish it;
|
|
12
|
-
* anyone can fetch and verify it.
|
|
13
|
-
* - The signed data-export ("credible exit") bundle.
|
|
14
|
-
* - Verified-domain badges (prove ownership of `nate.com`).
|
|
15
|
-
*
|
|
16
|
-
* Wire shapes come from `@oxy.so/contracts` (`DidDocument`,
|
|
17
|
-
* `SignedRecordEnvelope`, `AuthMethodsResponse`, `VerifiedDomain`,
|
|
18
|
-
* `DomainVerificationInstructions`, `ExportBundle`) — the single source of truth
|
|
19
|
-
* the API validates its output against, so producer and consumer cannot drift.
|
|
20
|
-
*
|
|
21
|
-
* Identity signing is NATIVE-ONLY: the private key lives in native secure
|
|
22
|
-
* storage, so `signRecord` and `publishRecord` require an
|
|
23
|
-
* on-device identity and throw on web (where `KeyManager.getPublicKey()` is
|
|
24
|
-
* always `null`).
|
|
25
|
-
*/
|
|
26
|
-
import type { AuthMethodsResponse, IdentityRootStatus, DidDocument, DomainVerificationInstructions, ExportBundle, OxySignedRecordType, SignedRecordEnvelope, VerifiedDomain } from '@oxy.so/contracts';
|
|
1
|
+
import type { AuthMethodsResponse, IdentityLinkCreateResponse, IdentityLinkState, IdentityRootStatus, DidDocument, DomainVerificationInstructions, ExportBundle, OxySignedRecordType, SignedRecordEnvelope, VerifiedDomain } from '@oxy.so/contracts';
|
|
27
2
|
import type { OxyServicesBase } from '../OxyServices.base';
|
|
28
3
|
import { type PendingIdentityResult } from '../crypto/recoveryPhrase';
|
|
29
4
|
/**
|
|
@@ -140,22 +115,47 @@ export declare function OxyServicesIdentityMixin<T extends typeof OxyServicesBas
|
|
|
140
115
|
*/
|
|
141
116
|
listAuthMethods(): Promise<AuthMethodsResponse>;
|
|
142
117
|
/**
|
|
143
|
-
*
|
|
144
|
-
* linked,
|
|
145
|
-
*
|
|
146
|
-
* nothing here can open the root — so any first-party surface may show a
|
|
147
|
-
* "save your recovery phrase" reminder from it.
|
|
118
|
+
* How the signed-in account is kept (ADR 0029 D3): whether Commons' root is
|
|
119
|
+
* linked (self-custody), or the recovery email of a passkey account. Any
|
|
120
|
+
* first-party surface may read it to recommend linking Commons.
|
|
148
121
|
*/
|
|
149
122
|
getIdentityRootStatus(): Promise<IdentityRootStatus>;
|
|
123
|
+
/**
|
|
124
|
+
* Open a request to link Commons to the signed-in passkey account (ADR 0029
|
|
125
|
+
* D3). auth.oxy.so shows its `qrPayload`; Commons scans it and signs with
|
|
126
|
+
* {@link signIdentityLink}.
|
|
127
|
+
*/
|
|
128
|
+
createIdentityLink(): Promise<IdentityLinkCreateResponse>;
|
|
129
|
+
/** Where a link request stands. Both devices poll it; it needs no session. */
|
|
130
|
+
getIdentityLink(linkId: string): Promise<IdentityLinkState>;
|
|
131
|
+
/**
|
|
132
|
+
* Commons' half: sign the scanned request's `link_identity` proof with THIS
|
|
133
|
+
* device's identity key and post it. NATIVE-ONLY (the key lives in native
|
|
134
|
+
* secure storage). Resolves to the key and the 6-digit code auth.oxy.so will
|
|
135
|
+
* show for it, for the person to compare.
|
|
136
|
+
*/
|
|
137
|
+
signIdentityLink(linkId: string, challenge: string): Promise<{
|
|
138
|
+
publicKey: string;
|
|
139
|
+
code: string;
|
|
140
|
+
username: string | null;
|
|
141
|
+
}>;
|
|
142
|
+
/** WebAuthn request options over the account's passkeys, whose challenge is the link's. Opaque. */
|
|
143
|
+
getIdentityLinkAssertionOptions(linkId: string, challenge: string): Promise<unknown>;
|
|
144
|
+
/**
|
|
145
|
+
* auth.oxy.so's half: the passkey assertion over the link's challenge. The
|
|
146
|
+
* account gains Commons' root and loses its recovery email.
|
|
147
|
+
*/
|
|
148
|
+
completeIdentityLink(linkId: string, assertion: unknown): Promise<{
|
|
149
|
+
success: true;
|
|
150
|
+
}>;
|
|
151
|
+
/** Withdraw a link request that has not completed. */
|
|
152
|
+
cancelIdentityLink(linkId: string): Promise<void>;
|
|
150
153
|
/**
|
|
151
154
|
* Remove ONE passkey (WebAuthn credential) from the current account.
|
|
152
155
|
*
|
|
153
156
|
* Passkeys are per-credential, so this targets a specific credential id.
|
|
154
157
|
* The server refuses to remove the last remaining auth method (the account
|
|
155
158
|
* would become inaccessible) and deletes the stored `WebauthnCredential`.
|
|
156
|
-
* A passkey that also opens the account's root on the web takes its wrap with
|
|
157
|
-
* it, and the ONLY such passkey is refused with
|
|
158
|
-
* `code: 'IDENTITY_LAST_WEB_HOLDER'` (ADR 0024 D6).
|
|
159
159
|
*
|
|
160
160
|
* @param credentialId - The passkey's public credential id
|
|
161
161
|
* (`AuthMethodEntry.credentialId`).
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* User Management Methods Mixin
|
|
3
3
|
*/
|
|
4
4
|
import type { CreateOxyNotificationRequest } from '@oxy.so/contracts';
|
|
5
|
-
import type { User, Notification, NotificationPreferences, UserPreferences, SearchProfilesResponse, PrivacySettings } from '../models/interfaces';
|
|
5
|
+
import type { User, Notification, NotificationPage, NotificationPreferences, UserPreferences, SearchProfilesResponse, PrivacySettings } from '../models/interfaces';
|
|
6
6
|
import type { UserNameResponse, UserProfileUpdate, RecommendationRequest, RecommendationItem, ThemePreference } from '@oxy.so/contracts';
|
|
7
7
|
import type { OxyServicesBase } from '../OxyServices.base';
|
|
8
8
|
import { type PaginationParams, type FollowGraphParams } from '../utils/apiUtils';
|
|
@@ -350,6 +350,23 @@ export declare function OxyServicesUserMixin<T extends typeof OxyServicesBase>(B
|
|
|
350
350
|
deleteAccount(confirmText: string): Promise<{
|
|
351
351
|
message: string;
|
|
352
352
|
}>;
|
|
353
|
+
/**
|
|
354
|
+
* WebAuthn request options to delete a PASSKEY account (ADR 0029 D3): its own
|
|
355
|
+
* passkeys, user verification required, and a challenge bound to it. Opaque —
|
|
356
|
+
* hand them to the browser's authentication ceremony, on auth.oxy.so. An
|
|
357
|
+
* account with a Commons key deletes with {@link deleteAccount} instead.
|
|
358
|
+
*/
|
|
359
|
+
getAccountDeletionOptions(): Promise<unknown>;
|
|
360
|
+
/**
|
|
361
|
+
* Delete a PASSKEY account permanently, with an assertion by one of its
|
|
362
|
+
* passkeys over {@link getAccountDeletionOptions}' challenge.
|
|
363
|
+
*
|
|
364
|
+
* @param confirmText - Must equal the user's username (verified server-side)
|
|
365
|
+
* @param assertion - The ceremony's opaque `AuthenticationResponseJSON`
|
|
366
|
+
*/
|
|
367
|
+
deleteAccountWithPasskey(confirmText: string, assertion: unknown): Promise<{
|
|
368
|
+
message: string;
|
|
369
|
+
}>;
|
|
353
370
|
/**
|
|
354
371
|
* Invalidate every cached read a follow/unfollow write invalidates.
|
|
355
372
|
*
|
|
@@ -497,9 +514,13 @@ export declare function OxyServicesUserMixin<T extends typeof OxyServicesBase>(B
|
|
|
497
514
|
*/
|
|
498
515
|
getViewerGraph(): Promise<ViewerGraph>;
|
|
499
516
|
/**
|
|
500
|
-
*
|
|
517
|
+
* One page of the signed-in user's Oxy notifications, newest first
|
|
518
|
+
* (`GET /notifications`), with the unread count across all pages.
|
|
501
519
|
*/
|
|
502
|
-
getNotifications(
|
|
520
|
+
getNotifications(params?: {
|
|
521
|
+
page?: number;
|
|
522
|
+
limit?: number;
|
|
523
|
+
}): Promise<NotificationPage>;
|
|
503
524
|
/**
|
|
504
525
|
* Get unread notification count
|
|
505
526
|
*/
|
|
@@ -511,11 +532,13 @@ export declare function OxyServicesUserMixin<T extends typeof OxyServicesBase>(B
|
|
|
511
532
|
* from an Oxy service about the recipient's own account, with the recipient
|
|
512
533
|
* as actor and their profile as the entity.
|
|
513
534
|
*/
|
|
514
|
-
createNotification(data: CreateOxyNotificationRequest
|
|
535
|
+
createNotification(data: CreateOxyNotificationRequest): Promise<Notification>;
|
|
515
536
|
/**
|
|
516
|
-
* Mark
|
|
537
|
+
* Mark one of the signed-in user's notifications read and return it as
|
|
538
|
+
* stored. Scoped to the recipient server-side, so it is also the
|
|
539
|
+
* authoritative read of a notification by id: a foreign or unknown id 404s.
|
|
517
540
|
*/
|
|
518
|
-
markNotificationAsRead(notificationId: string): Promise<
|
|
541
|
+
markNotificationAsRead(notificationId: string): Promise<Notification>;
|
|
519
542
|
/**
|
|
520
543
|
* Mark all notifications as read
|
|
521
544
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AccountKind, AccountCategoryId, UserNameResponse, UserRelationship, ThemePreference } from '@oxy.so/contracts';
|
|
1
|
+
import type { AccountKind, AccountCategoryId, UserNameResponse, UserRelationship, ThemePreference, OxyNotificationEntityType, OxyNotificationType } from '@oxy.so/contracts';
|
|
2
2
|
export interface OxyConfig {
|
|
3
3
|
baseURL: string;
|
|
4
4
|
cloudURL?: string;
|
|
@@ -13,6 +13,27 @@ export interface OxyConfig {
|
|
|
13
13
|
* bounce (which uses the RP origin, not this registered client id).
|
|
14
14
|
*/
|
|
15
15
|
clientId?: string;
|
|
16
|
+
/**
|
|
17
|
+
* How a BACKEND identifies itself on requests that carry no user session.
|
|
18
|
+
*
|
|
19
|
+
* - `'never'` (default): such requests go out anonymous. Right for browsers
|
|
20
|
+
* and apps, which have no service identity to offer.
|
|
21
|
+
* - `'when-anonymous'`: they carry this process's service token instead —
|
|
22
|
+
* from `configureServiceAuth()`'s key pair, or, with none, from workload
|
|
23
|
+
* attestation (ADR 0026). A user session, when there is one, still wins,
|
|
24
|
+
* and a process that can produce no service token (a local checkout) keeps
|
|
25
|
+
* sending the request anonymous.
|
|
26
|
+
*
|
|
27
|
+
* Why a server wants it: oxy-api charges anonymous traffic to the SOURCE
|
|
28
|
+
* ADDRESS (`rl:general`, and a +500 ms `slowDown` per request past 100 in 15
|
|
29
|
+
* minutes), and a fleet leaves through one NAT address, so every anonymous
|
|
30
|
+
* read a backend makes shares — and past the threshold pays for — one budget.
|
|
31
|
+
* A first-party service token is exempt from both and charged to its own
|
|
32
|
+
* application's budget instead. Measured from Mention's task (2026-09-25):
|
|
33
|
+
* `GET /users/:id` 543–575 ms anonymous past the threshold, `POST
|
|
34
|
+
* /users/by-ids` 19–24 ms with the service token.
|
|
35
|
+
*/
|
|
36
|
+
serviceIdentity?: 'never' | 'when-anonymous';
|
|
16
37
|
/**
|
|
17
38
|
* Enable the per-instance GET response cache. Defaults to `true` (5-minute
|
|
18
39
|
* TTL). Set to `false` to disable caching entirely for this instance — GET
|
|
@@ -241,25 +262,58 @@ export interface LoginResponse {
|
|
|
241
262
|
user: User;
|
|
242
263
|
message?: string;
|
|
243
264
|
}
|
|
265
|
+
/**
|
|
266
|
+
* The actor of a notification as `GET /notifications` populates it — the raw
|
|
267
|
+
* stored name, not a composed display name.
|
|
268
|
+
*/
|
|
269
|
+
export interface NotificationActor {
|
|
270
|
+
_id: string;
|
|
271
|
+
username?: string;
|
|
272
|
+
name?: {
|
|
273
|
+
first: string;
|
|
274
|
+
last: string;
|
|
275
|
+
};
|
|
276
|
+
avatar?: string;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* One Oxy notification exactly as the API serializes it
|
|
280
|
+
* (`packages/api/src/controllers/notification.controller.ts`). `_id` — not
|
|
281
|
+
* `id` — is the key every notification endpoint addresses it by.
|
|
282
|
+
*/
|
|
244
283
|
export interface Notification {
|
|
245
|
-
|
|
246
|
-
|
|
284
|
+
_id: string;
|
|
285
|
+
recipientId: string;
|
|
286
|
+
/** Populated on a list read; the bare id on a create or a mark-read. */
|
|
287
|
+
actorId: string | NotificationActor;
|
|
247
288
|
/** One of `OXY_NOTIFICATION_TYPES` (`@oxy.so/contracts`). */
|
|
248
|
-
type
|
|
289
|
+
type: OxyNotificationType;
|
|
249
290
|
/**
|
|
250
|
-
* `
|
|
251
|
-
*
|
|
252
|
-
|
|
291
|
+
* What `entityId` names: `post` | `reply` | `profile`, or `app` — an opaque id
|
|
292
|
+
* in the notifying application's namespace (`system` notifications only).
|
|
293
|
+
*/
|
|
294
|
+
entityType: OxyNotificationEntityType;
|
|
295
|
+
entityId: string;
|
|
296
|
+
/**
|
|
297
|
+
* `system` notifications only: the text an Oxy service sent about the
|
|
298
|
+
* recipient's own account. Render these instead of composing a sentence from
|
|
299
|
+
* actor + entity.
|
|
253
300
|
*/
|
|
254
301
|
title?: string;
|
|
302
|
+
message?: string;
|
|
255
303
|
/** `system` notifications only: an optional deep link (https or the sending app's scheme). */
|
|
256
304
|
url?: string;
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
305
|
+
read: boolean;
|
|
306
|
+
createdAt: string;
|
|
307
|
+
updatedAt: string;
|
|
308
|
+
}
|
|
309
|
+
/** One page of `GET /notifications`, newest first. */
|
|
310
|
+
export interface NotificationPage {
|
|
311
|
+
notifications: Notification[];
|
|
312
|
+
/** Unread notifications across ALL pages. */
|
|
313
|
+
unreadCount: number;
|
|
314
|
+
hasMore: boolean;
|
|
315
|
+
page: number;
|
|
316
|
+
limit: number;
|
|
263
317
|
}
|
|
264
318
|
export interface Wallet {
|
|
265
319
|
id: string;
|
|
@@ -167,9 +167,9 @@ export interface OxyPagesHeadersOptions {
|
|
|
167
167
|
* violation for a script we never asked for. The rest of the value is Pages'
|
|
168
168
|
* own default, so caching is unchanged.
|
|
169
169
|
*
|
|
170
|
-
* And, because this origin is
|
|
171
|
-
*
|
|
172
|
-
*
|
|
170
|
+
* And, because this origin is where a web account is created, recovered and
|
|
171
|
+
* deleted and every Oxy passkey is asserted (ADR 0028, ADR 0029) — the rest
|
|
172
|
+
* of the holder policy: no `<base>` and no form posts (`base-uri` /
|
|
173
173
|
* `form-action 'none'`), no Referer to anyone, passkeys for this origin only
|
|
174
174
|
* with every other powerful feature off (`Permissions-Policy`), and its
|
|
175
175
|
* responses readable by no other origin (`Cross-Origin-Resource-Policy`).
|
|
@@ -15,10 +15,9 @@
|
|
|
15
15
|
*/
|
|
16
16
|
export declare const CENTRAL_IDP_APEX = "oxy.so";
|
|
17
17
|
/**
|
|
18
|
-
* The IdP's origin, `auth.oxy.so` — the OAuth authorize/consent surface
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* open
|
|
22
|
-
* carriers). Apps open it; they never handle the identity themselves.
|
|
18
|
+
* The IdP's origin, `auth.oxy.so` — the OAuth authorize/consent surface and the
|
|
19
|
+
* one origin that asserts Oxy passkeys: where a person signs in, creates,
|
|
20
|
+
* recovers or deletes a web account, in the window an app opens (ADR 0029).
|
|
21
|
+
* Apps open it; they never run the passkey themselves.
|
|
23
22
|
*/
|
|
24
23
|
export declare const AUTH_WEB_ORIGIN = "https://auth.oxy.so";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxy.so/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.1",
|
|
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",
|
|
@@ -118,8 +118,8 @@
|
|
|
118
118
|
"@oxy.so/telemetry": "^1.1.1",
|
|
119
119
|
"@noble/ciphers": "^1.3.0",
|
|
120
120
|
"@noble/hashes": "^1.8.0",
|
|
121
|
-
"@oxy.so/contracts": "^2.
|
|
122
|
-
"@oxy.so/protocol": "^1.1.
|
|
121
|
+
"@oxy.so/contracts": "^2.2.0",
|
|
122
|
+
"@oxy.so/protocol": "^1.1.2",
|
|
123
123
|
"@scure/bip39": "^1.6.0",
|
|
124
124
|
"buffer": "^6.0.3",
|
|
125
125
|
"express-rate-limit": "^8.6.0",
|
package/src/HttpService.ts
CHANGED
|
@@ -38,6 +38,11 @@ interface JwtPayload {
|
|
|
38
38
|
export type AuthRefreshReason = 'preflight' | 'response-401';
|
|
39
39
|
export type AuthRefreshHandler = (reason: AuthRefreshReason) => Promise<string | null>;
|
|
40
40
|
export type AccessTokenProvider = () => string | null;
|
|
41
|
+
/**
|
|
42
|
+
* Supplies a bearer for a request made while this client holds NO user session
|
|
43
|
+
* — `null` to send it anonymous. See {@link HttpService.setAnonymousAuthProvider}.
|
|
44
|
+
*/
|
|
45
|
+
export type AnonymousAuthProvider = () => Promise<string | null>;
|
|
41
46
|
|
|
42
47
|
/**
|
|
43
48
|
* A low-level authenticated request whose response body remains unread.
|
|
@@ -378,6 +383,7 @@ export class HttpService {
|
|
|
378
383
|
private noFresherTokenThan: string | null = null;
|
|
379
384
|
private authRefreshHandler: AuthRefreshHandler | null = null;
|
|
380
385
|
private accessTokenProvider: AccessTokenProvider | null = null;
|
|
386
|
+
private anonymousAuthProvider: AnonymousAuthProvider | null = null;
|
|
381
387
|
private deviceSecretMintInFlight: Promise<DeviceSecretMintOutcome> | null = null;
|
|
382
388
|
/**
|
|
383
389
|
* Bumped by every {@link endSession}. A re-mint captures it before its first
|
|
@@ -1191,7 +1197,7 @@ export class HttpService {
|
|
|
1191
1197
|
private async getAuthHeader(): Promise<string | null> {
|
|
1192
1198
|
const accessToken = this.syncAccessTokenFromProvider();
|
|
1193
1199
|
if (!accessToken) {
|
|
1194
|
-
return
|
|
1200
|
+
return this.getAnonymousAuthHeader();
|
|
1195
1201
|
}
|
|
1196
1202
|
|
|
1197
1203
|
try {
|
|
@@ -1222,6 +1228,23 @@ export class HttpService {
|
|
|
1222
1228
|
}
|
|
1223
1229
|
}
|
|
1224
1230
|
|
|
1231
|
+
/**
|
|
1232
|
+
* The bearer for a request that has no user session behind it, from the
|
|
1233
|
+
* {@link AnonymousAuthProvider} if one is installed. Never throws: a provider
|
|
1234
|
+
* that fails sends the request anonymous, exactly as it went before any
|
|
1235
|
+
* provider existed.
|
|
1236
|
+
*/
|
|
1237
|
+
private async getAnonymousAuthHeader(): Promise<string | null> {
|
|
1238
|
+
if (!this.anonymousAuthProvider) return null;
|
|
1239
|
+
try {
|
|
1240
|
+
const token = await this.anonymousAuthProvider();
|
|
1241
|
+
return token ? `Bearer ${token}` : null;
|
|
1242
|
+
} catch (error) {
|
|
1243
|
+
this.logger.debug('Anonymous auth provider failed; sending the request anonymous:', error);
|
|
1244
|
+
return null;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1225
1248
|
async refreshAccessToken(reason: AuthRefreshReason): Promise<string | null> {
|
|
1226
1249
|
if (!this.authRefreshHandler) {
|
|
1227
1250
|
return null;
|
|
@@ -1565,6 +1588,17 @@ export class HttpService {
|
|
|
1565
1588
|
this.accessTokenProvider = provider;
|
|
1566
1589
|
}
|
|
1567
1590
|
|
|
1591
|
+
/**
|
|
1592
|
+
* Authenticate requests that carry no user session with the bearer this
|
|
1593
|
+
* provider returns — in practice a backend's own service token (see
|
|
1594
|
+
* `OxyConfig.serviceIdentity`). A user session always wins: the provider is
|
|
1595
|
+
* consulted only when there is no access token, and never for `skipAuth`
|
|
1596
|
+
* requests. `null` removes it.
|
|
1597
|
+
*/
|
|
1598
|
+
setAnonymousAuthProvider(provider: AnonymousAuthProvider | null): void {
|
|
1599
|
+
this.anonymousAuthProvider = provider;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1568
1602
|
clearTokens(): void {
|
|
1569
1603
|
this.tokenStore.clearTokens();
|
|
1570
1604
|
// Drop the response cache on logout. The cache is identity-scoped, so a
|