@aexol/spectral 0.9.122 → 0.9.124
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.
|
@@ -62,6 +62,13 @@ export interface RegisterResponse {
|
|
|
62
62
|
* Returns `null` if the token isn't a parseable JWT or has no `exp`.
|
|
63
63
|
*/
|
|
64
64
|
export declare function decodeJwtExp(jwt: string): number | null;
|
|
65
|
+
/**
|
|
66
|
+
* Decode the `sub` (subject) claim from a JWT payload without signature
|
|
67
|
+
* verification. Used to compare the currently logged-in user identity against
|
|
68
|
+
* the `ownerId` stored in `machine.json` so we can detect account switches
|
|
69
|
+
* and force re-registration (which triggers the backend ownership transfer).
|
|
70
|
+
*/
|
|
71
|
+
export declare function decodeJwtSub(jwt: string): string | null;
|
|
65
72
|
/** True when the JWT is past expiry (with skew). Treats undecodable as expired. */
|
|
66
73
|
export declare function isJwtExpired(jwt: string, skewSec?: number): boolean;
|
|
67
74
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../../src/relay/registration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,EAA4B,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAElF,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,+FAA+F;IAC/F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8BAA8B;IAC9B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,aAAa,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWvD;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,SAAK,GAAG,OAAO,CAI/D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,gBAAgB,CAAC,
|
|
1
|
+
{"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../../src/relay/registration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,EAA4B,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAElF,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,+FAA+F;IAC/F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8BAA8B;IAC9B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,aAAa,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWvD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUvD;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,SAAK,GAAG,OAAO,CAI/D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,gBAAgB,CAAC,CA0G3B"}
|
|
@@ -49,6 +49,25 @@ export function decodeJwtExp(jwt) {
|
|
|
49
49
|
return null;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Decode the `sub` (subject) claim from a JWT payload without signature
|
|
54
|
+
* verification. Used to compare the currently logged-in user identity against
|
|
55
|
+
* the `ownerId` stored in `machine.json` so we can detect account switches
|
|
56
|
+
* and force re-registration (which triggers the backend ownership transfer).
|
|
57
|
+
*/
|
|
58
|
+
export function decodeJwtSub(jwt) {
|
|
59
|
+
const parts = jwt.split(".");
|
|
60
|
+
if (parts.length < 2)
|
|
61
|
+
return null;
|
|
62
|
+
try {
|
|
63
|
+
const payloadJson = Buffer.from(parts[1], "base64url").toString("utf8");
|
|
64
|
+
const payload = JSON.parse(payloadJson);
|
|
65
|
+
return typeof payload.sub === "string" ? payload.sub : null;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
52
71
|
/** True when the JWT is past expiry (with skew). Treats undecodable as expired. */
|
|
53
72
|
export function isJwtExpired(jwt, skewSec = 60) {
|
|
54
73
|
const exp = decodeJwtExp(jwt);
|
|
@@ -73,7 +92,22 @@ export async function ensureMachineRegistered(deps) {
|
|
|
73
92
|
const skew = deps.expirySkewSec ?? 60;
|
|
74
93
|
const existing = await loadMachine();
|
|
75
94
|
if (existing && !isJwtExpired(existing.machineJwt, skew)) {
|
|
76
|
-
|
|
95
|
+
// The cached machine JWT is still valid. If the caller authenticated with a
|
|
96
|
+
// user JWT (OAuth login) and the cached machine was previously owned by a
|
|
97
|
+
// different user, we must NOT short-circuit — we need to hit the register
|
|
98
|
+
// endpoint so the backend can transfer ownership (upsert ownerId).
|
|
99
|
+
// Team-API-key auth (no userJwt) keeps the existing reuse behavior.
|
|
100
|
+
let shouldReuse = true;
|
|
101
|
+
if (deps.userJwt) {
|
|
102
|
+
const currentUser = decodeJwtSub(deps.userJwt);
|
|
103
|
+
if (currentUser && currentUser !== existing.ownerId) {
|
|
104
|
+
console.log("Machine registered under a different account, re-registering to transfer ownership...");
|
|
105
|
+
shouldReuse = false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (shouldReuse) {
|
|
109
|
+
return { reused: true, record: existing };
|
|
110
|
+
}
|
|
77
111
|
}
|
|
78
112
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
79
113
|
const url = `${deps.backendUrl.replace(/\/$/, "")}/api/machines/register`;
|