@oxyhq/core 9.2.0 → 9.2.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/boot/sessionColdBoot.js +13 -0
- package/dist/cjs/index.js +6 -4
- package/dist/cjs/mixins/OxyServices.accounts.js +3 -0
- package/dist/cjs/session/accountDialogController.js +1 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/boot/sessionColdBoot.js +13 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/mixins/OxyServices.accounts.js +1 -0
- package/dist/esm/session/accountDialogController.js +1 -0
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/mixins/OxyServices.accounts.d.ts +8 -0
- package/dist/types/mixins/OxyServices.auth.d.ts +1 -0
- package/dist/types/models/interfaces.d.ts +3 -1
- package/dist/types/models/session.d.ts +6 -0
- package/package.json +1 -1
- package/src/boot/__tests__/sessionColdBoot.test.ts +20 -0
- package/src/boot/sessionColdBoot.ts +13 -0
- package/src/index.ts +3 -0
- package/src/mixins/OxyServices.accounts.ts +9 -0
- package/src/mixins/OxyServices.auth.ts +2 -0
- package/src/models/interfaces.ts +3 -1
- package/src/models/session.ts +6 -0
- package/src/session/__tests__/accountDialogController.test.ts +8 -1
- package/src/session/accountDialogController.ts +9 -1
package/dist/types/index.d.ts
CHANGED
|
@@ -34,7 +34,8 @@ export type { CanonicalUserHandleInput, UserHandleInput } from './utils/userHand
|
|
|
34
34
|
export { normalizeProfileLinks } from './utils/profileLinks';
|
|
35
35
|
export type { ProfileLink, ProfileLinkMetadata } from './utils/profileLinks';
|
|
36
36
|
export type { PublicApplication, ConnectedApp, } from './mixins/OxyServices.connectedApps';
|
|
37
|
-
export type { AccountKind, AccountRelationship, AccountRole, AccountMemberStatus, AccountMemberSource, AccountMember, AccountNode, AccountCredentialType, AccountCredentialEnvironment, AccountCredentialStatus, AccountCredential, AccountCredentialWithSecret, RotateAccountCredentialResult, ListAccountsOptions, CreateAccountInput, UpdateAccountInput, InviteAccountMemberInput, UpdateAccountMemberInput, TransferAccountOwnershipInput, CreateAccountCredentialInput, AccountSuccessResult, SwitchAccountResult, Application, ApplicationType, ApplicationStatus, ApplicationCredential, ApplicationCredentialType, ApplicationCredentialStatus, ApplicationEnvironment, CreateApplicationInput, UpdateApplicationInput, CreateApplicationCredentialInput, ApplicationCredentialWithSecret, RotateApplicationCredentialResult, ApplicationUsagePeriod, ApplicationUsageSummary, ApplicationUsageByDay, ApplicationUsageByEndpoint, ApplicationUsageStats, } from './mixins/OxyServices.accounts';
|
|
37
|
+
export type { AccountKind, OrganizationCategory, AccountRelationship, AccountRole, AccountMemberStatus, AccountMemberSource, AccountMember, AccountNode, AccountCredentialType, AccountCredentialEnvironment, AccountCredentialStatus, AccountCredential, AccountCredentialWithSecret, RotateAccountCredentialResult, ListAccountsOptions, CreateAccountInput, UpdateAccountInput, InviteAccountMemberInput, UpdateAccountMemberInput, TransferAccountOwnershipInput, CreateAccountCredentialInput, AccountSuccessResult, SwitchAccountResult, Application, ApplicationType, ApplicationStatus, ApplicationCredential, ApplicationCredentialType, ApplicationCredentialStatus, ApplicationEnvironment, CreateApplicationInput, UpdateApplicationInput, CreateApplicationCredentialInput, ApplicationCredentialWithSecret, RotateApplicationCredentialResult, ApplicationUsagePeriod, ApplicationUsageSummary, ApplicationUsageByDay, ApplicationUsageByEndpoint, ApplicationUsageStats, } from './mixins/OxyServices.accounts';
|
|
38
|
+
export { ORGANIZATION_CATEGORIES } from './mixins/OxyServices.accounts';
|
|
38
39
|
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';
|
|
39
40
|
export { buildUserDid } from './mixins/OxyServices.identity';
|
|
40
41
|
export type { IdentityRecordType, UnlinkableAuthMethodType, LinkAuthMethodResult, PublishRecordResult, VerifyRecordResult, VerifyDomainResult, RemoveDomainResult, } from './mixins/OxyServices.identity';
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
* registers the switched session into the operator's device-set directly).
|
|
34
34
|
*/
|
|
35
35
|
import type { User } from '../models/interfaces';
|
|
36
|
+
import type { OrganizationCategory } from '@oxyhq/contracts';
|
|
36
37
|
import type { SessionLoginResponse } from '../models/session';
|
|
37
38
|
import type { OxyServicesBase } from '../OxyServices.base';
|
|
38
39
|
/**
|
|
@@ -42,6 +43,9 @@ import type { OxyServicesBase } from '../OxyServices.base';
|
|
|
42
43
|
* and have no direct login.
|
|
43
44
|
*/
|
|
44
45
|
export type AccountKind = 'personal' | 'organization' | 'project' | 'bot';
|
|
46
|
+
/** Real-estate / team taxonomy for `kind: 'organization'` accounts. */
|
|
47
|
+
export type { OrganizationCategory } from '@oxyhq/contracts';
|
|
48
|
+
export { ORGANIZATION_CATEGORIES } from '@oxyhq/contracts';
|
|
45
49
|
/**
|
|
46
50
|
* The calling user's relationship to an account node, as resolved by the API:
|
|
47
51
|
* - `self` — the caller's own personal (root) account.
|
|
@@ -143,6 +147,8 @@ export interface CreateAccountInput {
|
|
|
143
147
|
};
|
|
144
148
|
bio?: string;
|
|
145
149
|
avatar?: string;
|
|
150
|
+
/** Meaningful only when `kind` is `organization`. */
|
|
151
|
+
organizationCategory?: OrganizationCategory;
|
|
146
152
|
}
|
|
147
153
|
/** Input accepted by `updateAccount`. Tree placement changes go through `/move`. */
|
|
148
154
|
export interface UpdateAccountInput {
|
|
@@ -153,6 +159,8 @@ export interface UpdateAccountInput {
|
|
|
153
159
|
};
|
|
154
160
|
bio?: string | null;
|
|
155
161
|
avatar?: string | null;
|
|
162
|
+
/** Clears the category when `null`; only valid on `kind: 'organization'`. */
|
|
163
|
+
organizationCategory?: OrganizationCategory | null;
|
|
156
164
|
}
|
|
157
165
|
/** Input accepted by `inviteAccountMember`. The owner role cannot be invited. */
|
|
158
166
|
export interface InviteAccountMemberInput {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { UserNameResponse } from '@oxyhq/contracts';
|
|
1
|
+
import type { OrganizationCategory, UserNameResponse } from '@oxyhq/contracts';
|
|
2
2
|
export interface OxyConfig {
|
|
3
3
|
baseURL: string;
|
|
4
4
|
cloudURL?: string;
|
|
@@ -129,6 +129,8 @@ export interface User {
|
|
|
129
129
|
};
|
|
130
130
|
isManagedAccount?: boolean;
|
|
131
131
|
managedBy?: string;
|
|
132
|
+
/** Real-estate taxonomy when this user is a `kind: 'organization'` account. */
|
|
133
|
+
organizationCategory?: OrganizationCategory;
|
|
132
134
|
notificationPreferences?: NotificationPreferences;
|
|
133
135
|
userPreferences?: UserPreferences;
|
|
134
136
|
[key: string]: unknown;
|
|
@@ -31,4 +31,10 @@ export interface SessionLoginResponse {
|
|
|
31
31
|
user: MinimalUserData;
|
|
32
32
|
/** JWT access token for API authentication */
|
|
33
33
|
accessToken?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Rotating zero-cookie device credential minted on sign-in / claim. Persisted
|
|
36
|
+
* first-party alongside `deviceId` so cold boot can re-mint via
|
|
37
|
+
* `POST /session/device/token`.
|
|
38
|
+
*/
|
|
39
|
+
deviceSecret?: string;
|
|
34
40
|
}
|
package/package.json
CHANGED
|
@@ -258,6 +258,26 @@ describe('runSessionColdBoot — shared-key-signin (native)', () => {
|
|
|
258
258
|
);
|
|
259
259
|
});
|
|
260
260
|
|
|
261
|
+
it('persists deviceSecret from shared-key verifyChallenge for the next mint lane', async () => {
|
|
262
|
+
const store = createMemoryAuthStateStore();
|
|
263
|
+
const signInWithSharedIdentity = jest.fn(async () => ({
|
|
264
|
+
...sharedSession,
|
|
265
|
+
deviceSecret: 'shared-mint-secret',
|
|
266
|
+
}));
|
|
267
|
+
const { oxy } = makeOxy({ signInWithSharedIdentity });
|
|
268
|
+
|
|
269
|
+
await runSessionColdBoot({ oxy, store, platform: NATIVE });
|
|
270
|
+
|
|
271
|
+
const persisted = await store.load();
|
|
272
|
+
expect(persisted).toMatchObject({
|
|
273
|
+
sessionId: 'sess-shared',
|
|
274
|
+
userId: 'user-shared',
|
|
275
|
+
deviceId: 'dev-1',
|
|
276
|
+
deviceSecret: 'shared-mint-secret',
|
|
277
|
+
accessToken: 'access-shared',
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
261
281
|
it('does NOT run the shared-key lane on web', async () => {
|
|
262
282
|
const store = createMemoryAuthStateStore();
|
|
263
283
|
const signInWithSharedIdentity = jest.fn(async () => sharedSession);
|
|
@@ -156,6 +156,19 @@ export async function runSessionColdBoot(
|
|
|
156
156
|
if (!session?.accessToken) {
|
|
157
157
|
return { kind: 'skip' };
|
|
158
158
|
}
|
|
159
|
+
// `verifyChallenge` mints a rotating deviceSecret; persist it so the next
|
|
160
|
+
// boot can use the faster device-secret-mint lane (sockets + tab-focus
|
|
161
|
+
// re-mint depend on the credential being in the store).
|
|
162
|
+
if (session.deviceId && session.deviceSecret) {
|
|
163
|
+
await store.save({
|
|
164
|
+
sessionId: session.sessionId,
|
|
165
|
+
userId: session.user.id,
|
|
166
|
+
deviceId: session.deviceId,
|
|
167
|
+
deviceSecret: session.deviceSecret,
|
|
168
|
+
accessToken: session.accessToken,
|
|
169
|
+
expiresAt: session.expiresAt,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
159
172
|
return {
|
|
160
173
|
kind: 'session',
|
|
161
174
|
session: {
|
package/src/index.ts
CHANGED
|
@@ -86,6 +86,7 @@ export type {
|
|
|
86
86
|
// ---------------------------------------------------------------------------
|
|
87
87
|
export type {
|
|
88
88
|
AccountKind,
|
|
89
|
+
OrganizationCategory,
|
|
89
90
|
AccountRelationship,
|
|
90
91
|
AccountRole,
|
|
91
92
|
AccountMemberStatus,
|
|
@@ -127,6 +128,8 @@ export type {
|
|
|
127
128
|
ApplicationUsageStats,
|
|
128
129
|
} from './mixins/OxyServices.accounts';
|
|
129
130
|
|
|
131
|
+
export { ORGANIZATION_CATEGORIES } from './mixins/OxyServices.accounts';
|
|
132
|
+
|
|
130
133
|
// ---------------------------------------------------------------------------
|
|
131
134
|
// Reputation (Oxy Trust: ledger, balances, disputes, rules, influence)
|
|
132
135
|
// ---------------------------------------------------------------------------
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
* registers the switched session into the operator's device-set directly).
|
|
34
34
|
*/
|
|
35
35
|
import type { User } from '../models/interfaces';
|
|
36
|
+
import type { OrganizationCategory } from '@oxyhq/contracts';
|
|
36
37
|
import type { SessionLoginResponse } from '../models/session';
|
|
37
38
|
import type { OxyServicesBase } from '../OxyServices.base';
|
|
38
39
|
import { normalizeUserIdentity } from '../utils/userIdentity';
|
|
@@ -50,6 +51,10 @@ import { CACHE_TIMES } from './mixinHelpers';
|
|
|
50
51
|
*/
|
|
51
52
|
export type AccountKind = 'personal' | 'organization' | 'project' | 'bot';
|
|
52
53
|
|
|
54
|
+
/** Real-estate / team taxonomy for `kind: 'organization'` accounts. */
|
|
55
|
+
export type { OrganizationCategory } from '@oxyhq/contracts';
|
|
56
|
+
export { ORGANIZATION_CATEGORIES } from '@oxyhq/contracts';
|
|
57
|
+
|
|
53
58
|
/**
|
|
54
59
|
* The calling user's relationship to an account node, as resolved by the API:
|
|
55
60
|
* - `self` — the caller's own personal (root) account.
|
|
@@ -155,6 +160,8 @@ export interface CreateAccountInput {
|
|
|
155
160
|
name?: { first?: string; last?: string };
|
|
156
161
|
bio?: string;
|
|
157
162
|
avatar?: string;
|
|
163
|
+
/** Meaningful only when `kind` is `organization`. */
|
|
164
|
+
organizationCategory?: OrganizationCategory;
|
|
158
165
|
}
|
|
159
166
|
|
|
160
167
|
/** Input accepted by `updateAccount`. Tree placement changes go through `/move`. */
|
|
@@ -163,6 +170,8 @@ export interface UpdateAccountInput {
|
|
|
163
170
|
name?: { first?: string; last?: string };
|
|
164
171
|
bio?: string | null;
|
|
165
172
|
avatar?: string | null;
|
|
173
|
+
/** Clears the category when `null`; only valid on `kind: 'organization'`. */
|
|
174
|
+
organizationCategory?: OrganizationCategory | null;
|
|
166
175
|
}
|
|
167
176
|
|
|
168
177
|
/** Input accepted by `inviteAccountMember`. The owner role cannot be invited. */
|
|
@@ -664,6 +664,7 @@ export function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(Base: T)
|
|
|
664
664
|
deviceId: string;
|
|
665
665
|
expiresAt: string;
|
|
666
666
|
user: User;
|
|
667
|
+
deviceSecret?: string;
|
|
667
668
|
}> {
|
|
668
669
|
try {
|
|
669
670
|
const res = await this.makeRequest<{
|
|
@@ -672,6 +673,7 @@ export function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(Base: T)
|
|
|
672
673
|
deviceId: string;
|
|
673
674
|
expiresAt: string;
|
|
674
675
|
user: User;
|
|
676
|
+
deviceSecret?: string;
|
|
675
677
|
}>(
|
|
676
678
|
'POST',
|
|
677
679
|
'/auth/session/claim',
|
package/src/models/interfaces.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { UserNameResponse } from '@oxyhq/contracts';
|
|
1
|
+
import type { OrganizationCategory, UserNameResponse } from '@oxyhq/contracts';
|
|
2
2
|
|
|
3
3
|
export interface OxyConfig {
|
|
4
4
|
baseURL: string;
|
|
@@ -142,6 +142,8 @@ export interface User {
|
|
|
142
142
|
// Managed account fields
|
|
143
143
|
isManagedAccount?: boolean;
|
|
144
144
|
managedBy?: string;
|
|
145
|
+
/** Real-estate taxonomy when this user is a `kind: 'organization'` account. */
|
|
146
|
+
organizationCategory?: OrganizationCategory;
|
|
145
147
|
// User-controlled notification preferences. All channels default to on; users
|
|
146
148
|
// opt out per-channel. Updated via `PUT /users/me`.
|
|
147
149
|
notificationPreferences?: NotificationPreferences;
|
package/src/models/session.ts
CHANGED
|
@@ -35,4 +35,10 @@ export interface SessionLoginResponse {
|
|
|
35
35
|
user: MinimalUserData;
|
|
36
36
|
/** JWT access token for API authentication */
|
|
37
37
|
accessToken?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Rotating zero-cookie device credential minted on sign-in / claim. Persisted
|
|
40
|
+
* first-party alongside `deviceId` so cold boot can re-mint via
|
|
41
|
+
* `POST /session/device/token`.
|
|
42
|
+
*/
|
|
43
|
+
deviceSecret?: string;
|
|
38
44
|
}
|
|
@@ -461,6 +461,7 @@ describe('AccountDialogController — sign in with Oxy', () => {
|
|
|
461
461
|
accessToken: 'access-1',
|
|
462
462
|
sessionId: 'sess-1',
|
|
463
463
|
deviceId: 'device-1',
|
|
464
|
+
deviceSecret: 'claimed-secret',
|
|
464
465
|
expiresAt: '2030-01-01T00:00:00Z',
|
|
465
466
|
user: user('a1'),
|
|
466
467
|
});
|
|
@@ -473,7 +474,13 @@ describe('AccountDialogController — sign in with Oxy', () => {
|
|
|
473
474
|
|
|
474
475
|
await jest.advanceTimersByTimeAsync(1000); // second poll → authorized → claim
|
|
475
476
|
expect(oxy.claimSessionByToken).toHaveBeenCalledWith('secret-tok');
|
|
476
|
-
expect(commitSession).toHaveBeenCalledWith(
|
|
477
|
+
expect(commitSession).toHaveBeenCalledWith(
|
|
478
|
+
expect.objectContaining({
|
|
479
|
+
sessionId: 'sess-1',
|
|
480
|
+
accessToken: 'access-1',
|
|
481
|
+
deviceSecret: 'claimed-secret',
|
|
482
|
+
}),
|
|
483
|
+
);
|
|
477
484
|
expect(onSignedIn).toHaveBeenCalledWith(expect.objectContaining({ id: 'a1' }));
|
|
478
485
|
expect(controller.getSnapshot().view).toBe('accounts');
|
|
479
486
|
} finally {
|
|
@@ -638,7 +638,14 @@ export class AccountDialogController {
|
|
|
638
638
|
|
|
639
639
|
private async claimAndComplete(sessionId: string, sessionToken: string): Promise<void> {
|
|
640
640
|
this.setSignIn({ ...this.signIn, phase: 'authorized' });
|
|
641
|
-
let claimed: {
|
|
641
|
+
let claimed: {
|
|
642
|
+
accessToken: string;
|
|
643
|
+
sessionId: string;
|
|
644
|
+
deviceId: string;
|
|
645
|
+
expiresAt: string;
|
|
646
|
+
user: User;
|
|
647
|
+
deviceSecret?: string;
|
|
648
|
+
};
|
|
642
649
|
try {
|
|
643
650
|
claimed = await this.oxyServices.claimSessionByToken(sessionToken);
|
|
644
651
|
} catch (error) {
|
|
@@ -666,6 +673,7 @@ export class AccountDialogController {
|
|
|
666
673
|
expiresAt: claimed.expiresAt ?? '',
|
|
667
674
|
user: minimalUser,
|
|
668
675
|
accessToken: claimed.accessToken,
|
|
676
|
+
...(claimed.deviceSecret ? { deviceSecret: claimed.deviceSecret } : {}),
|
|
669
677
|
},
|
|
670
678
|
minimalUser,
|
|
671
679
|
);
|