@octokit/auth-oauth-device
GitHub OAuth Device Flow authentication strategy for Octokit-based JavaScript apps.
Repository Health
Technical Analysis
@octokit/auth-oauth-device implements GitHub’s OAuth Device Flow, the authentication strategy used by CLI tools and browser-based apps that cannot securely store a client secret. It requests a device and user verification code, walks the caller through prompting the user to enter that code at github.com/login/device, and polls the token endpoint on the caller’s behalf, backing off automatically on authorization_pending and slow_down responses until an access token is issued or the flow expires.
The package works for both traditional OAuth Apps and GitHub Apps with user-to-server tokens, including GitHub Apps with expiring tokens enabled. It exports a createOAuthDeviceAuth() factory that returns an auth() function plus a hook() that can be wired directly into @octokit/core or @octokit/request, so authenticated requests carry a valid token without any manual header management.
What You Get
- A
createOAuthDeviceAuth()factory implementing all three steps of GitHub’s OAuth Device Flow (request codes, prompt user, poll for token) - Support for both OAuth Apps and GitHub Apps, including GitHub Apps with expiring user tokens and automatic refresh metadata
- Automatic polling backoff that respects GitHub’s
authorization_pendingandslow_downresponses instead of hammering the token endpoint - A drop-in
hook()for@octokit/requestand@octokit/corethat authenticates outgoing requests transparently - Full TypeScript types for every strategy option, auth option, and authentication result shape
- Works isomorphically in Node and the browser via conditional ESM exports
Common Use Cases
- Authenticating a GitHub CLI tool where the user has no browser-based redirect flow available
- Building a desktop or terminal app that needs a GitHub-authenticated Octokit client without shipping a client secret
- Adding user login to a GitHub App with user-to-server tokens, including apps with expiring/refreshable tokens
- Prototyping OAuth App authentication quickly by printing the verification URL and code to the console
Under The Hood
Architecture - The package is a thin, purely functional layer over @octokit/oauth-methods. createOAuthDeviceAuth() (src/index.ts) builds an immutable state object holding clientId, clientType, scopes, and a request instance, then returns an auth() function bound to that state plus a hook() for Octokit request interception. auth() (src/auth.ts) simply forwards to getOAuthAccessToken() (src/get-oauth-access-token.ts), which is the real engine: it calls createDeviceCode() to get a device/user code pair, awaits the caller-supplied onVerification() callback so the user can approve the code in a browser, then recursively polls exchangeDeviceCode() via a waitForAccessToken() loop that reads GitHub’s authorization_pending/slow_down error codes to decide whether to keep waiting. hook() (src/hook.ts) wraps this in an Octokit-compatible request middleware, short-circuiting the two endpoints used internally (/login/device/code, /login/oauth/access_token) to avoid recursive authentication.
Tech Stack - Pure TypeScript targeting Node >=20 and browsers, built with tsc plus a small esbuild-based bundling script (scripts/build.mjs) producing conditional ESM exports. Runtime dependencies are all first-party Octokit packages (@octokit/oauth-methods, @octokit/request, @octokit/types) plus universal-user-agent, keeping the dependency surface minimal and isomorphic between Node and browser targets.
Code Quality - Test coverage is substantial: test/index.test.ts is 1,184 lines using vitest and fetch-mock to simulate the full device-code/poll/token exchange across OAuth App and GitHub App variants, including expiring-token and error-response paths, with @vitest/coverage-v8 wired into the test script. Naming is consistent and the module is small enough (five source files) that responsibilities stay clearly separated between state construction, the auth flow, and the request hook. A couple of narrow @ts-ignore/@ts-expect-error escapes exist where TypeScript’s overload resolution can’t reconcile the OAuth App/GitHub App union, called out explicitly in comments rather than silently suppressed.
API Design - The public surface is deliberately small: one factory function with two overloads (OAuth App vs GitHub App), returning an object with a callable auth() and an attached .hook(). Required options (clientId, onVerification) throw immediately with actionable error messages linking back to the README. The onVerification callback pattern cleanly separates the library’s polling mechanics from the app’s own UI for presenting the code to the user, which keeps integration code short in both CLI and browser contexts.