(options: {
cloudBaseUrl: string;
deviceCode: string;
expiresIn: number | undefined;
interval: number | undefined;
fetchImpl?: typeof fetch;
now?: () => number;
})
| 404 | } |
| 405 | |
| 406 | async function pollDeviceAuth(options: { |
| 407 | cloudBaseUrl: string; |
| 408 | deviceCode: string; |
| 409 | expiresIn: number | undefined; |
| 410 | interval: number | undefined; |
| 411 | fetchImpl?: typeof fetch; |
| 412 | now?: () => number; |
| 413 | }): Promise<DeviceAuthPollResponse> { |
| 414 | const now = options.now ?? Date.now; |
| 415 | const timeoutMs = Math.min((options.expiresIn ?? 600) * 1000, DEVICE_POLL_TIMEOUT_MS); |
| 416 | const deadline = now() + timeoutMs; |
| 417 | let intervalMs = Math.max( |
| 418 | MIN_POLL_INTERVAL_MS, |
| 419 | (options.interval ?? DEFAULT_POLL_INTERVAL_MS / 1000) * 1000, |
| 420 | ); |
| 421 | while (now() < deadline) { |
| 422 | const response = await postJson<DeviceAuthPollResponse>({ |
| 423 | baseUrl: options.cloudBaseUrl, |
| 424 | pathName: DEVICE_AUTH_POLL_PATH, |
| 425 | body: { deviceCode: options.deviceCode }, |
| 426 | fetchImpl: options.fetchImpl, |
| 427 | }); |
| 428 | if (response.status === 'approved' || hasToken(response.accessToken)) return response; |
| 429 | if (response.status === 'slow_down' || response.error === 'slow_down') { |
| 430 | intervalMs += MIN_POLL_INTERVAL_MS; |
| 431 | } else if ( |
| 432 | response.status !== 'authorization_pending' && |
| 433 | response.error !== 'authorization_pending' |
| 434 | ) { |
| 435 | throw new AppError('UNAUTHORIZED', 'Device authorization was not approved.', { |
| 436 | status: response.status, |
| 437 | error: response.error, |
| 438 | }); |
| 439 | } |
| 440 | await sleep(intervalMs); |
| 441 | } |
| 442 | throw new AppError('TIMEOUT', 'Device authorization expired before approval.'); |
| 443 | } |
| 444 | |
| 445 | async function postJson<T>(options: { |
| 446 | baseUrl: string; |
no test coverage detected