Skip to content

Commit 9fea621

Browse files
xuyushun441-sysos-zhuangclaude
authored
feat(cloud-connection): device context on the approval URL (runtime_name/runtime_version) (#1784)
bind/start appends the requesting runtime's hostname + version to verification_uri / verification_uri_complete so the cloud approval page can name the device being authorized (ADR runtime-identity-binding §2.3). Display-only informed consent — paired with the approval page's 'only approve if you started this' warning (objectui side). Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 48051ff commit 9fea621

3 files changed

Lines changed: 28 additions & 2 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@objectstack/cloud-connection": patch
3+
---
4+
5+
bind/start appends device context (`runtime_name`, `runtime_version`) to the device-flow verification URLs so the cloud approval page can show WHAT is being authorized (ADR runtime-identity-binding §2.3). Display-only informed-consent context; the approval page pairs it with an "only approve if you started this" warning.

packages/cloud-connection/src/cloud-connection-plugin.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,11 +271,27 @@ export class CloudConnectionPlugin implements Plugin {
271271
});
272272
const json: any = await resp.json().catch(() => ({}));
273273
if (!resp.ok) return c.json({ success: false, error: { code: 'device_code_failed', message: json?.error ?? `device/code ${resp.status}` } }, 502);
274+
// Device context for the approval page (ADR
275+
// runtime-identity-binding §2.3): name the requesting
276+
// runtime in the verification URL so the approver sees
277+
// WHAT they are authorizing. Display-only — informed
278+
// consent, not an authenticity proof (the page carries
279+
// the "only approve if you started this" warning).
280+
const withContext = (uri: unknown): string | undefined => {
281+
if (typeof uri !== 'string' || !uri) return undefined;
282+
try {
283+
const u = new URL(uri);
284+
try { u.searchParams.set('runtime_name', hostname()); } catch { /* no hostname */ }
285+
const ver = (process.env.OS_RUNTIME_VERSION ?? this.version) || '';
286+
if (ver) u.searchParams.set('runtime_version', ver);
287+
return u.toString();
288+
} catch { return uri; }
289+
};
274290
return c.json({ success: true, data: {
275291
device_code: json.device_code,
276292
user_code: json.user_code,
277-
verification_uri: json.verification_uri,
278-
verification_uri_complete: json.verification_uri_complete,
293+
verification_uri: withContext(json.verification_uri),
294+
verification_uri_complete: withContext(json.verification_uri_complete),
279295
interval: json.interval ?? 5,
280296
expires_in: json.expires_in ?? 600,
281297
} });

packages/cloud-connection/src/connection-credential-store.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,11 @@ describe('CloudConnectionPlugin credential behavior', () => {
208208
);
209209
expect(res.status).toBe(200);
210210
expect(res.payload.data.user_code).toBe('ABCD-EFGH');
211+
// Device context rides the verification URL for the approval page.
212+
const complete = new URL(res.payload.data.verification_uri_complete);
213+
expect(complete.searchParams.get('user_code')).toBe('ABCD-EFGH');
214+
expect(complete.searchParams.get('runtime_name')).toBeTruthy();
215+
expect(complete.searchParams.get('runtime_version')).toBeTruthy();
211216
});
212217

213218
it('unbind without an environment id revokes via the bearer (empty body) and clears the store', async () => {

0 commit comments

Comments
 (0)