@oxyhq/core 17.0.3 → 18.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.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/boot/sessionColdBoot.js +4 -5
- package/dist/cjs/mixins/OxyServices.accounts.js +36 -0
- package/dist/cjs/mixins/OxyServices.deviceBoot.js +3 -2
- package/dist/cjs/session/refresh.js +9 -14
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/boot/sessionColdBoot.js +4 -5
- package/dist/esm/mixins/OxyServices.accounts.js +36 -0
- package/dist/esm/mixins/OxyServices.deviceBoot.js +3 -2
- package/dist/esm/session/refresh.js +9 -14
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/mixins/OxyServices.accounts.d.ts +77 -3
- package/dist/types/mixins/OxyServices.deviceBoot.d.ts +3 -2
- package/dist/types/models/interfaces.d.ts +17 -1
- package/dist/types/session/refresh.d.ts +13 -18
- package/package.json +2 -2
- package/src/boot/sessionColdBoot.ts +4 -5
- package/src/index.ts +3 -0
- package/src/mixins/OxyServices.accounts.ts +118 -5
- package/src/mixins/OxyServices.deviceBoot.ts +3 -2
- package/src/models/interfaces.ts +17 -0
- package/src/session/refresh.ts +15 -20
|
@@ -134,10 +134,9 @@ async function runSessionColdBoot(opts) {
|
|
|
134
134
|
// origin persisted a deviceId + deviceSecret, mint a short access token with
|
|
135
135
|
// a single bearer-less POST (no cookie, no navigation). The mint itself runs
|
|
136
136
|
// through `refreshDeviceSecretArm`, which acquires the client's PROCESS-WIDE
|
|
137
|
-
// single-flight, persists
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
// the durable store always converges on the true `current` secret.
|
|
137
|
+
// single-flight, persists `nextDeviceSecret` BEFORE planting the token, and
|
|
138
|
+
// returns a classified outcome — so concurrent mint lanes share one in-flight
|
|
139
|
+
// request and the durable store always converges on the server's credential.
|
|
141
140
|
steps.push({
|
|
142
141
|
id: 'device-secret-mint',
|
|
143
142
|
// Network step — skip entirely when the caller reports the device offline so
|
|
@@ -156,7 +155,7 @@ async function runSessionColdBoot(opts) {
|
|
|
156
155
|
const result = await (0, refresh_1.refreshDeviceSecretArm)({ oxy, store, pin });
|
|
157
156
|
switch (result.status) {
|
|
158
157
|
case 'ok':
|
|
159
|
-
// The arm persisted
|
|
158
|
+
// The arm persisted nextDeviceSecret and planted the token.
|
|
160
159
|
return {
|
|
161
160
|
kind: 'session',
|
|
162
161
|
session: {
|
|
@@ -126,6 +126,42 @@ function OxyServicesAccountsMixin(Base) {
|
|
|
126
126
|
throw this.handleError(error);
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Mint a `channel` account under `ownerUserId` via service auth
|
|
131
|
+
* (`accounts:provision` scope). Requires `configureServiceAuth` first.
|
|
132
|
+
*/
|
|
133
|
+
async provisionChannelAccount(data) {
|
|
134
|
+
try {
|
|
135
|
+
return await this.makeServiceRequest('POST', '/accounts/service/channels', data);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
throw this.handleError(error);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Grant membership on a channel account via service auth
|
|
143
|
+
* (`accounts:provision` scope).
|
|
144
|
+
*/
|
|
145
|
+
async provisionChannelMember(channelAccountId, data) {
|
|
146
|
+
try {
|
|
147
|
+
return await this.makeServiceRequest('POST', `/accounts/service/channels/${encodeURIComponent(channelAccountId)}/members`, data);
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
throw this.handleError(error);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Revoke membership on a channel account via service auth
|
|
155
|
+
* (`accounts:provision` scope).
|
|
156
|
+
*/
|
|
157
|
+
async revokeChannelMember(channelAccountId, memberUserId) {
|
|
158
|
+
try {
|
|
159
|
+
return await this.makeServiceRequest('DELETE', `/accounts/service/channels/${encodeURIComponent(channelAccountId)}/members/${encodeURIComponent(memberUserId)}`);
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
throw this.handleError(error);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
129
165
|
/**
|
|
130
166
|
* Update an account's mutable profile fields. Tree placement changes
|
|
131
167
|
* (reparenting) go through the dedicated move endpoint, not here.
|
|
@@ -65,8 +65,9 @@ function OxyServicesDeviceBootMixin(Base) {
|
|
|
65
65
|
* Zero-cookie mint. Present the first-party `deviceId` + `deviceSecret` to
|
|
66
66
|
* `POST /session/device/token` — NO bearer, NO cookies: possession of the
|
|
67
67
|
* secret IS the device-ownership proof. Returns a fresh short access token
|
|
68
|
-
* for the device's active account plus `nextDeviceSecret` (
|
|
69
|
-
*
|
|
68
|
+
* for the device's active account plus `nextDeviceSecret` (on mint, the same
|
|
69
|
+
* proven secret echoed back — rotation happens on sign-in, not mint) and
|
|
70
|
+
* the projected device-session `state`.
|
|
70
71
|
*
|
|
71
72
|
* `skipAuth`: this call carries no bearer, so a 401 must surface DIRECTLY —
|
|
72
73
|
* never trigger `HttpService`'s 401→refresh→retry dance. The cold boot / re-
|
|
@@ -40,19 +40,14 @@ const MIN_SCHEDULE_DELAY_MS = 1000;
|
|
|
40
40
|
const MIN_FAILURE_BACKOFF_MS = 5000;
|
|
41
41
|
const MAX_FAILURE_BACKOFF_MS = 5 * 60000;
|
|
42
42
|
/**
|
|
43
|
-
* Arm 1 — the
|
|
44
|
-
*
|
|
43
|
+
* Arm 1 — the device-secret mint, run under the owning client's PROCESS-WIDE
|
|
44
|
+
* single-flight (`httpService.runSingleFlightDeviceSecretMint`).
|
|
45
45
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* SUPERSEDED secret — after the grace window the next cold boot mint 401s and the
|
|
52
|
-
* user is signed out. Routing EVERY lane through this one single-flight makes
|
|
53
|
-
* concurrent callers await the SAME in-flight mint and all receive its result, so
|
|
54
|
-
* there is exactly one rotation and the store always converges on the true
|
|
55
|
-
* `current` secret.
|
|
46
|
+
* Concurrent lanes (cold boot, the proactive scheduler, a request-time preflight,
|
|
47
|
+
* a 401 retry, the socket token transport, or a tab-focus reconcile) must not
|
|
48
|
+
* each persist a different view of the mint response. Routing EVERY lane through
|
|
49
|
+
* this one single-flight makes concurrent callers await the SAME in-flight mint
|
|
50
|
+
* and all receive its result, so the durable store converges on one credential.
|
|
56
51
|
*
|
|
57
52
|
* On success it persists `nextDeviceSecret` (read-back-verified) BEFORE planting
|
|
58
53
|
* the access token; a failed durable persist yields `persist-failed` WITHOUT
|
|
@@ -123,8 +118,8 @@ async function refreshDeviceSecretArm(deps) {
|
|
|
123
118
|
expiresAt: mint.expiresAt,
|
|
124
119
|
...(bound ? { sessionId: bound.sessionId, userId: bound.accountId } : {}),
|
|
125
120
|
};
|
|
126
|
-
//
|
|
127
|
-
//
|
|
121
|
+
// Persist nextDeviceSecret (read-back-verified) BEFORE planting the token.
|
|
122
|
+
// A failed durable persist must NOT plant.
|
|
128
123
|
const persistedOk = await store.save(next);
|
|
129
124
|
if (!persistedOk) {
|
|
130
125
|
return { status: 'persist-failed' };
|