@zeroxyz/cli 1.28.0 → 1.29.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/index.js +849 -102
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -62738,10 +62738,10 @@ var require_provider_etherscan = __commonJS({
|
|
|
62738
62738
|
*
|
|
62739
62739
|
* If %%post%%, the request is made as a POST request.
|
|
62740
62740
|
*/
|
|
62741
|
-
async fetch(module2, params,
|
|
62741
|
+
async fetch(module2, params, post2) {
|
|
62742
62742
|
const id = nextId++;
|
|
62743
|
-
const url4 =
|
|
62744
|
-
const payload =
|
|
62743
|
+
const url4 = post2 ? this.getPostUrl() : this.getUrl(module2, params);
|
|
62744
|
+
const payload = post2 ? this.getPostData(module2, params) : null;
|
|
62745
62745
|
this.emit("debug", { action: "sendRequest", id, url: url4, payload });
|
|
62746
62746
|
const request3 = new index_js_4.FetchRequest(url4);
|
|
62747
62747
|
request3.setThrottleParams({ slotInterval: 1e3 });
|
|
@@ -78668,7 +78668,7 @@ init_esm_shims();
|
|
|
78668
78668
|
// ../sdk/dist/index.js
|
|
78669
78669
|
init_esm_shims();
|
|
78670
78670
|
|
|
78671
|
-
// ../sdk/dist/chunk-
|
|
78671
|
+
// ../sdk/dist/chunk-5334O6ZN.js
|
|
78672
78672
|
init_esm_shims();
|
|
78673
78673
|
|
|
78674
78674
|
// ../../node_modules/.pnpm/viem@2.52.2_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@6.0.6_zod@4.3.6/node_modules/viem/_esm/index.js
|
|
@@ -164365,7 +164365,7 @@ var redactUrl = (raw) => {
|
|
|
164365
164365
|
};
|
|
164366
164366
|
var truncateError = (raw) => raw.length > ERROR_MAX ? `${raw.slice(0, ERROR_MAX)}\u2026` : raw;
|
|
164367
164367
|
var package_default = {
|
|
164368
|
-
version: "0.
|
|
164368
|
+
version: "0.22.0"
|
|
164369
164369
|
};
|
|
164370
164370
|
var SDK_VERSION2 = package_default.version;
|
|
164371
164371
|
var ZeroError = class extends Error {
|
|
@@ -167660,7 +167660,6 @@ var agentZeroSessionSchema = external_exports3.object({
|
|
|
167660
167660
|
user: external_exports3.object({ id: external_exports3.string() }).loose(),
|
|
167661
167661
|
walletAddress: external_exports3.string().optional()
|
|
167662
167662
|
}).loose();
|
|
167663
|
-
var JWT_BEARER_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer";
|
|
167664
167663
|
var trimSlash = (value) => value.replace(/\/$/, "");
|
|
167665
167664
|
var wrapNetworkError = (url4, err) => {
|
|
167666
167665
|
const cause = err?.cause;
|
|
@@ -167670,6 +167669,80 @@ var wrapNetworkError = (url4, err) => {
|
|
|
167670
167669
|
cause: err
|
|
167671
167670
|
});
|
|
167672
167671
|
};
|
|
167672
|
+
var getJson = async (fetchImpl, url4) => {
|
|
167673
|
+
const response = await fetchImpl(url4).catch((err) => {
|
|
167674
|
+
throw wrapNetworkError(url4, err);
|
|
167675
|
+
});
|
|
167676
|
+
if (!response.ok) {
|
|
167677
|
+
throw new ZeroAgentAuthError(`GET ${url4} failed (${response.status})`, {
|
|
167678
|
+
status: response.status
|
|
167679
|
+
});
|
|
167680
|
+
}
|
|
167681
|
+
return await response.json();
|
|
167682
|
+
};
|
|
167683
|
+
var postJson = (fetchImpl, url4, body, schema) => post(fetchImpl, url4, "application/json", JSON.stringify(body), schema);
|
|
167684
|
+
var postForm = (fetchImpl, url4, fields, schema) => post(
|
|
167685
|
+
fetchImpl,
|
|
167686
|
+
url4,
|
|
167687
|
+
"application/x-www-form-urlencoded",
|
|
167688
|
+
new URLSearchParams(fields).toString(),
|
|
167689
|
+
schema
|
|
167690
|
+
);
|
|
167691
|
+
var post = async (fetchImpl, url4, contentType, body, schema) => {
|
|
167692
|
+
const response = await fetchImpl(url4, {
|
|
167693
|
+
method: "POST",
|
|
167694
|
+
headers: { "content-type": contentType },
|
|
167695
|
+
body
|
|
167696
|
+
}).catch((err) => {
|
|
167697
|
+
throw wrapNetworkError(url4, err);
|
|
167698
|
+
});
|
|
167699
|
+
const text = await response.text();
|
|
167700
|
+
let parsed = null;
|
|
167701
|
+
try {
|
|
167702
|
+
parsed = text ? JSON.parse(text) : null;
|
|
167703
|
+
} catch {
|
|
167704
|
+
}
|
|
167705
|
+
if (!response.ok) {
|
|
167706
|
+
const errBody = parsed;
|
|
167707
|
+
const code = errBody?.code ?? errBody?.error;
|
|
167708
|
+
throw new ZeroAgentAuthError(
|
|
167709
|
+
`Agent auth request to ${url4} failed (${response.status}${typeof code === "string" ? `: ${code}` : ""})`,
|
|
167710
|
+
{ status: response.status, body: parsed ?? text }
|
|
167711
|
+
);
|
|
167712
|
+
}
|
|
167713
|
+
return schema.parse(parsed);
|
|
167714
|
+
};
|
|
167715
|
+
var completeClaimAt = async (fetchImpl, claimEndpoint, input) => {
|
|
167716
|
+
try {
|
|
167717
|
+
const identity = await postJson(
|
|
167718
|
+
fetchImpl,
|
|
167719
|
+
`${claimEndpoint}/complete`,
|
|
167720
|
+
{ claim_token: input.claimToken, user_code: input.userCode },
|
|
167721
|
+
completeClaimResponseSchema
|
|
167722
|
+
);
|
|
167723
|
+
return { status: "ok", ...identity };
|
|
167724
|
+
} catch (err) {
|
|
167725
|
+
if (err instanceof ZeroAgentAuthError && err.serverCode) {
|
|
167726
|
+
if (err.serverCode === "claim_not_confirmed") {
|
|
167727
|
+
return { status: "not_confirmed" };
|
|
167728
|
+
}
|
|
167729
|
+
if (err.serverCode === "invalid_user_code") {
|
|
167730
|
+
return { status: "invalid_code" };
|
|
167731
|
+
}
|
|
167732
|
+
if ([
|
|
167733
|
+
"user_code_expired",
|
|
167734
|
+
"claim_expired",
|
|
167735
|
+
"claim_revoked",
|
|
167736
|
+
"already_claimed",
|
|
167737
|
+
"claim_denied"
|
|
167738
|
+
].includes(err.serverCode)) {
|
|
167739
|
+
return { status: "expired", code: err.serverCode };
|
|
167740
|
+
}
|
|
167741
|
+
}
|
|
167742
|
+
throw err;
|
|
167743
|
+
}
|
|
167744
|
+
};
|
|
167745
|
+
var JWT_BEARER_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer";
|
|
167673
167746
|
var AuthAgent = class {
|
|
167674
167747
|
constructor(client) {
|
|
167675
167748
|
this.client = client;
|
|
@@ -167687,7 +167760,8 @@ var AuthAgent = class {
|
|
|
167687
167760
|
};
|
|
167688
167761
|
register = async () => {
|
|
167689
167762
|
const endpoints = await this.endpoints();
|
|
167690
|
-
return
|
|
167763
|
+
return postJson(
|
|
167764
|
+
this.client.fetchImpl,
|
|
167691
167765
|
endpoints.identityEndpoint,
|
|
167692
167766
|
{ type: "anonymous" },
|
|
167693
167767
|
identityEnvelopeSchema
|
|
@@ -167695,7 +167769,8 @@ var AuthAgent = class {
|
|
|
167695
167769
|
};
|
|
167696
167770
|
exchangeAssertion = async (identityAssertion) => {
|
|
167697
167771
|
const endpoints = await this.endpoints();
|
|
167698
|
-
return
|
|
167772
|
+
return postForm(
|
|
167773
|
+
this.client.fetchImpl,
|
|
167699
167774
|
endpoints.tokenEndpoint,
|
|
167700
167775
|
{ grant_type: JWT_BEARER_GRANT_TYPE, assertion: identityAssertion },
|
|
167701
167776
|
agentTokenResponseSchema
|
|
@@ -167725,7 +167800,8 @@ var AuthAgent = class {
|
|
|
167725
167800
|
{ status: 0 }
|
|
167726
167801
|
);
|
|
167727
167802
|
}
|
|
167728
|
-
return
|
|
167803
|
+
return postJson(
|
|
167804
|
+
this.client.fetchImpl,
|
|
167729
167805
|
endpoints.claimEndpoint,
|
|
167730
167806
|
{
|
|
167731
167807
|
type: "service_auth",
|
|
@@ -167735,8 +167811,6 @@ var AuthAgent = class {
|
|
|
167735
167811
|
startClaimResponseSchema
|
|
167736
167812
|
);
|
|
167737
167813
|
};
|
|
167738
|
-
// Submit the pairing code the human read off the hosted page. Terminal
|
|
167739
|
-
// states map to a typed result; anything unexpected throws.
|
|
167740
167814
|
completeClaim = async (input) => {
|
|
167741
167815
|
const endpoints = await this.endpoints();
|
|
167742
167816
|
if (!endpoints.claimEndpoint) {
|
|
@@ -167745,33 +167819,10 @@ var AuthAgent = class {
|
|
|
167745
167819
|
{ status: 0 }
|
|
167746
167820
|
);
|
|
167747
167821
|
}
|
|
167748
|
-
|
|
167749
|
-
|
|
167750
|
-
|
|
167751
|
-
|
|
167752
|
-
completeClaimResponseSchema
|
|
167753
|
-
);
|
|
167754
|
-
return { status: "ok", ...identity };
|
|
167755
|
-
} catch (err) {
|
|
167756
|
-
if (err instanceof ZeroAgentAuthError && err.serverCode) {
|
|
167757
|
-
if (err.serverCode === "claim_not_confirmed") {
|
|
167758
|
-
return { status: "not_confirmed" };
|
|
167759
|
-
}
|
|
167760
|
-
if (err.serverCode === "invalid_user_code") {
|
|
167761
|
-
return { status: "invalid_code" };
|
|
167762
|
-
}
|
|
167763
|
-
if ([
|
|
167764
|
-
"user_code_expired",
|
|
167765
|
-
"claim_expired",
|
|
167766
|
-
"claim_revoked",
|
|
167767
|
-
"already_claimed",
|
|
167768
|
-
"claim_denied"
|
|
167769
|
-
].includes(err.serverCode)) {
|
|
167770
|
-
return { status: "expired", code: err.serverCode };
|
|
167771
|
-
}
|
|
167772
|
-
}
|
|
167773
|
-
throw err;
|
|
167774
|
-
}
|
|
167822
|
+
return completeClaimAt(this.client.fetchImpl, endpoints.claimEndpoint, {
|
|
167823
|
+
claimToken: input.claimToken,
|
|
167824
|
+
userCode: input.userCode
|
|
167825
|
+
});
|
|
167775
167826
|
};
|
|
167776
167827
|
// Resolve (and cache) the agent-auth endpoints via discovery.
|
|
167777
167828
|
endpoints = () => {
|
|
@@ -167784,13 +167835,15 @@ var AuthAgent = class {
|
|
|
167784
167835
|
return this.endpointsPromise;
|
|
167785
167836
|
};
|
|
167786
167837
|
discover = async () => {
|
|
167787
|
-
const resource = await
|
|
167838
|
+
const resource = await getJson(
|
|
167839
|
+
this.client.fetchImpl,
|
|
167788
167840
|
`${trimSlash(this.client.baseUrl)}/.well-known/oauth-protected-resource`
|
|
167789
167841
|
);
|
|
167790
167842
|
let lastError = null;
|
|
167791
167843
|
for (const server of resource.authorization_servers ?? []) {
|
|
167792
167844
|
try {
|
|
167793
|
-
const raw = await
|
|
167845
|
+
const raw = await getJson(
|
|
167846
|
+
this.client.fetchImpl,
|
|
167794
167847
|
`${trimSlash(server)}/.well-known/oauth-authorization-server`
|
|
167795
167848
|
);
|
|
167796
167849
|
const metadata = agentAuthMetadataSchema.parse(raw);
|
|
@@ -167809,48 +167862,6 @@ var AuthAgent = class {
|
|
|
167809
167862
|
{ status: 0, cause: lastError }
|
|
167810
167863
|
);
|
|
167811
167864
|
};
|
|
167812
|
-
getJson = async (url4) => {
|
|
167813
|
-
const response = await this.client.fetchImpl(url4).catch((err) => {
|
|
167814
|
-
throw wrapNetworkError(url4, err);
|
|
167815
|
-
});
|
|
167816
|
-
if (!response.ok) {
|
|
167817
|
-
throw new ZeroAgentAuthError(`GET ${url4} failed (${response.status})`, {
|
|
167818
|
-
status: response.status
|
|
167819
|
-
});
|
|
167820
|
-
}
|
|
167821
|
-
return await response.json();
|
|
167822
|
-
};
|
|
167823
|
-
postJson = (url4, body, schema) => this.post(url4, "application/json", JSON.stringify(body), schema);
|
|
167824
|
-
postForm = (url4, fields, schema) => this.post(
|
|
167825
|
-
url4,
|
|
167826
|
-
"application/x-www-form-urlencoded",
|
|
167827
|
-
new URLSearchParams(fields).toString(),
|
|
167828
|
-
schema
|
|
167829
|
-
);
|
|
167830
|
-
post = async (url4, contentType, body, schema) => {
|
|
167831
|
-
const response = await this.client.fetchImpl(url4, {
|
|
167832
|
-
method: "POST",
|
|
167833
|
-
headers: { "content-type": contentType },
|
|
167834
|
-
body
|
|
167835
|
-
}).catch((err) => {
|
|
167836
|
-
throw wrapNetworkError(url4, err);
|
|
167837
|
-
});
|
|
167838
|
-
const text = await response.text();
|
|
167839
|
-
let parsed = null;
|
|
167840
|
-
try {
|
|
167841
|
-
parsed = text ? JSON.parse(text) : null;
|
|
167842
|
-
} catch {
|
|
167843
|
-
}
|
|
167844
|
-
if (!response.ok) {
|
|
167845
|
-
const errBody = parsed;
|
|
167846
|
-
const code = errBody?.code ?? errBody?.error;
|
|
167847
|
-
throw new ZeroAgentAuthError(
|
|
167848
|
-
`Agent auth request to ${url4} failed (${response.status}${typeof code === "string" ? `: ${code}` : ""})`,
|
|
167849
|
-
{ status: response.status, body: parsed ?? text }
|
|
167850
|
-
);
|
|
167851
|
-
}
|
|
167852
|
-
return schema.parse(parsed);
|
|
167853
|
-
};
|
|
167854
167865
|
};
|
|
167855
167866
|
var userWalletDtoSchema = external_exports3.object({
|
|
167856
167867
|
walletAddress: external_exports3.string(),
|
|
@@ -167961,6 +167972,294 @@ var AuthDevice = class {
|
|
|
167961
167972
|
};
|
|
167962
167973
|
};
|
|
167963
167974
|
};
|
|
167975
|
+
var ID_JAG_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:id-jag";
|
|
167976
|
+
var idJagMintResponseSchema = external_exports3.object({
|
|
167977
|
+
access_token: external_exports3.string(),
|
|
167978
|
+
issued_token_type: external_exports3.string().optional(),
|
|
167979
|
+
expires_in: external_exports3.number().optional()
|
|
167980
|
+
}).loose().transform((raw) => ({
|
|
167981
|
+
idJag: raw.access_token,
|
|
167982
|
+
issuedTokenType: raw.issued_token_type ?? ID_JAG_TOKEN_TYPE,
|
|
167983
|
+
expiresIn: raw.expires_in ?? null
|
|
167984
|
+
}));
|
|
167985
|
+
var protectedResourceMetadataSchema = external_exports3.object({
|
|
167986
|
+
resource: external_exports3.string().optional(),
|
|
167987
|
+
resource_name: external_exports3.string().optional(),
|
|
167988
|
+
authorization_servers: external_exports3.array(external_exports3.string()).optional()
|
|
167989
|
+
}).loose();
|
|
167990
|
+
var serviceAuthMetadataSchema = external_exports3.object({
|
|
167991
|
+
resource: external_exports3.string().optional(),
|
|
167992
|
+
resource_name: external_exports3.string().optional(),
|
|
167993
|
+
token_endpoint: external_exports3.string().optional(),
|
|
167994
|
+
agent_auth: external_exports3.object({
|
|
167995
|
+
identity_endpoint: external_exports3.string(),
|
|
167996
|
+
claim_endpoint: external_exports3.string().optional(),
|
|
167997
|
+
identity_types_supported: external_exports3.array(external_exports3.string()).optional(),
|
|
167998
|
+
identity_assertion: external_exports3.object({
|
|
167999
|
+
assertion_types_supported: external_exports3.array(external_exports3.string()).optional(),
|
|
168000
|
+
// Optional shared-audience hint: a platform fronting many
|
|
168001
|
+
// storefronts advertises one audience (its AS) so a single
|
|
168002
|
+
// consent covers the network.
|
|
168003
|
+
audience: external_exports3.string().optional()
|
|
168004
|
+
}).loose().optional()
|
|
168005
|
+
}).loose()
|
|
168006
|
+
}).loose();
|
|
168007
|
+
var identityAssertionEnvelopeSchema = external_exports3.object({
|
|
168008
|
+
id: external_exports3.string(),
|
|
168009
|
+
identity: external_exports3.object({
|
|
168010
|
+
assertion: external_exports3.string(),
|
|
168011
|
+
expires_at: external_exports3.string().optional(),
|
|
168012
|
+
refresh_token: external_exports3.object({ value: external_exports3.string() }).loose().optional()
|
|
168013
|
+
}).loose(),
|
|
168014
|
+
assertion_expires: external_exports3.string().nullable().optional()
|
|
168015
|
+
}).loose().transform((raw) => ({
|
|
168016
|
+
registrationId: raw.id,
|
|
168017
|
+
identityAssertion: raw.identity.assertion,
|
|
168018
|
+
assertionExpires: raw.assertion_expires ?? raw.identity.expires_at ?? null,
|
|
168019
|
+
refreshToken: raw.identity.refresh_token?.value ?? null
|
|
168020
|
+
}));
|
|
168021
|
+
var interactionRequiredBodySchema = external_exports3.object({
|
|
168022
|
+
error_description: external_exports3.string().optional(),
|
|
168023
|
+
claim: external_exports3.object({
|
|
168024
|
+
token: external_exports3.string().optional(),
|
|
168025
|
+
expires_at: external_exports3.string().optional(),
|
|
168026
|
+
attempt: external_exports3.object({
|
|
168027
|
+
verification_uri: external_exports3.string().optional(),
|
|
168028
|
+
expires_at: external_exports3.string().optional()
|
|
168029
|
+
}).loose().optional()
|
|
168030
|
+
}).loose().optional()
|
|
168031
|
+
}).loose();
|
|
168032
|
+
var identityGrantSchema = external_exports3.object({ audience: external_exports3.string(), createdAt: external_exports3.string() }).loose();
|
|
168033
|
+
var identityGrantListSchema = external_exports3.object({ grants: external_exports3.array(identityGrantSchema) }).loose();
|
|
168034
|
+
var identityGrantRevokeSchema = external_exports3.object({ revoked: external_exports3.boolean() }).loose();
|
|
168035
|
+
var identityGrantRequestSchema = external_exports3.object({
|
|
168036
|
+
requestId: external_exports3.string(),
|
|
168037
|
+
approveUrl: external_exports3.string(),
|
|
168038
|
+
expiresAt: external_exports3.string()
|
|
168039
|
+
}).loose();
|
|
168040
|
+
var identityGrantRequestStatusSchema = external_exports3.object({
|
|
168041
|
+
requestId: external_exports3.string(),
|
|
168042
|
+
status: external_exports3.enum(["pending", "approved", "denied", "expired"]),
|
|
168043
|
+
audience: external_exports3.string(),
|
|
168044
|
+
resourceName: external_exports3.string().nullable().optional()
|
|
168045
|
+
}).loose();
|
|
168046
|
+
var TOKEN_EXCHANGE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange";
|
|
168047
|
+
var SUBJECT_TOKEN_TYPE_ACCESS_TOKEN = "urn:ietf:params:oauth:token-type:access_token";
|
|
168048
|
+
var JWT_BEARER_GRANT_TYPE2 = "urn:ietf:params:oauth:grant-type:jwt-bearer";
|
|
168049
|
+
var IDENTITY_ASSERTION_TYPE = "identity_assertion";
|
|
168050
|
+
var AuthIdentity = class {
|
|
168051
|
+
constructor(client) {
|
|
168052
|
+
this.client = client;
|
|
168053
|
+
}
|
|
168054
|
+
discover = async (url4) => {
|
|
168055
|
+
const origin2 = new URL(url4).origin;
|
|
168056
|
+
const prm = protectedResourceMetadataSchema.parse(
|
|
168057
|
+
await getJson(
|
|
168058
|
+
this.client.fetchImpl,
|
|
168059
|
+
`${origin2}/.well-known/oauth-protected-resource`
|
|
168060
|
+
)
|
|
168061
|
+
);
|
|
168062
|
+
let lastError = null;
|
|
168063
|
+
for (const server of prm.authorization_servers ?? [origin2]) {
|
|
168064
|
+
try {
|
|
168065
|
+
const metadata = serviceAuthMetadataSchema.parse(
|
|
168066
|
+
await getJson(
|
|
168067
|
+
this.client.fetchImpl,
|
|
168068
|
+
`${trimSlash(server)}/.well-known/oauth-authorization-server`
|
|
168069
|
+
)
|
|
168070
|
+
);
|
|
168071
|
+
const agentAuth = metadata.agent_auth;
|
|
168072
|
+
const assertionTypes = agentAuth.identity_assertion?.assertion_types_supported;
|
|
168073
|
+
const resource = prm.resource ?? `${origin2}/`;
|
|
168074
|
+
return {
|
|
168075
|
+
resource,
|
|
168076
|
+
mintAudience: agentAuth.identity_assertion?.audience ?? resource,
|
|
168077
|
+
resourceName: prm.resource_name ?? metadata.resource_name ?? null,
|
|
168078
|
+
authorizationServer: trimSlash(server),
|
|
168079
|
+
identityEndpoint: agentAuth.identity_endpoint,
|
|
168080
|
+
claimEndpoint: agentAuth.claim_endpoint ?? null,
|
|
168081
|
+
tokenEndpoint: metadata.token_endpoint ?? `${trimSlash(server)}/oauth2/token`,
|
|
168082
|
+
identityAssertionSupported: (agentAuth.identity_types_supported ?? []).includes(
|
|
168083
|
+
IDENTITY_ASSERTION_TYPE
|
|
168084
|
+
) && (assertionTypes ? assertionTypes.includes(ID_JAG_TOKEN_TYPE) : true)
|
|
168085
|
+
};
|
|
168086
|
+
} catch (err) {
|
|
168087
|
+
lastError = err;
|
|
168088
|
+
}
|
|
168089
|
+
}
|
|
168090
|
+
throw new ZeroAgentAuthError(
|
|
168091
|
+
`No authorization server with an agent_auth block found at ${origin2} \u2014 agent auth is not enabled for this service.`,
|
|
168092
|
+
{ status: 0, cause: lastError }
|
|
168093
|
+
);
|
|
168094
|
+
};
|
|
168095
|
+
// The minted grant is single-use (fresh jti) and minutes-lived — mint per
|
|
168096
|
+
// registration, never cache.
|
|
168097
|
+
mint = async (input) => {
|
|
168098
|
+
const attempt = () => {
|
|
168099
|
+
const credentials = this.client.getCredentials();
|
|
168100
|
+
if (credentials.kind !== "session") {
|
|
168101
|
+
throw new ZeroAuthError(
|
|
168102
|
+
"auth_no_credentials",
|
|
168103
|
+
"mint() requires session credentials \u2014 the ID-JAG asserts the signed-in user's identity."
|
|
168104
|
+
);
|
|
168105
|
+
}
|
|
168106
|
+
return request2(
|
|
168107
|
+
this.client,
|
|
168108
|
+
{
|
|
168109
|
+
method: "POST",
|
|
168110
|
+
path: "/v1/oauth/token",
|
|
168111
|
+
body: {
|
|
168112
|
+
grant_type: TOKEN_EXCHANGE_GRANT_TYPE,
|
|
168113
|
+
subject_token: credentials.accessToken,
|
|
168114
|
+
subject_token_type: SUBJECT_TOKEN_TYPE_ACCESS_TOKEN,
|
|
168115
|
+
audience: input.audience,
|
|
168116
|
+
requested_token_type: ID_JAG_TOKEN_TYPE
|
|
168117
|
+
}
|
|
168118
|
+
},
|
|
168119
|
+
idJagMintResponseSchema
|
|
168120
|
+
);
|
|
168121
|
+
};
|
|
168122
|
+
try {
|
|
168123
|
+
return await attempt();
|
|
168124
|
+
} catch (err) {
|
|
168125
|
+
if (err instanceof ZeroApiError && err.status === 400 && err.body?.error === "invalid_grant" && await this.client.refreshSessionOnce()) {
|
|
168126
|
+
return await attempt();
|
|
168127
|
+
}
|
|
168128
|
+
throw err;
|
|
168129
|
+
}
|
|
168130
|
+
};
|
|
168131
|
+
// Recoverable protocol states come back as typed outcomes; mint-bug
|
|
168132
|
+
// rejections (invalid_audience, expired, …) throw. replay_detected is
|
|
168133
|
+
// retried once with a freshly minted grant (single-use jti).
|
|
168134
|
+
assert = async (input) => {
|
|
168135
|
+
let discovery;
|
|
168136
|
+
try {
|
|
168137
|
+
discovery = input.discovery ?? await this.discover(input.url);
|
|
168138
|
+
} catch (err) {
|
|
168139
|
+
if (err instanceof ZeroAgentAuthError) {
|
|
168140
|
+
return { status: "unsupported", discovery: null };
|
|
168141
|
+
}
|
|
168142
|
+
throw err;
|
|
168143
|
+
}
|
|
168144
|
+
if (!discovery.identityAssertionSupported) {
|
|
168145
|
+
return { status: "unsupported", discovery };
|
|
168146
|
+
}
|
|
168147
|
+
try {
|
|
168148
|
+
return await this.register(discovery);
|
|
168149
|
+
} catch (err) {
|
|
168150
|
+
if (err instanceof ZeroAgentAuthError && err.serverCode === "replay_detected") {
|
|
168151
|
+
return await this.register(discovery);
|
|
168152
|
+
}
|
|
168153
|
+
throw err;
|
|
168154
|
+
}
|
|
168155
|
+
};
|
|
168156
|
+
// RFC 7523 jwt-bearer. Bearer tokens are minutes-lived: exchange per work
|
|
168157
|
+
// session, persist only the assertion + refresh token.
|
|
168158
|
+
exchange = (input) => postForm(
|
|
168159
|
+
this.client.fetchImpl,
|
|
168160
|
+
input.tokenEndpoint,
|
|
168161
|
+
{ grant_type: JWT_BEARER_GRANT_TYPE2, assertion: input.assertion },
|
|
168162
|
+
agentTokenResponseSchema
|
|
168163
|
+
);
|
|
168164
|
+
completeClaim = (input) => completeClaimAt(this.client.fetchImpl, input.claimEndpoint, {
|
|
168165
|
+
claimToken: input.claimToken,
|
|
168166
|
+
userCode: input.userCode
|
|
168167
|
+
});
|
|
168168
|
+
grants = (opts = {}) => request2(
|
|
168169
|
+
this.client,
|
|
168170
|
+
{
|
|
168171
|
+
method: "GET",
|
|
168172
|
+
path: "/v1/users/me/identity-grants",
|
|
168173
|
+
signal: opts.signal
|
|
168174
|
+
},
|
|
168175
|
+
identityGrantListSchema
|
|
168176
|
+
).then((body) => body.grants);
|
|
168177
|
+
// `resourceUrl` lets the server verify and display the service's
|
|
168178
|
+
// name/logo on the approve page. Poll grantRequest() until decided.
|
|
168179
|
+
requestGrant = (input) => request2(
|
|
168180
|
+
this.client,
|
|
168181
|
+
{
|
|
168182
|
+
method: "POST",
|
|
168183
|
+
path: "/v1/users/me/identity-grants/requests",
|
|
168184
|
+
body: {
|
|
168185
|
+
audience: input.audience,
|
|
168186
|
+
...input.resourceUrl ? { resourceUrl: input.resourceUrl } : {}
|
|
168187
|
+
}
|
|
168188
|
+
},
|
|
168189
|
+
identityGrantRequestSchema
|
|
168190
|
+
);
|
|
168191
|
+
grantRequest = (requestId, opts = {}) => request2(
|
|
168192
|
+
this.client,
|
|
168193
|
+
{
|
|
168194
|
+
method: "GET",
|
|
168195
|
+
path: `/v1/users/me/identity-grants/requests/${requestId}`,
|
|
168196
|
+
signal: opts.signal
|
|
168197
|
+
},
|
|
168198
|
+
identityGrantRequestStatusSchema
|
|
168199
|
+
);
|
|
168200
|
+
revokeGrant = (input) => request2(
|
|
168201
|
+
this.client,
|
|
168202
|
+
{
|
|
168203
|
+
method: "DELETE",
|
|
168204
|
+
path: "/v1/users/me/identity-grants",
|
|
168205
|
+
query: { audience: input.audience }
|
|
168206
|
+
},
|
|
168207
|
+
identityGrantRevokeSchema
|
|
168208
|
+
).then((body) => body.revoked);
|
|
168209
|
+
register = async (discovery) => {
|
|
168210
|
+
let minted;
|
|
168211
|
+
try {
|
|
168212
|
+
minted = await this.mint({ audience: discovery.mintAudience });
|
|
168213
|
+
} catch (err) {
|
|
168214
|
+
if (err instanceof ZeroApiError && err.body?.error === "consent_required") {
|
|
168215
|
+
return { status: "consent_required", discovery };
|
|
168216
|
+
}
|
|
168217
|
+
throw err;
|
|
168218
|
+
}
|
|
168219
|
+
try {
|
|
168220
|
+
const registration = await postJson(
|
|
168221
|
+
this.client.fetchImpl,
|
|
168222
|
+
discovery.identityEndpoint,
|
|
168223
|
+
{
|
|
168224
|
+
type: IDENTITY_ASSERTION_TYPE,
|
|
168225
|
+
assertion_type: ID_JAG_TOKEN_TYPE,
|
|
168226
|
+
assertion: minted.idJag
|
|
168227
|
+
},
|
|
168228
|
+
identityAssertionEnvelopeSchema
|
|
168229
|
+
);
|
|
168230
|
+
return { status: "ok", discovery, registration };
|
|
168231
|
+
} catch (err) {
|
|
168232
|
+
if (err instanceof ZeroAgentAuthError) {
|
|
168233
|
+
if (err.serverCode === "issuer_not_enabled") {
|
|
168234
|
+
return { status: "issuer_not_enabled", discovery };
|
|
168235
|
+
}
|
|
168236
|
+
const description = err.body?.error_description;
|
|
168237
|
+
if (err.serverCode === "login_required") {
|
|
168238
|
+
return {
|
|
168239
|
+
status: "login_required",
|
|
168240
|
+
discovery,
|
|
168241
|
+
description: typeof description === "string" ? description : null
|
|
168242
|
+
};
|
|
168243
|
+
}
|
|
168244
|
+
if (err.serverCode === "interaction_required") {
|
|
168245
|
+
const body = interactionRequiredBodySchema.safeParse(err.body);
|
|
168246
|
+
const claim3 = body.success ? body.data.claim : void 0;
|
|
168247
|
+
return {
|
|
168248
|
+
status: "interaction_required",
|
|
168249
|
+
discovery,
|
|
168250
|
+
claim: {
|
|
168251
|
+
claimToken: claim3?.token ?? null,
|
|
168252
|
+
claimTokenExpires: claim3?.expires_at ?? null,
|
|
168253
|
+
verificationUri: claim3?.attempt?.verification_uri ?? null,
|
|
168254
|
+
description: body.success ? body.data.error_description ?? null : null
|
|
168255
|
+
}
|
|
168256
|
+
};
|
|
168257
|
+
}
|
|
168258
|
+
}
|
|
168259
|
+
throw err;
|
|
168260
|
+
}
|
|
168261
|
+
};
|
|
168262
|
+
};
|
|
167964
168263
|
var userWalletsSchema = external_exports3.array(userWalletDtoSchema);
|
|
167965
168264
|
var normalizeMessage = (message2) => {
|
|
167966
168265
|
if (typeof message2 === "string") return message2;
|
|
@@ -167992,9 +168291,11 @@ var Auth = class {
|
|
|
167992
168291
|
this.client = client;
|
|
167993
168292
|
this.agent = new AuthAgent(client);
|
|
167994
168293
|
this.device = new AuthDevice(client);
|
|
168294
|
+
this.identity = new AuthIdentity(client);
|
|
167995
168295
|
}
|
|
167996
168296
|
agent;
|
|
167997
168297
|
device;
|
|
168298
|
+
identity;
|
|
167998
168299
|
// The internal (web-app facing) user profile. Richer than profile(): full
|
|
167999
168300
|
// wallet list with per-wallet delegation/signer/limit metadata.
|
|
168000
168301
|
me = (opts = {}) => request2(
|
|
@@ -168537,7 +168838,7 @@ var Runs = class _Runs {
|
|
|
168537
168838
|
* target left to name, so that server could never have recorded it.
|
|
168538
168839
|
*/
|
|
168539
168840
|
create = async (input, opts = {}) => {
|
|
168540
|
-
const
|
|
168841
|
+
const post2 = (body) => request2(
|
|
168541
168842
|
this.client,
|
|
168542
168843
|
{
|
|
168543
168844
|
method: "POST",
|
|
@@ -168551,7 +168852,7 @@ var Runs = class _Runs {
|
|
|
168551
168852
|
let generation = 0;
|
|
168552
168853
|
for (; ; ) {
|
|
168553
168854
|
try {
|
|
168554
|
-
return await
|
|
168855
|
+
return await post2(payload);
|
|
168555
168856
|
} catch (err) {
|
|
168556
168857
|
const rejected = err instanceof ZeroApiError && (err.status === 400 || err.status === 422);
|
|
168557
168858
|
if (!rejected) throw err;
|
|
@@ -169956,7 +170257,7 @@ var placeholderFor = (fieldName, propSchema) => String(sampleValueFor(fieldName,
|
|
|
169956
170257
|
|
|
169957
170258
|
// package.json
|
|
169958
170259
|
var package_default2 = {
|
|
169959
|
-
version: "1.
|
|
170260
|
+
version: "1.29.0"};
|
|
169960
170261
|
|
|
169961
170262
|
// src/app.ts
|
|
169962
170263
|
init_esm_shims();
|
|
@@ -170608,13 +170909,458 @@ var readConfig = (path3) => {
|
|
|
170608
170909
|
}
|
|
170609
170910
|
};
|
|
170610
170911
|
|
|
170611
|
-
// src/commands/auth-command.ts
|
|
170912
|
+
// src/commands/auth-identity-command.ts
|
|
170913
|
+
init_esm_shims();
|
|
170612
170914
|
var getConfigPath = () => join$1(homedir(), ".zero", "config.json");
|
|
170915
|
+
var CLAIM_NOT_CONFIRMED_RETRIES = 12;
|
|
170916
|
+
var CLAIM_NOT_CONFIRMED_INTERVAL_S = 5;
|
|
170917
|
+
var CONSENT_POLL_INTERVAL_S = 3;
|
|
170918
|
+
var ASSERTION_EXPIRY_MARGIN_MS = 6e4;
|
|
170919
|
+
var sleep2 = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
170920
|
+
var normalizeUrl = (raw) => {
|
|
170921
|
+
try {
|
|
170922
|
+
return new URL(/^https?:\/\//.test(raw) ? raw : `https://${raw}`);
|
|
170923
|
+
} catch {
|
|
170924
|
+
return null;
|
|
170925
|
+
}
|
|
170926
|
+
};
|
|
170927
|
+
var updateServiceIdentity = (host, update2) => {
|
|
170928
|
+
const path3 = getConfigPath();
|
|
170929
|
+
ensureSecureDir(dirname(path3));
|
|
170930
|
+
const config3 = readConfig(path3);
|
|
170931
|
+
const identities = { ...config3.serviceIdentities };
|
|
170932
|
+
const next = update2(identities[host] ?? {});
|
|
170933
|
+
if (next === void 0) {
|
|
170934
|
+
delete identities[host];
|
|
170935
|
+
} else {
|
|
170936
|
+
identities[host] = next;
|
|
170937
|
+
}
|
|
170938
|
+
writeSecureFile(
|
|
170939
|
+
path3,
|
|
170940
|
+
JSON.stringify({ ...config3, serviceIdentities: identities }, null, 2)
|
|
170941
|
+
);
|
|
170942
|
+
};
|
|
170943
|
+
var isUsable = (expiresAt) => {
|
|
170944
|
+
if (!expiresAt) return true;
|
|
170945
|
+
const parsed = Date.parse(expiresAt);
|
|
170946
|
+
return Number.isNaN(parsed) ? true : parsed - ASSERTION_EXPIRY_MARGIN_MS > Date.now();
|
|
170947
|
+
};
|
|
170948
|
+
var emitAccessToken = (accessToken, discovery, host, json3, reused) => {
|
|
170949
|
+
if (json3) {
|
|
170950
|
+
process.stdout.write(
|
|
170951
|
+
`${JSON.stringify({
|
|
170952
|
+
status: "ok",
|
|
170953
|
+
accessToken,
|
|
170954
|
+
resource: discovery.resource,
|
|
170955
|
+
resourceName: discovery.resourceName,
|
|
170956
|
+
tokenEndpoint: discovery.tokenEndpoint,
|
|
170957
|
+
reused
|
|
170958
|
+
})}
|
|
170959
|
+
`
|
|
170960
|
+
);
|
|
170961
|
+
return;
|
|
170962
|
+
}
|
|
170963
|
+
process.stderr.write(
|
|
170964
|
+
[
|
|
170965
|
+
`Signed in to ${discovery.resourceName ?? host} with your Zero identity.`,
|
|
170966
|
+
"The bearer token below is short-lived \u2014 use it right away:",
|
|
170967
|
+
` zero fetch <url> -H "Authorization: Bearer <token>"`,
|
|
170968
|
+
`Re-run this command for a fresh token; it reuses the stored registration.`,
|
|
170969
|
+
""
|
|
170970
|
+
].join("\n")
|
|
170971
|
+
);
|
|
170972
|
+
process.stdout.write(`${accessToken}
|
|
170973
|
+
`);
|
|
170974
|
+
};
|
|
170975
|
+
var exchangeAndEmit = async (client, discovery, host, assertion, json3, reused) => {
|
|
170976
|
+
const token = await client.auth.identity.exchange({ tokenEndpoint: discovery.tokenEndpoint, assertion }).catch((err) => {
|
|
170977
|
+
process.stderr.write(
|
|
170978
|
+
`Token exchange at ${discovery.tokenEndpoint} failed: ${truncateError(
|
|
170979
|
+
err instanceof Error ? err.message : String(err)
|
|
170980
|
+
)}
|
|
170981
|
+
`
|
|
170982
|
+
);
|
|
170983
|
+
return null;
|
|
170984
|
+
});
|
|
170985
|
+
if (!token) return false;
|
|
170986
|
+
emitAccessToken(token.accessToken, discovery, host, json3, reused);
|
|
170987
|
+
return true;
|
|
170988
|
+
};
|
|
170989
|
+
var completeServiceClaim = async (client, discovery, host, claimToken, userCode, json3) => {
|
|
170990
|
+
if (!discovery.claimEndpoint) {
|
|
170991
|
+
process.stderr.write(
|
|
170992
|
+
"The service advertises no claim endpoint \u2014 cannot complete the claim.\n"
|
|
170993
|
+
);
|
|
170994
|
+
return false;
|
|
170995
|
+
}
|
|
170996
|
+
for (let attempt = 0; attempt < CLAIM_NOT_CONFIRMED_RETRIES; attempt++) {
|
|
170997
|
+
const result = await client.auth.identity.completeClaim({
|
|
170998
|
+
claimEndpoint: discovery.claimEndpoint,
|
|
170999
|
+
claimToken,
|
|
171000
|
+
userCode
|
|
171001
|
+
}).catch((err) => {
|
|
171002
|
+
process.stderr.write(
|
|
171003
|
+
`
|
|
171004
|
+
Claim completion failed: ${truncateError(
|
|
171005
|
+
err instanceof Error ? err.message : String(err)
|
|
171006
|
+
)}
|
|
171007
|
+
`
|
|
171008
|
+
);
|
|
171009
|
+
return null;
|
|
171010
|
+
});
|
|
171011
|
+
if (result === null) return false;
|
|
171012
|
+
if (result.status === "not_confirmed") {
|
|
171013
|
+
if (!json3) process.stderr.write(".");
|
|
171014
|
+
await sleep2(CLAIM_NOT_CONFIRMED_INTERVAL_S * 1e3);
|
|
171015
|
+
continue;
|
|
171016
|
+
}
|
|
171017
|
+
if (result.status === "invalid_code") {
|
|
171018
|
+
process.stderr.write(
|
|
171019
|
+
"\nThat pairing code was not accepted. Ask the account owner to re-read the code from the claim page and try again.\n"
|
|
171020
|
+
);
|
|
171021
|
+
return false;
|
|
171022
|
+
}
|
|
171023
|
+
if (result.status === "expired") {
|
|
171024
|
+
process.stderr.write(
|
|
171025
|
+
`
|
|
171026
|
+
The claim attempt is no longer valid (${result.code}). Re-run \`zero auth identity ${host}\` for a fresh one.
|
|
171027
|
+
`
|
|
171028
|
+
);
|
|
171029
|
+
updateServiceIdentity(host, ({ pendingClaim: _drop, ...rest }) => rest);
|
|
171030
|
+
return false;
|
|
171031
|
+
}
|
|
171032
|
+
updateServiceIdentity(host, ({ pendingClaim: _drop, ...rest }) => ({
|
|
171033
|
+
...rest,
|
|
171034
|
+
assertion: result.identityAssertion,
|
|
171035
|
+
...result.assertionExpires && {
|
|
171036
|
+
assertionExpires: result.assertionExpires
|
|
171037
|
+
},
|
|
171038
|
+
...result.refreshToken && { refreshToken: result.refreshToken }
|
|
171039
|
+
}));
|
|
171040
|
+
return exchangeAndEmit(
|
|
171041
|
+
client,
|
|
171042
|
+
discovery,
|
|
171043
|
+
host,
|
|
171044
|
+
result.identityAssertion,
|
|
171045
|
+
json3,
|
|
171046
|
+
false
|
|
171047
|
+
);
|
|
171048
|
+
}
|
|
171049
|
+
process.stderr.write(
|
|
171050
|
+
"\nThe account owner never confirmed the claim on the page. Re-run with --claim-code once they have.\n"
|
|
171051
|
+
);
|
|
171052
|
+
return false;
|
|
171053
|
+
};
|
|
171054
|
+
var authIdentityCommand = (appContext) => new Command("identity").description(
|
|
171055
|
+
"Sign in to a service that accepts ID-JAG identity assertion \u2014 mints one from Zero, registers it, and prints a bearer token for the service's API"
|
|
171056
|
+
).argument("<url>", "The service host, or any URL on it").option(
|
|
171057
|
+
"--claim-code <code>",
|
|
171058
|
+
"Finish an interaction_required claim with the pairing code the account owner read off the claim page"
|
|
171059
|
+
).option("--json", "Emit machine-readable JSON instead of human text").action(
|
|
171060
|
+
async (rawUrl, opts) => {
|
|
171061
|
+
const { client, analyticsService } = appContext.services;
|
|
171062
|
+
const json3 = opts.json ?? false;
|
|
171063
|
+
const target = normalizeUrl(rawUrl);
|
|
171064
|
+
if (!target) {
|
|
171065
|
+
process.stderr.write(`Not a valid URL or host: ${rawUrl}
|
|
171066
|
+
`);
|
|
171067
|
+
process.exitCode = 1;
|
|
171068
|
+
return;
|
|
171069
|
+
}
|
|
171070
|
+
const host = target.host;
|
|
171071
|
+
const capture = (outcome2) => analyticsService.capture("auth_identity_asserted", {
|
|
171072
|
+
host,
|
|
171073
|
+
outcome: outcome2,
|
|
171074
|
+
json: json3
|
|
171075
|
+
});
|
|
171076
|
+
if (client.getCredentials().kind !== "session") {
|
|
171077
|
+
process.stderr.write(
|
|
171078
|
+
"Identity assertion needs a signed-in Zero session. Run `zero auth login` (humans) or `zero auth agent register` + `zero auth agent claim` (agents) first.\n"
|
|
171079
|
+
);
|
|
171080
|
+
process.exitCode = 1;
|
|
171081
|
+
return;
|
|
171082
|
+
}
|
|
171083
|
+
let discovery;
|
|
171084
|
+
try {
|
|
171085
|
+
discovery = await client.auth.identity.discover(target.toString());
|
|
171086
|
+
} catch (err) {
|
|
171087
|
+
capture("unsupported");
|
|
171088
|
+
process.stderr.write(
|
|
171089
|
+
`${truncateError(err instanceof Error ? err.message : String(err))}
|
|
171090
|
+
This host does not appear to support agent auth. If it serves an /auth.md, follow that instead.
|
|
171091
|
+
`
|
|
171092
|
+
);
|
|
171093
|
+
process.exitCode = 1;
|
|
171094
|
+
return;
|
|
171095
|
+
}
|
|
171096
|
+
if (opts.claimCode) {
|
|
171097
|
+
const pending = readConfig(getConfigPath()).serviceIdentities?.[host]?.pendingClaim;
|
|
171098
|
+
if (!pending) {
|
|
171099
|
+
process.stderr.write(
|
|
171100
|
+
`No pending identity claim for ${host}. Run \`zero auth identity ${host}\` first.
|
|
171101
|
+
`
|
|
171102
|
+
);
|
|
171103
|
+
process.exitCode = 1;
|
|
171104
|
+
return;
|
|
171105
|
+
}
|
|
171106
|
+
const ok = await completeServiceClaim(
|
|
171107
|
+
client,
|
|
171108
|
+
discovery,
|
|
171109
|
+
host,
|
|
171110
|
+
pending.token,
|
|
171111
|
+
opts.claimCode.trim(),
|
|
171112
|
+
json3
|
|
171113
|
+
);
|
|
171114
|
+
capture(ok ? "claim_completed" : "error");
|
|
171115
|
+
if (!ok) process.exitCode = 1;
|
|
171116
|
+
return;
|
|
171117
|
+
}
|
|
171118
|
+
if (!discovery.identityAssertionSupported) {
|
|
171119
|
+
capture("unsupported");
|
|
171120
|
+
process.stderr.write(
|
|
171121
|
+
`${discovery.resourceName ?? host} does not accept identity assertion. Fall back to its standard agent auth (see ${target.origin}/auth.md).
|
|
171122
|
+
`
|
|
171123
|
+
);
|
|
171124
|
+
process.exitCode = 1;
|
|
171125
|
+
return;
|
|
171126
|
+
}
|
|
171127
|
+
const stored = readConfig(getConfigPath()).serviceIdentities?.[host];
|
|
171128
|
+
if (stored?.assertion && isUsable(stored.assertionExpires)) {
|
|
171129
|
+
const token = await client.auth.identity.exchange({
|
|
171130
|
+
tokenEndpoint: discovery.tokenEndpoint,
|
|
171131
|
+
assertion: stored.assertion
|
|
171132
|
+
}).catch(() => null);
|
|
171133
|
+
if (token) {
|
|
171134
|
+
capture("reused");
|
|
171135
|
+
emitAccessToken(token.accessToken, discovery, host, json3, true);
|
|
171136
|
+
return;
|
|
171137
|
+
}
|
|
171138
|
+
}
|
|
171139
|
+
const runAssert = async () => {
|
|
171140
|
+
try {
|
|
171141
|
+
return await client.auth.identity.assert({
|
|
171142
|
+
url: target.toString(),
|
|
171143
|
+
discovery
|
|
171144
|
+
});
|
|
171145
|
+
} catch (err) {
|
|
171146
|
+
capture("error");
|
|
171147
|
+
if (err instanceof ZeroApiError && err.body?.error === "account_unclaimed") {
|
|
171148
|
+
process.stderr.write(
|
|
171149
|
+
"This Zero account has no human owner yet \u2014 identity assertion is only available on claimed accounts. Run `zero auth agent claim <email>` first.\n"
|
|
171150
|
+
);
|
|
171151
|
+
} else {
|
|
171152
|
+
process.stderr.write(
|
|
171153
|
+
`Identity assertion failed: ${truncateError(err instanceof Error ? err.message : String(err))}
|
|
171154
|
+
`
|
|
171155
|
+
);
|
|
171156
|
+
}
|
|
171157
|
+
return null;
|
|
171158
|
+
}
|
|
171159
|
+
};
|
|
171160
|
+
let outcome = await runAssert();
|
|
171161
|
+
if (!outcome) {
|
|
171162
|
+
process.exitCode = 1;
|
|
171163
|
+
return;
|
|
171164
|
+
}
|
|
171165
|
+
if (outcome.status === "consent_required") {
|
|
171166
|
+
const requested = await client.auth.identity.requestGrant({
|
|
171167
|
+
audience: discovery.mintAudience,
|
|
171168
|
+
resourceUrl: target.origin
|
|
171169
|
+
}).catch((err) => {
|
|
171170
|
+
process.stderr.write(
|
|
171171
|
+
`Could not create the consent request: ${truncateError(err instanceof Error ? err.message : String(err))}
|
|
171172
|
+
`
|
|
171173
|
+
);
|
|
171174
|
+
return null;
|
|
171175
|
+
});
|
|
171176
|
+
if (!requested) {
|
|
171177
|
+
capture("error");
|
|
171178
|
+
process.exitCode = 1;
|
|
171179
|
+
return;
|
|
171180
|
+
}
|
|
171181
|
+
process.stderr.write(
|
|
171182
|
+
[
|
|
171183
|
+
"",
|
|
171184
|
+
`Signing in to ${discovery.resourceName ?? host} needs the user's one-time consent.`,
|
|
171185
|
+
`Send them here to approve: ${requested.approveUrl}`,
|
|
171186
|
+
`(valid until ${requested.expiresAt}; the decision is remembered on their Zero account)`,
|
|
171187
|
+
"Waiting for their decision\u2026"
|
|
171188
|
+
].join("\n")
|
|
171189
|
+
);
|
|
171190
|
+
let decision = "expired";
|
|
171191
|
+
for (; ; ) {
|
|
171192
|
+
const status = await client.auth.identity.grantRequest(requested.requestId).catch(() => null);
|
|
171193
|
+
if (status && status.status !== "pending") {
|
|
171194
|
+
decision = status.status;
|
|
171195
|
+
break;
|
|
171196
|
+
}
|
|
171197
|
+
process.stderr.write(".");
|
|
171198
|
+
await sleep2(CONSENT_POLL_INTERVAL_S * 1e3);
|
|
171199
|
+
}
|
|
171200
|
+
process.stderr.write("\n");
|
|
171201
|
+
if (decision === "denied") {
|
|
171202
|
+
capture("consent_denied");
|
|
171203
|
+
process.stderr.write(
|
|
171204
|
+
"The user denied the request \u2014 not signing in.\n"
|
|
171205
|
+
);
|
|
171206
|
+
process.exitCode = 1;
|
|
171207
|
+
return;
|
|
171208
|
+
}
|
|
171209
|
+
if (decision === "expired") {
|
|
171210
|
+
capture("consent_expired");
|
|
171211
|
+
process.stderr.write(
|
|
171212
|
+
"The approval link expired before the user decided. Re-run to mint a fresh one.\n"
|
|
171213
|
+
);
|
|
171214
|
+
process.exitCode = 1;
|
|
171215
|
+
return;
|
|
171216
|
+
}
|
|
171217
|
+
outcome = await runAssert();
|
|
171218
|
+
if (!outcome) {
|
|
171219
|
+
process.exitCode = 1;
|
|
171220
|
+
return;
|
|
171221
|
+
}
|
|
171222
|
+
if (outcome.status === "consent_required") {
|
|
171223
|
+
capture("error");
|
|
171224
|
+
process.stderr.write(
|
|
171225
|
+
"The issuer still reports consent_required after approval \u2014 retry, and report this if it persists.\n"
|
|
171226
|
+
);
|
|
171227
|
+
process.exitCode = 1;
|
|
171228
|
+
return;
|
|
171229
|
+
}
|
|
171230
|
+
}
|
|
171231
|
+
switch (outcome.status) {
|
|
171232
|
+
case "ok": {
|
|
171233
|
+
updateServiceIdentity(host, ({ pendingClaim: _drop, ...rest }) => ({
|
|
171234
|
+
...rest,
|
|
171235
|
+
registrationId: outcome.registration.registrationId,
|
|
171236
|
+
assertion: outcome.registration.identityAssertion,
|
|
171237
|
+
...outcome.registration.assertionExpires && {
|
|
171238
|
+
assertionExpires: outcome.registration.assertionExpires
|
|
171239
|
+
},
|
|
171240
|
+
...outcome.registration.refreshToken && {
|
|
171241
|
+
refreshToken: outcome.registration.refreshToken
|
|
171242
|
+
},
|
|
171243
|
+
...discovery.resourceName && {
|
|
171244
|
+
resourceName: discovery.resourceName
|
|
171245
|
+
}
|
|
171246
|
+
}));
|
|
171247
|
+
const ok = await exchangeAndEmit(
|
|
171248
|
+
client,
|
|
171249
|
+
discovery,
|
|
171250
|
+
host,
|
|
171251
|
+
outcome.registration.identityAssertion,
|
|
171252
|
+
json3,
|
|
171253
|
+
false
|
|
171254
|
+
);
|
|
171255
|
+
capture(ok ? "ok" : "error");
|
|
171256
|
+
if (!ok) process.exitCode = 1;
|
|
171257
|
+
return;
|
|
171258
|
+
}
|
|
171259
|
+
case "unsupported":
|
|
171260
|
+
case "issuer_not_enabled": {
|
|
171261
|
+
capture(outcome.status);
|
|
171262
|
+
process.stderr.write(
|
|
171263
|
+
`${discovery.resourceName ?? host} does not accept Zero identity assertions${outcome.status === "issuer_not_enabled" ? " (Zero is not on its issuer trust list)" : ""}. Fall back to its standard agent auth (see ${target.origin}/auth.md).
|
|
171264
|
+
`
|
|
171265
|
+
);
|
|
171266
|
+
process.exitCode = 1;
|
|
171267
|
+
return;
|
|
171268
|
+
}
|
|
171269
|
+
case "login_required": {
|
|
171270
|
+
capture("login_required");
|
|
171271
|
+
process.stderr.write(
|
|
171272
|
+
`The service requires a fresher Zero sign-in${outcome.description ? ` (${outcome.description})` : ""}. Run \`zero auth login\` again, then retry \u2014 nothing at the service side helps.
|
|
171273
|
+
`
|
|
171274
|
+
);
|
|
171275
|
+
process.exitCode = 1;
|
|
171276
|
+
return;
|
|
171277
|
+
}
|
|
171278
|
+
case "interaction_required": {
|
|
171279
|
+
capture("interaction_required");
|
|
171280
|
+
const { claim: claim3 } = outcome;
|
|
171281
|
+
if (!claim3.claimToken || !claim3.verificationUri) {
|
|
171282
|
+
process.stderr.write(
|
|
171283
|
+
`The service requires account-owner confirmation but returned no claim handle${claim3.description ? ` (${claim3.description})` : ""}.
|
|
171284
|
+
`
|
|
171285
|
+
);
|
|
171286
|
+
process.exitCode = 1;
|
|
171287
|
+
return;
|
|
171288
|
+
}
|
|
171289
|
+
updateServiceIdentity(host, (current) => ({
|
|
171290
|
+
...current,
|
|
171291
|
+
pendingClaim: {
|
|
171292
|
+
token: claim3.claimToken,
|
|
171293
|
+
verificationUri: claim3.verificationUri ?? void 0,
|
|
171294
|
+
expiresAt: claim3.claimTokenExpires ?? void 0
|
|
171295
|
+
}
|
|
171296
|
+
}));
|
|
171297
|
+
if (json3) {
|
|
171298
|
+
process.stdout.write(
|
|
171299
|
+
`${JSON.stringify({
|
|
171300
|
+
status: "interaction_required",
|
|
171301
|
+
verificationUri: claim3.verificationUri,
|
|
171302
|
+
expiresAt: claim3.claimTokenExpires,
|
|
171303
|
+
next: `The claim page shows the account owner a pairing code. Re-run \`zero auth identity ${host} --claim-code <code>\` with it.`
|
|
171304
|
+
})}
|
|
171305
|
+
`
|
|
171306
|
+
);
|
|
171307
|
+
} else {
|
|
171308
|
+
process.stderr.write(
|
|
171309
|
+
[
|
|
171310
|
+
"",
|
|
171311
|
+
`Your email already has an account at ${discovery.resourceName ?? host} \u2014 its owner must confirm the link.`,
|
|
171312
|
+
`Send them here: ${claim3.verificationUri}`,
|
|
171313
|
+
"The page shows THEM a pairing code.",
|
|
171314
|
+
...claim3.claimTokenExpires ? [`(link valid until ${claim3.claimTokenExpires})`] : [],
|
|
171315
|
+
""
|
|
171316
|
+
].join("\n")
|
|
171317
|
+
);
|
|
171318
|
+
}
|
|
171319
|
+
if (!process.stdin.isTTY) {
|
|
171320
|
+
if (!json3) {
|
|
171321
|
+
process.stderr.write(
|
|
171322
|
+
`Once they read the code back, run: zero auth identity ${host} --claim-code <code>
|
|
171323
|
+
`
|
|
171324
|
+
);
|
|
171325
|
+
}
|
|
171326
|
+
return;
|
|
171327
|
+
}
|
|
171328
|
+
const rl = createInterface$1({
|
|
171329
|
+
input: process.stdin,
|
|
171330
|
+
output: process.stderr
|
|
171331
|
+
});
|
|
171332
|
+
const userCode = (await rl.question("Enter the pairing code they read back: ")).trim();
|
|
171333
|
+
rl.close();
|
|
171334
|
+
if (!userCode) {
|
|
171335
|
+
process.stderr.write(
|
|
171336
|
+
`No code entered. Re-run \`zero auth identity ${host} --claim-code <code>\` when you have it.
|
|
171337
|
+
`
|
|
171338
|
+
);
|
|
171339
|
+
process.exitCode = 1;
|
|
171340
|
+
return;
|
|
171341
|
+
}
|
|
171342
|
+
const ok = await completeServiceClaim(
|
|
171343
|
+
client,
|
|
171344
|
+
discovery,
|
|
171345
|
+
host,
|
|
171346
|
+
claim3.claimToken,
|
|
171347
|
+
userCode,
|
|
171348
|
+
json3
|
|
171349
|
+
);
|
|
171350
|
+
if (!ok) process.exitCode = 1;
|
|
171351
|
+
return;
|
|
171352
|
+
}
|
|
171353
|
+
}
|
|
171354
|
+
}
|
|
171355
|
+
);
|
|
171356
|
+
|
|
171357
|
+
// src/commands/auth-command.ts
|
|
171358
|
+
var getConfigPath2 = () => join$1(homedir(), ".zero", "config.json");
|
|
170613
171359
|
var FINISH_POLL_INTERVAL_S = 2;
|
|
170614
171360
|
var FINISH_MAX_WAIT_MS = 10 * 60 * 1e3;
|
|
170615
|
-
var
|
|
171361
|
+
var sleep3 = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
170616
171362
|
var persistSession = (result) => {
|
|
170617
|
-
const path3 =
|
|
171363
|
+
const path3 = getConfigPath2();
|
|
170618
171364
|
ensureSecureDir(dirname(path3));
|
|
170619
171365
|
const current = readConfig(path3);
|
|
170620
171366
|
const next = {
|
|
@@ -170632,7 +171378,7 @@ var LEGACY_KEY_NOTICE = {
|
|
|
170632
171378
|
message: "A wallet private key from a previous setup is still on this machine. Now that you're signed in, your managed Zero wallet signs payments \u2014 the old key is no longer used.",
|
|
170633
171379
|
keep: "zero wallet set <key>"
|
|
170634
171380
|
};
|
|
170635
|
-
var hasLegacyPrivateKey = () => readConfig(
|
|
171381
|
+
var hasLegacyPrivateKey = () => readConfig(getConfigPath2()).privateKey !== void 0;
|
|
170636
171382
|
var reportSignedIn = (result, json3, la) => {
|
|
170637
171383
|
const legacyKeyPresent = hasLegacyPrivateKey();
|
|
170638
171384
|
la?.analytics.capture("auth_login_completed", {
|
|
@@ -170732,7 +171478,7 @@ var runFinish = async (client, deviceCode, json3, la) => {
|
|
|
170732
171478
|
} else if (!json3) {
|
|
170733
171479
|
process.stdout.write(".");
|
|
170734
171480
|
}
|
|
170735
|
-
await
|
|
171481
|
+
await sleep3(FINISH_POLL_INTERVAL_S * 1e3);
|
|
170736
171482
|
continue;
|
|
170737
171483
|
}
|
|
170738
171484
|
if (result.status === "expired") {
|
|
@@ -170759,7 +171505,7 @@ User code: ${start.userCode}
|
|
|
170759
171505
|
});
|
|
170760
171506
|
}
|
|
170761
171507
|
while (Date.now() < start.expiresAt) {
|
|
170762
|
-
await
|
|
171508
|
+
await sleep3(start.pollInterval * 1e3);
|
|
170763
171509
|
const result = await client.auth.device.poll(start.deviceCode);
|
|
170764
171510
|
if (result.status === "pending") {
|
|
170765
171511
|
process.stdout.write(".");
|
|
@@ -170795,7 +171541,7 @@ var authLoginCommand = (appContext) => new Command("login").description("Sign in
|
|
|
170795
171541
|
return;
|
|
170796
171542
|
}
|
|
170797
171543
|
const mode = opts.finish ? "finish" : "interactive";
|
|
170798
|
-
const hadPriorSession = readConfig(
|
|
171544
|
+
const hadPriorSession = readConfig(getConfigPath2()).session !== void 0;
|
|
170799
171545
|
analyticsService.capture("auth_login_started", { mode, json: json3 });
|
|
170800
171546
|
const la = {
|
|
170801
171547
|
analytics: analyticsService,
|
|
@@ -170820,7 +171566,7 @@ var authLoginCommand = (appContext) => new Command("login").description("Sign in
|
|
|
170820
171566
|
);
|
|
170821
171567
|
var authLogoutCommand = (appContext) => new Command("logout").description("Sign out of Zero").action(async () => {
|
|
170822
171568
|
const { client, analyticsService } = appContext.services;
|
|
170823
|
-
const path3 =
|
|
171569
|
+
const path3 = getConfigPath2();
|
|
170824
171570
|
const config3 = readConfig(path3);
|
|
170825
171571
|
const hadSession = config3.session !== void 0;
|
|
170826
171572
|
if (config3.session) {
|
|
@@ -170925,7 +171671,7 @@ var authAgentRegisterCommand = (appContext) => new Command("register").descripti
|
|
|
170925
171671
|
).option("--json", "Emit machine-readable JSON instead of human text").action(async (opts) => {
|
|
170926
171672
|
const { client } = appContext.services;
|
|
170927
171673
|
const json3 = opts.json ?? false;
|
|
170928
|
-
const path3 =
|
|
171674
|
+
const path3 = getConfigPath2();
|
|
170929
171675
|
if (readConfig(path3).session) {
|
|
170930
171676
|
process.stderr.write(
|
|
170931
171677
|
"Already signed in. Run `zero auth logout` first if you want a fresh account.\n"
|
|
@@ -171005,12 +171751,12 @@ Agent registration may not be enabled for this service yet.
|
|
|
171005
171751
|
].join("\n")
|
|
171006
171752
|
);
|
|
171007
171753
|
});
|
|
171008
|
-
var
|
|
171009
|
-
var
|
|
171754
|
+
var CLAIM_NOT_CONFIRMED_RETRIES2 = 12;
|
|
171755
|
+
var CLAIM_NOT_CONFIRMED_INTERVAL_S2 = 5;
|
|
171010
171756
|
var completeClaimAndExchange = async (agentAuth, claimToken, userCode, email5, json3) => {
|
|
171011
|
-
const path3 =
|
|
171757
|
+
const path3 = getConfigPath2();
|
|
171012
171758
|
let verified = null;
|
|
171013
|
-
for (let attempt = 0; attempt <
|
|
171759
|
+
for (let attempt = 0; attempt < CLAIM_NOT_CONFIRMED_RETRIES2; attempt++) {
|
|
171014
171760
|
const result = await agentAuth.completeClaim({ claimToken, userCode }).catch((err) => {
|
|
171015
171761
|
process.stderr.write(
|
|
171016
171762
|
`
|
|
@@ -171022,7 +171768,7 @@ Claim completion failed: ${truncateError(err instanceof Error ? err.message : St
|
|
|
171022
171768
|
if (result === null) return false;
|
|
171023
171769
|
if (result.status === "not_confirmed") {
|
|
171024
171770
|
if (!json3) process.stdout.write(".");
|
|
171025
|
-
await
|
|
171771
|
+
await sleep3(CLAIM_NOT_CONFIRMED_INTERVAL_S2 * 1e3);
|
|
171026
171772
|
continue;
|
|
171027
171773
|
}
|
|
171028
171774
|
if (result.status === "invalid_code") {
|
|
@@ -171090,7 +171836,7 @@ var authAgentClaimCommand = (appContext) => new Command("claim").description(
|
|
|
171090
171836
|
).option("--json", "Emit machine-readable JSON instead of human text").action(async (email5, opts) => {
|
|
171091
171837
|
const { client } = appContext.services;
|
|
171092
171838
|
const json3 = opts.json ?? false;
|
|
171093
|
-
const config3 = readConfig(
|
|
171839
|
+
const config3 = readConfig(getConfigPath2());
|
|
171094
171840
|
const registration = config3.agentRegistration;
|
|
171095
171841
|
if (!registration?.claimToken) {
|
|
171096
171842
|
process.stderr.write(
|
|
@@ -171183,13 +171929,13 @@ var authAgentCommand = (appContext) => {
|
|
|
171183
171929
|
return cmd;
|
|
171184
171930
|
};
|
|
171185
171931
|
var ensureAuthenticated = async (client, shouldOpen = true) => {
|
|
171186
|
-
const session5 = readConfig(
|
|
171932
|
+
const session5 = readConfig(getConfigPath2()).session;
|
|
171187
171933
|
if (session5) return;
|
|
171188
171934
|
process.stdout.write(
|
|
171189
171935
|
"\nZero requires authentication before handing off to Claude.\n\n"
|
|
171190
171936
|
);
|
|
171191
171937
|
await runInteractive(client, shouldOpen);
|
|
171192
|
-
if (!readConfig(
|
|
171938
|
+
if (!readConfig(getConfigPath2()).session) {
|
|
171193
171939
|
process.exitCode = 1;
|
|
171194
171940
|
process.exit(1);
|
|
171195
171941
|
}
|
|
@@ -171198,6 +171944,7 @@ var authCommand = (appContext) => {
|
|
|
171198
171944
|
const cmd = new Command("auth").description("Authentication commands");
|
|
171199
171945
|
cmd.addCommand(authLoginCommand(appContext).alias("register"));
|
|
171200
171946
|
cmd.addCommand(authAgentCommand(appContext));
|
|
171947
|
+
cmd.addCommand(authIdentityCommand(appContext));
|
|
171201
171948
|
cmd.addCommand(authLogoutCommand(appContext));
|
|
171202
171949
|
cmd.addCommand(authExchangeCommand(appContext));
|
|
171203
171950
|
cmd.addCommand(authWhoamiCommand(appContext), { hidden: true });
|