| 183 | }; |
| 184 | |
| 185 | export const requestDeviceCode = async (discovery: CliLoginDiscovery): Promise<DeviceCodeGrant> => { |
| 186 | const response = await post( |
| 187 | discovery.deviceAuthorizationEndpoint, |
| 188 | { client_id: discovery.clientId, scope: discovery.scope }, |
| 189 | discovery.requestFormat, |
| 190 | ); |
| 191 | const body = await readJson(response); |
| 192 | if (!response.ok) { |
| 193 | throw new DeviceLoginError( |
| 194 | `Device authorization failed (${response.status}): ${asString(body.error_description) ?? asString(body.error) ?? "unknown error"}`, |
| 195 | ); |
| 196 | } |
| 197 | const deviceCode = asString(body.device_code); |
| 198 | const userCode = asString(body.user_code); |
| 199 | const verificationUri = asString(body.verification_uri); |
| 200 | if (!deviceCode || !userCode || !verificationUri) { |
| 201 | throw new DeviceLoginError("Device authorization response was missing required fields."); |
| 202 | } |
| 203 | return { |
| 204 | deviceCode, |
| 205 | userCode, |
| 206 | verificationUri, |
| 207 | verificationUriComplete: asString(body.verification_uri_complete), |
| 208 | expiresInSeconds: asNumber(body.expires_in) ?? 300, |
| 209 | intervalSeconds: asNumber(body.interval) ?? DEFAULT_INTERVAL_SECONDS, |
| 210 | }; |
| 211 | }; |
| 212 | |
| 213 | const sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms)); |
| 214 | |