(
flow: DeviceFlow
)
| 644 | } |
| 645 | |
| 646 | private async pollDeviceTokenOnce( |
| 647 | flow: DeviceFlow |
| 648 | ): Promise< |
| 649 | | { kind: "success"; auth: CodexOauthAuth } |
| 650 | | { kind: "pending" } |
| 651 | | { kind: "fatal"; message: string } |
| 652 | > { |
| 653 | try { |
| 654 | const response = await fetch(CODEX_OAUTH_DEVICE_TOKEN_POLL_URL, { |
| 655 | method: "POST", |
| 656 | headers: { "Content-Type": "application/json" }, |
| 657 | body: JSON.stringify({ device_auth_id: flow.deviceAuthId, user_code: flow.userCode }), |
| 658 | signal: flow.abortController.signal, |
| 659 | }); |
| 660 | |
| 661 | if (response.status === 403 || response.status === 404) { |
| 662 | return { kind: "pending" }; |
| 663 | } |
| 664 | |
| 665 | if (response.status !== 200) { |
| 666 | const errorText = await response.text().catch(() => ""); |
| 667 | const prefix = `Codex OAuth device token poll failed (${response.status})`; |
| 668 | return { kind: "fatal", message: errorText ? `${prefix}: ${errorText}` : prefix }; |
| 669 | } |
| 670 | |
| 671 | const json = (await response.json().catch(() => null)) as unknown; |
| 672 | if (!isPlainObject(json)) { |
| 673 | return { kind: "fatal", message: "Codex OAuth device token poll returned invalid JSON" }; |
| 674 | } |
| 675 | |
| 676 | const authorizationCode = |
| 677 | typeof json.authorization_code === "string" ? json.authorization_code : null; |
| 678 | const codeVerifier = typeof json.code_verifier === "string" ? json.code_verifier : null; |
| 679 | |
| 680 | if (!authorizationCode || !codeVerifier) { |
| 681 | return { |
| 682 | kind: "fatal", |
| 683 | message: "Codex OAuth device token poll response missing required fields", |
| 684 | }; |
| 685 | } |
| 686 | |
| 687 | const tokenResult = await this.exchangeCodeForTokens({ |
| 688 | code: authorizationCode, |
| 689 | redirectUri: "https://auth.openai.com/deviceauth/callback", |
| 690 | codeVerifier, |
| 691 | }); |
| 692 | |
| 693 | if (!tokenResult.success) { |
| 694 | return { kind: "fatal", message: tokenResult.error }; |
| 695 | } |
| 696 | |
| 697 | return { kind: "success", auth: tokenResult.data }; |
| 698 | } catch (error) { |
| 699 | // Abort is treated as cancellation. |
| 700 | if (flow.abortController.signal.aborted) { |
| 701 | return { kind: "fatal", message: "OAuth flow cancelled" }; |
| 702 | } |
| 703 |
no test coverage detected