@agentxm/registry-auth 0.28.11 → 0.28.13
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/src/auth-client.d.ts +13 -5
- package/dist/src/auth-client.js +80 -39
- package/dist/src/credential-store.d.ts +39 -2
- package/dist/src/credential-store.js +50 -46
- package/dist/src/device-login.d.ts +20 -8
- package/dist/src/device-login.js +9 -4
- package/dist/src/errors.d.ts +37 -12
- package/dist/src/errors.js +20 -0
- package/dist/src/identity.d.ts +46 -0
- package/dist/src/identity.js +49 -0
- package/dist/src/index.d.ts +12 -2
- package/dist/src/index.js +14 -1
- package/dist/src/internal/environment.d.ts +23 -5
- package/dist/src/internal/environment.js +59 -9
- package/dist/src/live.d.ts +1 -0
- package/dist/src/live.js +1 -0
- package/dist/src/login-presenter.d.ts +55 -0
- package/dist/src/login-presenter.js +22 -0
- package/dist/src/login-suggestion.d.ts +18 -0
- package/dist/src/login-suggestion.js +34 -0
- package/dist/src/login.d.ts +80 -0
- package/dist/src/login.js +155 -0
- package/dist/src/logout.d.ts +35 -0
- package/dist/src/logout.js +37 -0
- package/dist/src/loopback-login.js +2 -1
- package/dist/src/loopback-server.d.ts +3 -1
- package/dist/src/loopback-server.js +51 -7
- package/dist/src/pending-publish-authorization-store.d.ts +33 -0
- package/dist/src/pending-publish-authorization-store.js +90 -0
- package/dist/src/publish-authorization-polling.d.ts +13 -0
- package/dist/src/publish-authorization-polling.js +176 -0
- package/dist/src/publish-authorization.d.ts +4 -1
- package/dist/src/publish-authorization.js +24 -5
- package/dist/src/selected-registry.d.ts +25 -0
- package/dist/src/selected-registry.js +18 -0
- package/dist/src/step-up.d.ts +49 -0
- package/dist/src/step-up.js +174 -0
- package/dist/src/testing.d.ts +1 -0
- package/dist/src/testing.js +1 -0
- package/dist/src/tokens.d.ts +57 -0
- package/dist/src/tokens.js +78 -0
- package/package.json +7 -6
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PublishAuthorizationDelivery, PublishAuthorizationPollingStatus } from "@agentxm/registry-protocol/unstable/publish-authorization";
|
|
1
2
|
/**
|
|
2
3
|
* AuthClient Effect service — device flow login, token refresh, revocation, identity queries.
|
|
3
4
|
*
|
|
@@ -16,10 +17,10 @@ import * as ServiceMap from "effect/Context";
|
|
|
16
17
|
import * as Effect from "effect/Effect";
|
|
17
18
|
import * as Layer from "effect/Layer";
|
|
18
19
|
import { type Handle } from "@agentxm/extension-model/unstable/extensions/handle";
|
|
19
|
-
import { type PublishVisibility } from "@agentxm/registry-protocol/unstable/publish
|
|
20
|
+
import { type PublishVisibility } from "@agentxm/registry-protocol/unstable/publish";
|
|
20
21
|
import { type PreviewPublicationSetRequest, type PreviewPublicationSetResponse, type Sha256Hex } from "@agentxm/registry-protocol/unstable/registry/publication-set";
|
|
21
22
|
import { type NormalizedTokenResponse } from "./oauth-contract.js";
|
|
22
|
-
import { RegistryUrl } from "@agentxm/registry-client";
|
|
23
|
+
import { GeneratedRegistryClient, RegistryUrl } from "@agentxm/registry-client";
|
|
23
24
|
import { type AuthError, type StepUpRequest } from "./errors.js";
|
|
24
25
|
export declare const OIDC_LOGIN_SCOPES: readonly ["openid", "profile", "email", "offline_access"];
|
|
25
26
|
export declare const BASELINE_REGISTRY_LOGIN_SCOPES: readonly ["extensions:read", "account:read"];
|
|
@@ -102,16 +103,20 @@ export interface ExchangePkceCodeParams {
|
|
|
102
103
|
}
|
|
103
104
|
export interface CreatePublishAuthorizationRequestParams {
|
|
104
105
|
readonly registryUrl: string;
|
|
105
|
-
readonly
|
|
106
|
-
readonly state: string;
|
|
107
|
-
readonly codeChallenge: string;
|
|
106
|
+
readonly delivery: PublishAuthorizationDelivery;
|
|
108
107
|
readonly publicationSet: PreviewPublicationSetRequest;
|
|
109
108
|
}
|
|
110
109
|
export interface PublishAuthorizationRequestResponse {
|
|
110
|
+
readonly interval: number;
|
|
111
111
|
readonly requestId: string;
|
|
112
112
|
readonly authorizationUrl: string;
|
|
113
113
|
readonly expiresAt: DateTime.Utc;
|
|
114
114
|
}
|
|
115
|
+
export interface PublishAuthorizationPollingParams {
|
|
116
|
+
readonly registryUrl: string;
|
|
117
|
+
readonly requestId: string;
|
|
118
|
+
readonly initiatorProof: string;
|
|
119
|
+
}
|
|
115
120
|
export interface ExchangePublishAuthorizationCodeParams {
|
|
116
121
|
readonly registryUrl: string;
|
|
117
122
|
readonly code: string;
|
|
@@ -156,6 +161,8 @@ export interface AuthClientService {
|
|
|
156
161
|
readonly getAuthorizationIssuer: () => string;
|
|
157
162
|
readonly exchangePkceCode: (params: ExchangePkceCodeParams) => Effect.Effect<NormalizedTokenResponse, AuthError>;
|
|
158
163
|
readonly createPublishAuthorizationRequest: (params: CreatePublishAuthorizationRequestParams) => Effect.Effect<PublishAuthorizationRequestResponse, AuthError>;
|
|
164
|
+
readonly pollPublishAuthorization: (params: PublishAuthorizationPollingParams) => Effect.Effect<PublishAuthorizationPollingStatus, AuthError>;
|
|
165
|
+
readonly exchangePublishAuthorization: (params: PublishAuthorizationPollingParams) => Effect.Effect<PublishAuthorizationExchangeResponse, AuthError>;
|
|
159
166
|
readonly exchangePublishAuthorizationCode: (params: ExchangePublishAuthorizationCodeParams) => Effect.Effect<PublishAuthorizationExchangeResponse, AuthError>;
|
|
160
167
|
readonly initiateDeviceFlow: (options?: LoginScopeOptions) => Effect.Effect<DeviceFlowResponse, AuthError>;
|
|
161
168
|
readonly pollDeviceToken: (deviceCode: string, interval: number) => Effect.Effect<NormalizedTokenResponse, AuthError>;
|
|
@@ -167,6 +174,7 @@ export interface AuthClientService {
|
|
|
167
174
|
readonly limit?: number;
|
|
168
175
|
readonly cursor?: string;
|
|
169
176
|
}) => Effect.Effect<TokenListResponse, AuthError>;
|
|
177
|
+
readonly getStepUpRequest: (accessToken: string, requestId: string) => Effect.Effect<GeneratedRegistryClient.StepUpRequestStatusResponse, AuthError>;
|
|
170
178
|
readonly waitForStepUpRequest: (accessToken: string, statusUrl: string, intervalSeconds: number) => Effect.Effect<void, AuthError>;
|
|
171
179
|
readonly deleteToken: (accessToken: string, tokenId: string, options?: DeleteTokenOptions) => Effect.Effect<void, AuthError>;
|
|
172
180
|
}
|
package/dist/src/auth-client.js
CHANGED
|
@@ -23,10 +23,10 @@ import * as Schedule from "effect/Schedule";
|
|
|
23
23
|
import * as Schema from "effect/Schema";
|
|
24
24
|
import { DateTimeUtcSchema } from "@agentxm/extension-model/unstable/date-time";
|
|
25
25
|
import { normalizeHandle } from "@agentxm/extension-model/unstable/extensions/handle";
|
|
26
|
-
import { PublishVisibilitySchema, } from "@agentxm/registry-protocol/unstable/publish
|
|
26
|
+
import { PublishVisibilitySchema, } from "@agentxm/registry-protocol/unstable/publish";
|
|
27
27
|
import { PreviewPublicationSetResponseSchema, } from "@agentxm/registry-protocol/unstable/registry/publication-set";
|
|
28
28
|
import {} from "./oauth-contract.js";
|
|
29
|
-
import { GeneratedRegistryClient, RegistryUrl, captureRegistryErrorResponseBodies, getString, isHttpClientError, isRegistryClientError, isRegistryClientFailure, isTransientHttpClientError, mapRegistryFailure, } from "@agentxm/registry-client";
|
|
29
|
+
import { GeneratedRegistryClient, executeRegistryRequest, RegistryUrl, captureRegistryErrorResponseBodies, getString, isHttpClientError, isRegistryClientError, isRegistryClientFailure, isTransientHttpClientError, mapRegistryFailure, } from "@agentxm/registry-client";
|
|
30
30
|
import { AuthExchangeFailed, DeviceLoginCodeExpired, DeviceLoginDenied, RegistryAuthFailed, StepUpRequired, } from "./errors.js";
|
|
31
31
|
// -----------------------------------------------------------------------------
|
|
32
32
|
// Constants
|
|
@@ -93,6 +93,29 @@ const PublishAuthorizationExchangeResponseSchema = Schema.Union([
|
|
|
93
93
|
grants: Schema.Tuple([]),
|
|
94
94
|
}),
|
|
95
95
|
]);
|
|
96
|
+
const decodePublishAuthorizationExchange = Effect.fn("AuthClient.decodePublishAuthorizationExchange")(function* (registryUrl, response) {
|
|
97
|
+
if (!Schema.is(PublishAuthorizationExchangeResponseSchema)(response)) {
|
|
98
|
+
return yield* mapRegistryAuthError(registryUrl, "The Registry is incompatible with exact publish authorization", new Error("Invalid publish capability response"));
|
|
99
|
+
}
|
|
100
|
+
if (response.status === "blocked") {
|
|
101
|
+
return response;
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
status: "admitted",
|
|
105
|
+
preview: response.preview,
|
|
106
|
+
grants: response.grants.map((grant) => ({
|
|
107
|
+
accessToken: grant.access_token,
|
|
108
|
+
expiresAt: grant.expires_at,
|
|
109
|
+
scope: grant.scope,
|
|
110
|
+
publishRequestId: grant.publish_request_id,
|
|
111
|
+
visibilityContract: grant.visibility_contract,
|
|
112
|
+
visibility: grant.visibility,
|
|
113
|
+
condition: grant.condition,
|
|
114
|
+
publicationSetDigest: grant.publication_set_digest,
|
|
115
|
+
publicationDescriptorDigest: grant.publication_descriptor_digest,
|
|
116
|
+
})),
|
|
117
|
+
};
|
|
118
|
+
});
|
|
96
119
|
const deriveAuthorizationOrigin = (registryUrl) => {
|
|
97
120
|
const url = new URL(registryUrl);
|
|
98
121
|
if (url.origin === "https://registry.agentxm.ai") {
|
|
@@ -101,14 +124,13 @@ const deriveAuthorizationOrigin = (registryUrl) => {
|
|
|
101
124
|
if (url.origin === "https://registry-dev.agentxm.ai") {
|
|
102
125
|
return "https://web-dev.agentxm.ai";
|
|
103
126
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return `${url.protocol}//${url.hostname}:4200`;
|
|
127
|
+
const localHosts = ["localhost", "127.0.0.1", "[::1]"];
|
|
128
|
+
const registryPort = Number(url.port);
|
|
129
|
+
if (localHosts.some((host) => host === url.hostname) &&
|
|
130
|
+
Number.isInteger(registryPort) &&
|
|
131
|
+
registryPort >= 4300 &&
|
|
132
|
+
registryPort <= 4399) {
|
|
133
|
+
return `${url.protocol}//${url.hostname}:${String(registryPort - 100)}`;
|
|
112
134
|
}
|
|
113
135
|
return url.origin;
|
|
114
136
|
};
|
|
@@ -298,24 +320,54 @@ export const AuthClientLive = Layer.effect(AuthClient, Effect.gen(function* () {
|
|
|
298
320
|
const createPublishAuthorizationRequest = Effect.fn("AuthClient.createPublishAuthorizationRequest")(function* (params) {
|
|
299
321
|
const publishClient = makeGeneratedAuthClient(httpClient, params.registryUrl);
|
|
300
322
|
const publicationSet = yield* Schema.encodeUnknownEffect(GeneratedRegistryClient.PreviewPublicationSetRequest)(params.publicationSet).pipe(Effect.mapError((error) => mapRegistryAuthError(params.registryUrl, "Could not encode publish authorization request", error)));
|
|
301
|
-
const response = yield* publishClient
|
|
302
|
-
.AuthCreatePublishAuthorizationRequest({
|
|
323
|
+
const response = yield* executeRegistryRequest(publishClient.AuthCreatePublishAuthorizationRequest({
|
|
303
324
|
payload: {
|
|
304
325
|
client_id: CLIENT_ID,
|
|
305
|
-
|
|
306
|
-
state: params.state,
|
|
307
|
-
code_challenge: params.codeChallenge,
|
|
308
|
-
code_challenge_method: "S256",
|
|
326
|
+
delivery: params.delivery,
|
|
309
327
|
publication_set: publicationSet,
|
|
310
328
|
},
|
|
311
|
-
})
|
|
312
|
-
|
|
329
|
+
}), {
|
|
330
|
+
operation: "createPublishAuthorizationRequest",
|
|
331
|
+
request: {
|
|
332
|
+
service: "registry",
|
|
333
|
+
method: "POST",
|
|
334
|
+
url: `${params.registryUrl.replace(/\/+$/, "")}/v1/auth/publish-requests`,
|
|
335
|
+
},
|
|
336
|
+
replaySafety: { kind: "mutation" },
|
|
337
|
+
mapError: (error) => mapRegistryAuthError(params.registryUrl, "Could not create publish authorization request; no automatic replacement was attempted", error),
|
|
338
|
+
});
|
|
313
339
|
return {
|
|
340
|
+
interval: response.interval,
|
|
314
341
|
requestId: response.request_id,
|
|
315
342
|
authorizationUrl: response.authorization_url,
|
|
316
343
|
expiresAt: response.expires_at,
|
|
317
344
|
};
|
|
318
345
|
});
|
|
346
|
+
const pollPublishAuthorization = Effect.fn("AuthClient.pollPublishAuthorization")(function* (params) {
|
|
347
|
+
const publishClient = makeGeneratedAuthClient(httpClient, params.registryUrl);
|
|
348
|
+
const requestRef = `${params.registryUrl.replace(/\/+$/, "")}/v1/auth/publish-requests/${params.requestId}`;
|
|
349
|
+
return yield* executeRegistryRequest(publishClient.AuthPollPublishAuthorization(params.requestId, {
|
|
350
|
+
payload: { initiator_proof: params.initiatorProof },
|
|
351
|
+
}), {
|
|
352
|
+
operation: "pollPublishAuthorization",
|
|
353
|
+
request: { service: "registry", method: "POST", url: `${requestRef}/status` },
|
|
354
|
+
replaySafety: { kind: "safe" },
|
|
355
|
+
mapError: (error) => mapRegistryAuthError(params.registryUrl, `Could not read publish authorization status. Resume the same command with --authorization-request ${requestRef}`, error),
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
const exchangePublishAuthorization = Effect.fn("AuthClient.exchangePublishAuthorization")(function* (params) {
|
|
359
|
+
const publishClient = makeGeneratedAuthClient(httpClient, params.registryUrl);
|
|
360
|
+
const requestRef = `${params.registryUrl.replace(/\/+$/, "")}/v1/auth/publish-requests/${params.requestId}`;
|
|
361
|
+
const response = yield* executeRegistryRequest(publishClient.AuthExchangePublishAuthorization(params.requestId, {
|
|
362
|
+
payload: { initiator_proof: params.initiatorProof },
|
|
363
|
+
}), {
|
|
364
|
+
operation: "exchangePublishAuthorization",
|
|
365
|
+
request: { service: "registry", method: "POST", url: `${requestRef}/exchange` },
|
|
366
|
+
replaySafety: { kind: "mutation" },
|
|
367
|
+
mapError: (error) => mapRegistryAuthError(params.registryUrl, `Publish authorization exchange did not complete. Resume with --authorization-request ${requestRef} to check its status before requesting new consent`, error),
|
|
368
|
+
});
|
|
369
|
+
return yield* decodePublishAuthorizationExchange(params.registryUrl, response);
|
|
370
|
+
});
|
|
319
371
|
const exchangePublishAuthorizationCode = Effect.fn("AuthClient.exchangePublishAuthorizationCode")(function* (params) {
|
|
320
372
|
const publishClient = makeGeneratedAuthClient(httpClient, params.registryUrl);
|
|
321
373
|
const response = yield* publishClient
|
|
@@ -345,27 +397,7 @@ export const AuthClientLive = Layer.effect(AuthClient, Effect.gen(function* () {
|
|
|
345
397
|
})
|
|
346
398
|
: mapped;
|
|
347
399
|
}));
|
|
348
|
-
|
|
349
|
-
return yield* mapRegistryAuthError(params.registryUrl, "The Registry is incompatible with exact publish authorization", response);
|
|
350
|
-
}
|
|
351
|
-
if (response.status === "blocked") {
|
|
352
|
-
return response;
|
|
353
|
-
}
|
|
354
|
-
return {
|
|
355
|
-
status: "admitted",
|
|
356
|
-
preview: response.preview,
|
|
357
|
-
grants: response.grants.map((grant) => ({
|
|
358
|
-
accessToken: grant.access_token,
|
|
359
|
-
expiresAt: grant.expires_at,
|
|
360
|
-
scope: grant.scope,
|
|
361
|
-
publishRequestId: grant.publish_request_id,
|
|
362
|
-
visibilityContract: grant.visibility_contract,
|
|
363
|
-
visibility: grant.visibility,
|
|
364
|
-
condition: grant.condition,
|
|
365
|
-
publicationSetDigest: grant.publication_set_digest,
|
|
366
|
-
publicationDescriptorDigest: grant.publication_descriptor_digest,
|
|
367
|
-
})),
|
|
368
|
-
};
|
|
400
|
+
return yield* decodePublishAuthorizationExchange(params.registryUrl, response);
|
|
369
401
|
});
|
|
370
402
|
const initiateDeviceFlow = Effect.fn("AuthClient.initiateDeviceFlow")(function* (options) {
|
|
371
403
|
const response = yield* client
|
|
@@ -496,6 +528,9 @@ export const AuthClientLive = Layer.effect(AuthClient, Effect.gen(function* () {
|
|
|
496
528
|
cursor: decoded.cursor,
|
|
497
529
|
};
|
|
498
530
|
});
|
|
531
|
+
const getStepUpRequest = (accessToken, requestId) => makeGeneratedAuthClient(httpClient, registryUrl, accessToken)
|
|
532
|
+
.AuthGetStepUpRequest(requestId, undefined)
|
|
533
|
+
.pipe(Effect.mapError((error) => mapRegistryAuthError(registryUrl, "Could not read step-up request", error)));
|
|
499
534
|
const waitForStepUpRequest = Effect.fn("AuthClient.waitForStepUpRequest")(function* (accessToken, statusUrl, intervalSeconds) {
|
|
500
535
|
const parsedStatusUrl = new URL(statusUrl);
|
|
501
536
|
const requestId = parsedStatusUrl.pathname.slice(parsedStatusUrl.pathname.lastIndexOf("/") + 1);
|
|
@@ -558,6 +593,8 @@ export const AuthClientLive = Layer.effect(AuthClient, Effect.gen(function* () {
|
|
|
558
593
|
getAuthorizationIssuer,
|
|
559
594
|
exchangePkceCode,
|
|
560
595
|
createPublishAuthorizationRequest,
|
|
596
|
+
pollPublishAuthorization,
|
|
597
|
+
exchangePublishAuthorization,
|
|
561
598
|
exchangePublishAuthorizationCode,
|
|
562
599
|
initiateDeviceFlow,
|
|
563
600
|
pollDeviceToken,
|
|
@@ -566,6 +603,7 @@ export const AuthClientLive = Layer.effect(AuthClient, Effect.gen(function* () {
|
|
|
566
603
|
getMe,
|
|
567
604
|
createToken,
|
|
568
605
|
listTokens,
|
|
606
|
+
getStepUpRequest,
|
|
569
607
|
waitForStepUpRequest,
|
|
570
608
|
deleteToken,
|
|
571
609
|
};
|
|
@@ -584,6 +622,8 @@ export const AuthClientTest = (overrides) => Layer.succeed(AuthClient, {
|
|
|
584
622
|
category: "auth",
|
|
585
623
|
detail: "Not implemented in test",
|
|
586
624
|
})),
|
|
625
|
+
pollPublishAuthorization: () => Effect.fail(new RegistryAuthFailed({ category: "auth", detail: "Not implemented in test" })),
|
|
626
|
+
exchangePublishAuthorization: () => Effect.fail(new RegistryAuthFailed({ category: "auth", detail: "Not implemented in test" })),
|
|
587
627
|
exchangePublishAuthorizationCode: () => Effect.fail(new RegistryAuthFailed({
|
|
588
628
|
category: "auth",
|
|
589
629
|
detail: "Not implemented in test",
|
|
@@ -613,6 +653,7 @@ export const AuthClientTest = (overrides) => Layer.succeed(AuthClient, {
|
|
|
613
653
|
category: "auth",
|
|
614
654
|
detail: "Not implemented in test",
|
|
615
655
|
})),
|
|
656
|
+
getStepUpRequest: () => Effect.fail(new RegistryAuthFailed({ category: "auth", detail: "Not implemented in test" })),
|
|
616
657
|
waitForStepUpRequest: () => Effect.fail(new RegistryAuthFailed({
|
|
617
658
|
category: "auth",
|
|
618
659
|
detail: "Not implemented in test",
|
|
@@ -68,8 +68,45 @@ export declare const makePersistedCredentialsUnsupportedError: () => AuthTokenPo
|
|
|
68
68
|
export declare const CredentialStoreLive: Layer.Layer<CredentialStore, never, FileSystem.FileSystem | Path.Path>;
|
|
69
69
|
/**
|
|
70
70
|
* Decorates a credential store with a per-layer, per-origin read memo.
|
|
71
|
-
*
|
|
72
|
-
*
|
|
71
|
+
*
|
|
72
|
+
* Reads go through an `Effect.Cache` keyed by registry origin, which owns the
|
|
73
|
+
* concurrent-lookup sharing, per-key invalidation, and result-dependent expiry
|
|
74
|
+
* that this layer previously hand-rolled with a value map, a semaphore map, and
|
|
75
|
+
* a double-checked read.
|
|
76
|
+
*
|
|
77
|
+
* Capacity is `Number.POSITIVE_INFINITY`: the cache never evicts on size, which
|
|
78
|
+
* preserves the unbounded map this layer already used. The key space is the set
|
|
79
|
+
* of distinct registry origins a single process contacts — the configured
|
|
80
|
+
* registry plus any origins named by workspace extension sources — so there is
|
|
81
|
+
* no measured workload that justifies a numeric bound. The bound that matters
|
|
82
|
+
* is lifetime, and it is the layer's: the cache is created per
|
|
83
|
+
* `CredentialStoreSessionLive` construction and released with it, so a CLI
|
|
84
|
+
* session is its longest possible life.
|
|
85
|
+
*
|
|
86
|
+
* Expiry is result-dependent. Successful reads — including a successful empty
|
|
87
|
+
* read — live for the session, so repeated authentication paths pay one load
|
|
88
|
+
* per origin. Failed reads expire immediately, so a failure is never memoized
|
|
89
|
+
* and a later request retries against the store.
|
|
90
|
+
*
|
|
91
|
+
* Callers that arrive while a lookup is in flight share it rather than issuing
|
|
92
|
+
* their own; callers that arrive after a failed lookup resolves start a new
|
|
93
|
+
* one. That replaces the old per-origin semaphore, under which every waiter on
|
|
94
|
+
* a failing origin retried serially. Sharing is safe here because each auth
|
|
95
|
+
* path issues a single load and none of them depends on serial retry.
|
|
96
|
+
*
|
|
97
|
+
* `Effect.onInterrupt` is load-path scaffolding, not decoration. `Cache.get`
|
|
98
|
+
* runs its lookup on a detached daemon fiber and leaves the pending entry in
|
|
99
|
+
* the map when the only caller is interrupted, so without this guard the next
|
|
100
|
+
* read of that origin would await an abandoned lookup forever. Invalidating on
|
|
101
|
+
* interrupt restores the pre-cache behavior, where an interrupted read left
|
|
102
|
+
* nothing behind and the next read simply ran again.
|
|
103
|
+
*
|
|
104
|
+
* Known limitation, unchanged by this layer's move to `Cache` and not fixed by
|
|
105
|
+
* it: a `save` or `clear` whose invalidation lands while a read of the same
|
|
106
|
+
* origin is still in flight can be overwritten when that read populates the
|
|
107
|
+
* memo, leaving the pre-write value cached for the rest of the session. The
|
|
108
|
+
* previous value-map-and-semaphore memo lost the same invalidation through its
|
|
109
|
+
* own write-after-invalidate window.
|
|
73
110
|
*/
|
|
74
111
|
export declare const CredentialStoreSessionLive: Layer.Layer<CredentialStore, never, CredentialStore>;
|
|
75
112
|
export declare const CredentialStoreTest: (tier?: StorageTier, initialData?: CredentialFile, allowsPersistedCredentials?: boolean) => Layer.Layer<CredentialStore, never, never>;
|
|
@@ -20,12 +20,13 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
20
20
|
import * as FileSystem from "effect/FileSystem";
|
|
21
21
|
import * as Path from "effect/Path";
|
|
22
22
|
import * as ServiceMap from "effect/Context";
|
|
23
|
+
import * as Cache from "effect/Cache";
|
|
24
|
+
import * as Duration from "effect/Duration";
|
|
23
25
|
import * as Effect from "effect/Effect";
|
|
26
|
+
import * as Exit from "effect/Exit";
|
|
24
27
|
import * as Layer from "effect/Layer";
|
|
25
28
|
import * as Option from "effect/Option";
|
|
26
|
-
import * as Ref from "effect/Ref";
|
|
27
29
|
import * as Schema from "effect/Schema";
|
|
28
|
-
import * as Semaphore from "effect/Semaphore";
|
|
29
30
|
import * as lockfile from "proper-lockfile";
|
|
30
31
|
import { decodeHandleSync } from "@agentxm/extension-model/unstable/extensions/handle";
|
|
31
32
|
import { AuthTokenPolicyRequired, RegistryAuthFailed } from "./errors.js";
|
|
@@ -340,57 +341,60 @@ export const CredentialStoreLive = Layer.effect(CredentialStore, Effect.gen(func
|
|
|
340
341
|
}));
|
|
341
342
|
/**
|
|
342
343
|
* Decorates a credential store with a per-layer, per-origin read memo.
|
|
343
|
-
*
|
|
344
|
-
*
|
|
344
|
+
*
|
|
345
|
+
* Reads go through an `Effect.Cache` keyed by registry origin, which owns the
|
|
346
|
+
* concurrent-lookup sharing, per-key invalidation, and result-dependent expiry
|
|
347
|
+
* that this layer previously hand-rolled with a value map, a semaphore map, and
|
|
348
|
+
* a double-checked read.
|
|
349
|
+
*
|
|
350
|
+
* Capacity is `Number.POSITIVE_INFINITY`: the cache never evicts on size, which
|
|
351
|
+
* preserves the unbounded map this layer already used. The key space is the set
|
|
352
|
+
* of distinct registry origins a single process contacts — the configured
|
|
353
|
+
* registry plus any origins named by workspace extension sources — so there is
|
|
354
|
+
* no measured workload that justifies a numeric bound. The bound that matters
|
|
355
|
+
* is lifetime, and it is the layer's: the cache is created per
|
|
356
|
+
* `CredentialStoreSessionLive` construction and released with it, so a CLI
|
|
357
|
+
* session is its longest possible life.
|
|
358
|
+
*
|
|
359
|
+
* Expiry is result-dependent. Successful reads — including a successful empty
|
|
360
|
+
* read — live for the session, so repeated authentication paths pay one load
|
|
361
|
+
* per origin. Failed reads expire immediately, so a failure is never memoized
|
|
362
|
+
* and a later request retries against the store.
|
|
363
|
+
*
|
|
364
|
+
* Callers that arrive while a lookup is in flight share it rather than issuing
|
|
365
|
+
* their own; callers that arrive after a failed lookup resolves start a new
|
|
366
|
+
* one. That replaces the old per-origin semaphore, under which every waiter on
|
|
367
|
+
* a failing origin retried serially. Sharing is safe here because each auth
|
|
368
|
+
* path issues a single load and none of them depends on serial retry.
|
|
369
|
+
*
|
|
370
|
+
* `Effect.onInterrupt` is load-path scaffolding, not decoration. `Cache.get`
|
|
371
|
+
* runs its lookup on a detached daemon fiber and leaves the pending entry in
|
|
372
|
+
* the map when the only caller is interrupted, so without this guard the next
|
|
373
|
+
* read of that origin would await an abandoned lookup forever. Invalidating on
|
|
374
|
+
* interrupt restores the pre-cache behavior, where an interrupted read left
|
|
375
|
+
* nothing behind and the next read simply ran again.
|
|
376
|
+
*
|
|
377
|
+
* Known limitation, unchanged by this layer's move to `Cache` and not fixed by
|
|
378
|
+
* it: a `save` or `clear` whose invalidation lands while a read of the same
|
|
379
|
+
* origin is still in flight can be overwritten when that read populates the
|
|
380
|
+
* memo, leaving the pre-write value cached for the rest of the session. The
|
|
381
|
+
* previous value-map-and-semaphore memo lost the same invalidation through its
|
|
382
|
+
* own write-after-invalidate window.
|
|
345
383
|
*/
|
|
346
384
|
export const CredentialStoreSessionLive = Layer.effect(CredentialStore, Effect.gen(function* () {
|
|
347
385
|
const store = yield* CredentialStore;
|
|
348
|
-
const cache = yield*
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
const existing = current.get(registryUrl);
|
|
352
|
-
if (existing !== undefined)
|
|
353
|
-
return [existing, current];
|
|
354
|
-
const created = Semaphore.makeUnsafe(1);
|
|
355
|
-
const updated = new Map(current);
|
|
356
|
-
updated.set(registryUrl, created);
|
|
357
|
-
return [created, updated];
|
|
358
|
-
});
|
|
359
|
-
const getCached = (registryUrl) => Effect.map(Ref.get(cache), (current) => {
|
|
360
|
-
const cached = current.get(registryUrl);
|
|
361
|
-
return cached === undefined
|
|
362
|
-
? Option.none()
|
|
363
|
-
: Option.some(cached);
|
|
364
|
-
});
|
|
365
|
-
const invalidate = (registryUrl) => Ref.update(cache, (current) => {
|
|
366
|
-
const updated = new Map(current);
|
|
367
|
-
updated.delete(registryUrl);
|
|
368
|
-
return updated;
|
|
369
|
-
});
|
|
370
|
-
const load = (registryUrl) => Effect.gen(function* () {
|
|
371
|
-
const cached = yield* getCached(registryUrl);
|
|
372
|
-
if (Option.isSome(cached))
|
|
373
|
-
return cached.value;
|
|
374
|
-
const lock = yield* getLock(registryUrl);
|
|
375
|
-
return yield* lock.withPermits(1)(Effect.gen(function* () {
|
|
376
|
-
const afterWait = yield* getCached(registryUrl);
|
|
377
|
-
if (Option.isSome(afterWait))
|
|
378
|
-
return afterWait.value;
|
|
379
|
-
const loaded = yield* store.load(registryUrl);
|
|
380
|
-
yield* Ref.update(cache, (current) => {
|
|
381
|
-
const updated = new Map(current);
|
|
382
|
-
updated.set(registryUrl, loaded);
|
|
383
|
-
return updated;
|
|
384
|
-
});
|
|
385
|
-
return loaded;
|
|
386
|
-
}));
|
|
386
|
+
const cache = yield* Cache.makeWith((registryUrl) => store.load(registryUrl), {
|
|
387
|
+
capacity: Number.POSITIVE_INFINITY,
|
|
388
|
+
timeToLive: (exit) => Exit.isSuccess(exit) ? Duration.infinity : Duration.zero,
|
|
387
389
|
});
|
|
388
390
|
return {
|
|
389
391
|
tier: store.tier,
|
|
390
392
|
allowsPersistedCredentials: store.allowsPersistedCredentials,
|
|
391
|
-
load,
|
|
392
|
-
save: (registryUrl, handle, credentials) => store
|
|
393
|
-
|
|
393
|
+
load: (registryUrl) => Cache.get(cache, registryUrl).pipe(Effect.onInterrupt(() => Cache.invalidate(cache, registryUrl))),
|
|
394
|
+
save: (registryUrl, handle, credentials) => store
|
|
395
|
+
.save(registryUrl, handle, credentials)
|
|
396
|
+
.pipe(Effect.andThen(Cache.invalidate(cache, registryUrl))),
|
|
397
|
+
clear: (registryUrl) => store.clear(registryUrl).pipe(Effect.andThen(Cache.invalidate(cache, registryUrl))),
|
|
394
398
|
};
|
|
395
399
|
}));
|
|
396
400
|
// -----------------------------------------------------------------------------
|
|
@@ -53,11 +53,15 @@ export declare const DeviceLoginPendingResultSchema: Schema.Struct<{
|
|
|
53
53
|
readonly interval: Schema.Number;
|
|
54
54
|
readonly resume: Schema.String;
|
|
55
55
|
readonly action: Schema.Struct<{
|
|
56
|
-
readonly
|
|
57
|
-
readonly url: Schema.String;
|
|
56
|
+
readonly purpose: Schema.Literal<"login">;
|
|
58
57
|
readonly fallbackUrl: Schema.String;
|
|
59
58
|
readonly code: Schema.String;
|
|
59
|
+
readonly kind: Schema.Literal<"open-url">;
|
|
60
|
+
readonly requestRef: Schema.String;
|
|
61
|
+
readonly registryUrl: Schema.String;
|
|
62
|
+
readonly url: Schema.String;
|
|
60
63
|
readonly expiresAt: Schema.String;
|
|
64
|
+
readonly intervalSeconds: Schema.Number;
|
|
61
65
|
readonly resume: Schema.String;
|
|
62
66
|
}>;
|
|
63
67
|
}>;
|
|
@@ -77,11 +81,15 @@ export declare const DeviceLoginPendingDocumentSchema: Schema.Struct<{
|
|
|
77
81
|
readonly interval: Schema.Number;
|
|
78
82
|
readonly resume: Schema.String;
|
|
79
83
|
readonly action: Schema.Struct<{
|
|
80
|
-
readonly
|
|
81
|
-
readonly url: Schema.String;
|
|
84
|
+
readonly purpose: Schema.Literal<"login">;
|
|
82
85
|
readonly fallbackUrl: Schema.String;
|
|
83
86
|
readonly code: Schema.String;
|
|
87
|
+
readonly kind: Schema.Literal<"open-url">;
|
|
88
|
+
readonly requestRef: Schema.String;
|
|
89
|
+
readonly registryUrl: Schema.String;
|
|
90
|
+
readonly url: Schema.String;
|
|
84
91
|
readonly expiresAt: Schema.String;
|
|
92
|
+
readonly intervalSeconds: Schema.Number;
|
|
85
93
|
readonly resume: Schema.String;
|
|
86
94
|
}>;
|
|
87
95
|
}>;
|
|
@@ -100,15 +108,19 @@ export declare const initiateDeviceLogin: (registryUrl: string, options?: RunDev
|
|
|
100
108
|
readonly interval: number;
|
|
101
109
|
readonly resume: string;
|
|
102
110
|
readonly action: {
|
|
103
|
-
readonly
|
|
104
|
-
readonly url: string;
|
|
111
|
+
readonly purpose: "login";
|
|
105
112
|
readonly fallbackUrl: string;
|
|
106
113
|
readonly code: string;
|
|
114
|
+
readonly kind: "open-url";
|
|
115
|
+
readonly requestRef: string;
|
|
116
|
+
readonly registryUrl: string;
|
|
117
|
+
readonly url: string;
|
|
107
118
|
readonly expiresAt: string;
|
|
119
|
+
readonly intervalSeconds: number;
|
|
108
120
|
readonly resume: string;
|
|
109
121
|
};
|
|
110
122
|
}, import("./errors.js").AuthError, AuthClient | CredentialStore | AuthLoginPresenter | PendingDeviceLoginStore | DeviceLoginInteraction>;
|
|
111
|
-
export declare const resumeDeviceLogin: (registryUrl: string, options?: ResumeDeviceLoginOptions) => Effect.Effect<undefined, RegistryAuthFailed | import("./errors.js").AuthLoginRequired | import("./errors.js").AuthTokenPolicyRequired | DeviceAuthorizationPending | import("./errors.js").StepUpRequired | import("@agentxm/registry-client").RegistryClientFailure | import("./errors.js").AuthExchangeFailed, AuthClient | CredentialStore | AuthLoginPresenter | PendingDeviceLoginStore>;
|
|
112
|
-
export declare const runDeviceLogin: (registryUrl: string, options?: RunDeviceLoginOptions) => Effect.Effect<void, RegistryAuthFailed | import("./errors.js").AuthLoginRequired | import("./errors.js").AuthTokenPolicyRequired | import("./errors.js").DeviceLoginDenied | import("./errors.js").DeviceLoginCodeExpired | DeviceAuthorizationPending | import("./errors.js").StepUpRequired | import("@agentxm/registry-client").RegistryProblem | import("@agentxm/registry-client").RegistryRequestFailed | import("@agentxm/registry-client").RegistryOperationFailed | import("./errors.js").AuthExchangeFailed, AuthClient | CredentialStore | AuthLoginPresenter | PendingDeviceLoginStore | DeviceLoginInteraction>;
|
|
123
|
+
export declare const resumeDeviceLogin: (registryUrl: string, options?: ResumeDeviceLoginOptions) => Effect.Effect<undefined, RegistryAuthFailed | import("./errors.js").AuthLoginRequired | import("./errors.js").AuthTokenPolicyRequired | DeviceAuthorizationPending | import("./errors.js").StepUpRequired | import("@agentxm/registry-client").RegistryClientFailure | import("./errors.js").AuthExchangeFailed | import("./errors.js").PublishAuthorizationPending | import("./errors.js").StepUpVerificationPending | import("./errors.js").AuthInteractionAbandoned, AuthClient | CredentialStore | AuthLoginPresenter | PendingDeviceLoginStore>;
|
|
124
|
+
export declare const runDeviceLogin: (registryUrl: string, options?: RunDeviceLoginOptions) => Effect.Effect<void, RegistryAuthFailed | import("./errors.js").AuthLoginRequired | import("./errors.js").AuthTokenPolicyRequired | import("./errors.js").DeviceLoginDenied | import("./errors.js").DeviceLoginCodeExpired | DeviceAuthorizationPending | import("./errors.js").StepUpRequired | import("@agentxm/registry-client").RegistryProblem | import("@agentxm/registry-client").RegistryRequestFailed | import("@agentxm/registry-client").RegistryOperationFailed | import("./errors.js").AuthExchangeFailed | import("./errors.js").PublishAuthorizationPending | import("./errors.js").StepUpVerificationPending | import("./errors.js").AuthInteractionAbandoned, AuthClient | CredentialStore | AuthLoginPresenter | PendingDeviceLoginStore | DeviceLoginInteraction>;
|
|
113
125
|
export {};
|
|
114
126
|
//# sourceMappingURL=device-login.d.ts.map
|
package/dist/src/device-login.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { HumanHandoffActionSchema } from "@agentxm/registry-protocol/unstable/human-handoff";
|
|
1
2
|
/**
|
|
2
3
|
* Shared device-code login flow for auth commands and guards.
|
|
3
4
|
*
|
|
@@ -58,12 +59,10 @@ const persistLoginCredentials = (registryUrl, token) => Effect.gen(function* ()
|
|
|
58
59
|
return Option.map(meResult, (me) => me.userHandle);
|
|
59
60
|
});
|
|
60
61
|
const DeviceLoginActionSchema = Schema.Struct({
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
...HumanHandoffActionSchema.fields,
|
|
63
|
+
purpose: Schema.Literal("login"),
|
|
63
64
|
fallbackUrl: Schema.String,
|
|
64
65
|
code: Schema.String,
|
|
65
|
-
expiresAt: Schema.String,
|
|
66
|
-
resume: Schema.String,
|
|
67
66
|
});
|
|
68
67
|
export const DeviceLoginPendingResultSchema = Schema.Struct({
|
|
69
68
|
status: Schema.Literal("pending-human"),
|
|
@@ -119,6 +118,10 @@ const makePendingResult = (pending, flow = "started") => {
|
|
|
119
118
|
resume,
|
|
120
119
|
action: {
|
|
121
120
|
kind: "open-url",
|
|
121
|
+
purpose: "login",
|
|
122
|
+
requestRef: pending.verificationUriComplete,
|
|
123
|
+
registryUrl: pending.registryUrl,
|
|
124
|
+
intervalSeconds: pending.interval,
|
|
122
125
|
url: pending.verificationUriComplete,
|
|
123
126
|
fallbackUrl: pending.verificationUri,
|
|
124
127
|
code: pending.userCode,
|
|
@@ -258,6 +261,8 @@ export const resumeDeviceLogin = (registryUrl, options = {}) => Effect.gen(funct
|
|
|
258
261
|
const action = makePendingResult(pending).action;
|
|
259
262
|
return Effect.fail(new DeviceAuthorizationPending({
|
|
260
263
|
timeoutSeconds,
|
|
264
|
+
registryUrl: pending.registryUrl,
|
|
265
|
+
intervalSeconds: pending.interval,
|
|
261
266
|
verificationUri: action.fallbackUrl,
|
|
262
267
|
verificationUriComplete: action.url,
|
|
263
268
|
userCode: action.code,
|
package/dist/src/errors.d.ts
CHANGED
|
@@ -1,17 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
* Typed failures for the registry-auth feature. The producer owns the
|
|
3
|
-
* category choice and user-facing wording; the application boundary converts
|
|
4
|
-
* the carried fields into its error envelope verbatim. Failures that carry a
|
|
5
|
-
* registry transport failure keep it intact so the boundary can restore the
|
|
6
|
-
* exact evidence, metadata, and semantics the former in-place conversion
|
|
7
|
-
* produced.
|
|
8
|
-
*
|
|
9
|
-
* @experimental This API is unstable and may change without notice.
|
|
10
|
-
*/
|
|
1
|
+
import type { HumanHandoffAction } from "@agentxm/registry-protocol/unstable/human-handoff";
|
|
11
2
|
import { type RegistryClientFailure } from "@agentxm/registry-client";
|
|
12
3
|
import type { SuggestedAction } from "@agentxm/registry-protocol/unstable/suggested-action";
|
|
13
4
|
/** Every category a registry-auth failure can carry. Identical strings to the CLI error codes. */
|
|
14
|
-
export declare const REGISTRY_AUTH_ERROR_CATEGORIES: readonly ["auth", "auth_denied", "auth_expired", "conflict", "internal", "not_found", "validation"];
|
|
5
|
+
export declare const REGISTRY_AUTH_ERROR_CATEGORIES: readonly ["auth", "auth_denied", "auth_expired", "conflict", "internal", "not_found", "timeout", "usage", "validation"];
|
|
15
6
|
export type RegistryAuthErrorCategory = (typeof REGISTRY_AUTH_ERROR_CATEGORIES)[number];
|
|
16
7
|
declare const RegistryAuthFailed_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
17
8
|
readonly _tag: "RegistryAuthFailed";
|
|
@@ -80,6 +71,8 @@ declare const DeviceAuthorizationPending_base: new <A extends Record<string, any
|
|
|
80
71
|
*/
|
|
81
72
|
export declare class DeviceAuthorizationPending extends DeviceAuthorizationPending_base<{
|
|
82
73
|
readonly timeoutSeconds: number;
|
|
74
|
+
readonly registryUrl: string;
|
|
75
|
+
readonly intervalSeconds: number;
|
|
83
76
|
readonly verificationUri: string;
|
|
84
77
|
readonly verificationUriComplete: string;
|
|
85
78
|
readonly userCode: string;
|
|
@@ -128,8 +121,40 @@ export declare class AuthExchangeFailed extends AuthExchangeFailed_base<{
|
|
|
128
121
|
readonly failure: RegistryClientFailure;
|
|
129
122
|
}> {
|
|
130
123
|
}
|
|
124
|
+
declare const PublishAuthorizationPending_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
125
|
+
readonly _tag: "PublishAuthorizationPending";
|
|
126
|
+
} & Readonly<A>;
|
|
127
|
+
export declare class PublishAuthorizationPending extends PublishAuthorizationPending_base<{
|
|
128
|
+
readonly action: HumanHandoffAction;
|
|
129
|
+
readonly timedOut: boolean;
|
|
130
|
+
}> {
|
|
131
|
+
}
|
|
132
|
+
declare const StepUpVerificationPending_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
133
|
+
readonly _tag: "StepUpVerificationPending";
|
|
134
|
+
} & Readonly<A>;
|
|
135
|
+
/**
|
|
136
|
+
* A challenged Registry write is waiting on human verification. No challenged
|
|
137
|
+
* write has completed: the operation is retried exactly once, after the
|
|
138
|
+
* verification the carried handoff describes.
|
|
139
|
+
*/
|
|
140
|
+
export declare class StepUpVerificationPending extends StepUpVerificationPending_base<{
|
|
141
|
+
readonly action: HumanHandoffAction;
|
|
142
|
+
readonly timedOut: boolean;
|
|
143
|
+
}> {
|
|
144
|
+
}
|
|
145
|
+
declare const AuthInteractionAbandoned_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
146
|
+
readonly _tag: "AuthInteractionAbandoned";
|
|
147
|
+
} & Readonly<A>;
|
|
148
|
+
/**
|
|
149
|
+
* A person abandoned an auth interaction the capability asked the application
|
|
150
|
+
* to run (declining is not abandoning: it returns a decision).
|
|
151
|
+
*/
|
|
152
|
+
export declare class AuthInteractionAbandoned extends AuthInteractionAbandoned_base<{
|
|
153
|
+
readonly message: string;
|
|
154
|
+
}> {
|
|
155
|
+
}
|
|
131
156
|
/** Every typed failure the registry-auth feature constructs. */
|
|
132
|
-
export type RegistryAuthFailure = RegistryAuthFailed | AuthLoginRequired | AuthTokenPolicyRequired | DeviceLoginDenied | DeviceLoginCodeExpired | DeviceAuthorizationPending | StepUpRequired | AuthExchangeFailed;
|
|
157
|
+
export type RegistryAuthFailure = RegistryAuthFailed | AuthLoginRequired | AuthTokenPolicyRequired | DeviceLoginDenied | DeviceLoginCodeExpired | DeviceAuthorizationPending | PublishAuthorizationPending | StepUpVerificationPending | AuthInteractionAbandoned | StepUpRequired | AuthExchangeFailed;
|
|
133
158
|
export declare const isRegistryAuthFailure: (error: unknown) => error is RegistryAuthFailure;
|
|
134
159
|
/**
|
|
135
160
|
* Every failure a registry-auth use case can surface: the feature's own typed
|
package/dist/src/errors.js
CHANGED
|
@@ -18,6 +18,8 @@ export const REGISTRY_AUTH_ERROR_CATEGORIES = [
|
|
|
18
18
|
"conflict",
|
|
19
19
|
"internal",
|
|
20
20
|
"not_found",
|
|
21
|
+
"timeout",
|
|
22
|
+
"usage",
|
|
21
23
|
"validation",
|
|
22
24
|
];
|
|
23
25
|
/**
|
|
@@ -72,12 +74,30 @@ export class StepUpRequired extends Data.TaggedError("StepUpRequired") {
|
|
|
72
74
|
*/
|
|
73
75
|
export class AuthExchangeFailed extends Data.TaggedError("AuthExchangeFailed") {
|
|
74
76
|
}
|
|
77
|
+
export class PublishAuthorizationPending extends Data.TaggedError("PublishAuthorizationPending") {
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* A challenged Registry write is waiting on human verification. No challenged
|
|
81
|
+
* write has completed: the operation is retried exactly once, after the
|
|
82
|
+
* verification the carried handoff describes.
|
|
83
|
+
*/
|
|
84
|
+
export class StepUpVerificationPending extends Data.TaggedError("StepUpVerificationPending") {
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A person abandoned an auth interaction the capability asked the application
|
|
88
|
+
* to run (declining is not abandoning: it returns a decision).
|
|
89
|
+
*/
|
|
90
|
+
export class AuthInteractionAbandoned extends Data.TaggedError("AuthInteractionAbandoned") {
|
|
91
|
+
}
|
|
75
92
|
export const isRegistryAuthFailure = (error) => error instanceof RegistryAuthFailed ||
|
|
76
93
|
error instanceof AuthLoginRequired ||
|
|
77
94
|
error instanceof AuthTokenPolicyRequired ||
|
|
78
95
|
error instanceof DeviceLoginDenied ||
|
|
79
96
|
error instanceof DeviceLoginCodeExpired ||
|
|
80
97
|
error instanceof DeviceAuthorizationPending ||
|
|
98
|
+
error instanceof PublishAuthorizationPending ||
|
|
99
|
+
error instanceof StepUpVerificationPending ||
|
|
100
|
+
error instanceof AuthInteractionAbandoned ||
|
|
81
101
|
error instanceof StepUpRequired ||
|
|
82
102
|
error instanceof AuthExchangeFailed;
|
|
83
103
|
export const isAuthError = (error) => isRegistryAuthFailure(error) || isRegistryClientFailure(error);
|