visual continunity
This commit is contained in:
@@ -7,12 +7,32 @@ export interface DeviceInfo {
|
||||
scopes?: string;
|
||||
}
|
||||
|
||||
/** Distinguishable failure modes the verify page renders differently. */
|
||||
export type DeviceLookupError = 'unauthorized' | 'not-found' | 'failed';
|
||||
|
||||
/** Thrown by lookupDeviceCode so the page can show a tailored message. */
|
||||
export class DeviceLookupFailure extends Error {
|
||||
constructor(readonly kind: DeviceLookupError) {
|
||||
super(kind);
|
||||
this.name = 'DeviceLookupFailure';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a device user-code. The backend returns HTTP 200 with `{valid:false}`
|
||||
* for unknown/expired codes (NOT a non-2xx), so the body must be inspected — a
|
||||
* 2xx alone does not mean the code is good. A 401 means the caller isn't signed
|
||||
* in and must authenticate before authorizing a device.
|
||||
*/
|
||||
export async function lookupDeviceCode(code: string): Promise<DeviceInfo> {
|
||||
const res = await apiFetch(`/api/auth/device/verify?code=${encodeURIComponent(code)}`, {
|
||||
credentials: 'same-origin'
|
||||
});
|
||||
if (!res.ok) throw new Error(`device lookup failed: ${res.status}`);
|
||||
return (await res.json()) as DeviceInfo;
|
||||
if (res.status === 401) throw new DeviceLookupFailure('unauthorized');
|
||||
if (!res.ok) throw new DeviceLookupFailure('failed');
|
||||
const data = (await res.json()) as DeviceInfo & { valid?: boolean };
|
||||
if (data.valid === false) throw new DeviceLookupFailure('not-found');
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function decideDevice(userCode: string, action: 'approve' | 'deny'): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user