Quickly add authentication to your command-line application.
Add CLI authentication so users can sign in to your command-line application via AuthKit.
To begin the authentication flow, make a request to the /authorize/device endpoint to obtain the necessary codes and URLs for user authentication.
const response = await fetch( 'https://api.workos.com/user_management/authorize/device', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ client_id: 'client_123456789', }), }, ); const data = await response.json();
After receiving a response, provide next steps to the user.

Display the user_code from the response, along with the verification_uri in the terminal. If offering the ability to open in a browser easily as shown in this screenshot, use the verification_uri_complete for that.
Never display the device_code to the user. That is only for the device to poll the token endpoint.
If the user navigates to the verification_uri, a form appears to enter the code manually. If the user is not logged in, they are prompted to do so first and then returned to the code entry screen.

If the user goes to the verification_uri_complete (for example, https://<authkit_domain>/device?user_code=ABCD-EFGH), they instead confirm that the code matches what is displayed in the terminal.

While the user is completing authentication in their browser, poll the token endpoint to check for authorization completion.
Make requests to the token endpoint using device_code from the authorization response in step 1:
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); async function pollForTokens({ clientId, deviceCode, expiresIn = 300, interval = 5, }) { const timeout = AbortSignal.timeout(expiresIn * 1000); while (true) { const response = await (async () => { try { return await fetch( 'https://api.workos.com/user_management/authenticate', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ grant_type: 'urn:ietf:params:oauth:grant-type:device_code', device_code: deviceCode, client_id: clientId, }), signal: timeout, }, ); } catch (error) { if (error.name === 'TimeoutError') { throw new Error('Authorization timed out'); } throw error; } })(); const data = await response.json(); if (response.ok) { // Success! Return tokens for further processing. return data; } switch (data.error) { case 'authorization_pending': // Wait before polling again await sleep(interval * 1000); break; case 'slow_down': // You shouldn't see this if you're only polling every 5 seconds. // In this example, we're increasing the interval by 1 second when this happens. interval += 1; await sleep(interval * 1000); break; // Terminal cases case 'access_denied': case 'expired_token': throw new Error('Authorization failed'); default: throw new Error('Authorization failed'); } } }
slow_down errors by increasing your polling intervalaccess_denied or expired_token errorsCLI Auth is available for Connect applications, allowing you and third-party developers to build CLI tools that integrate with your app’s credentials.
The flow is the same but uses Connect endpoints:
https://<authkit_domain>/oauth2/device_authorizationhttps://<authkit_domain>/oauth2/tokenSince command-line applications are distributed to end users, avoid embedding the client secret in the app. Instead, set up your Connect app as a Public application.
Third-party Connect applications will require users to grant consent to the third-party app.