new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Better Auth · Plugins · all subjects

device-authorization/flow

6 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Device Authorization flow steps

The device flow follows these steps: 1) Device requests a device code and user code from the authorization server. 2) User visits a verification URL and enters the user code. 3) Device polls the server until the user completes authorization. 4) Once authorized, the device receives an access token.

Device Authorization error codes

Error codes returned by device flow: authorization_pending (user hasn't approved yet, continue polling), slow_down (polling too frequently, increase interval), expired_token (device code has expired), access_denied (user denied the authorization), invalid_grant (invalid device code or client ID).

Device Authorization polling example - handling all error cases

Complete polling implementation with error handling: ```ts let pollingInterval = 5; // Start with 5 seconds const pollForToken = async () => { const { data, error } = await authClient.device.token({ grant_type: "urn:ietf:params:oauth:grant-type:device_code", device_code, client_id: yourClientId, fetchOptions: { headers: { "user-agent": `My CLI`, }, }, }); if (data?.access_token) { console.log("Authorization successful!"); } else if (error) { switch (error.error) { case "authorization_pending": // Continue polling break; case "slow_down": pollingInterval += 5; break; case "access_denied": console.error("Access was denied by the user"); return; case "expired_token": console.error("The device code has expired. Please try again."); return; default: console.error(`Error: ${error.error_description}`); return; } setTimeout(pollForToken, pollingInterval * 1000); } }; pollForToken(); ```

Device Authorization user code entry form

React component example for entering device code: ```tsx export default function DeviceAuthorizationPage() { const { data: session } = authClient.useSession(); const searchParams = useSearchParams(); const [userCode, setUserCode] = useState(searchParams.get("user_code") || ""); const [error, setError] = useState(null); const handleSubmit = async (e) => { e.preventDefault(); try { // Format the code: remove dashes and convert to uppercase const formattedCode = userCode.trim().replace(/-/g, "").toUpperCase(); const approvalPath = `/device/approve?user_code=${encodeURIComponent(formattedCode)}`; if (!session?.user) { const verificationPath = `/device?user_code=${encodeURIComponent(formattedCode)}`; window.location.href = `/login?redirect=${encodeURIComponent(verificationPath)}`; return; } // Check if the code is valid using GET /device endpoint const response = await authClient.device({ query: { user_code: formattedCode }, }); if (response.data) { // Redirect to approval page window.location.href = approvalPath; } } catch (err) { setError("Invalid or expired code"); } }; return ( <form onSubmit={handleSubmit}> <input type="text" value={userCode} onChange={(e) => setUserCode(e.target.value)} placeholder="Enter device code (e.g., ABCD-1234)" maxLength={12} /> <button type="submit">Continue</button> {error && <p>{error}</p>} </form> ); } ```

Device Authorization approval page example

React component for device approval/denial: ```tsx export default function DeviceApprovalPage() { const { user } = useAuth(); // Must be authenticated const searchParams = useSearchParams(); const userCode = searchParams.get("user_code"); const [isProcessing, setIsProcessing] = useState(false); const handleApprove = async () => { setIsProcessing(true); try { await authClient.device.approve({ userCode: userCode, }); // Show success message alert("Device approved successfully!"); window.location.href = "/"; } catch (error) { alert("Failed to approve device"); } setIsProcessing(false); }; const handleDeny = async () => { setIsProcessing(true); try { await authClient.device.deny({ userCode: userCode, }); alert("Device denied"); window.location.href = "/"; } catch (error) { alert("Failed to deny device"); } setIsProcessing(false); }; if (!user) { // Redirect to login if not authenticated const verificationPath = `/device?user_code=${encodeURIComponent(userCode || "")}`; window.location.href = `/login?redirect=${encodeURIComponent(verificationPath)}`; return null; } return ( <div> <h2>Device Authorization Request</h2> <p>A device is requesting access to your account.</p> <p>Code: {userCode}</p> <button onClick={handleApprove} disabled={isProcessing}> Approve </button> <button onClick={handleDeny} disabled={isProcessing}> Deny </button> </div> ); } ```

Device Authorization CLI example - complete authentication flow

Complete CLI application example using device authorization: ```ts import { createAuthClient } from "better-auth/client"; import { deviceAuthorizationClient } from "better-auth/client/plugins"; import open from "open"; const authClient = createAuthClient({ baseURL: "http://localhost:3000", plugins: [deviceAuthorizationClient()], }); async function authenticateCLI() { console.log("🔐 Better Auth Device Authorization Demo"); console.log("⏳ Requesting device authorization..."); try { // Request device code const { data, error } = await authClient.device.code({ client_id: "demo-cli", scope: "openid profile email", }); if (error || !data) { console.error("❌ Error:", error?.error_description); process.exit(1); } const { device_code, user_code, verification_uri, verification_uri_complete, interval = 5, } = data; console.log("\n📱 Device Authorization in Progress"); console.log(`Please visit: ${verification_uri}`); console.log(`Enter code: ${user_code}\n`); // Open browser to verification page const urlToOpen = verification_uri_complete || verification_uri; console.log("🌐 Opening browser..."); await open(urlToOpen); console.log(`⏳ Waiting for authorization... (polling every ${interval}s)`); // Poll for token await pollForToken(device_code, interval); } catch (err) { console.error("❌ Error:", err.message); process.exit(1); } } async function pollForToken(deviceCode: string, interval: number) { let pollingInterval = interval; return new Promise<void>((resolve) => { const poll = async () => { try { const { data, error } = await authClient.device.token({ grant_type: "urn:ietf:params:oauth:grant-type:device_code", device_code: deviceCode, client_id: "demo-cli", }); if (data?.access_token) { console.log("\nAuthorization Successful!"); console.log("Access token received!"); // Get user session const { data: session } = await authClient.getSession({ fetchOptions: { headers: { Authorization: `Bearer ${data.access_token}`, }, }, }); console.log(`Hello, ${session?.user?.name || "User"}!`); resolve(); process.exit(0); } else if (error) { switch (error.error) { case "authorization_pending": // Continue polling silently break; case "slow_down": pollingInterval += 5; console.log(`⚠️ Slowing down polling to ${pollingInterval}s`); break; case "access_denied": console.error("❌ Access was denied by the user"); process.exit(1); break; case "expired_token": console.error("❌ The device code has expired. Please try again."); process.exit(1); break; default: console.error("❌ Error:", error.error_description); process.exit(1); } } } catch (err) { console.error("❌ Network error:", err.message); process.exit(1); } // Schedule next poll setTimeout(poll, pollingInterval * 1000); }; // Start polling setTimeout(poll, pollingInterval * 1000); }); } // Run the authentication flow authenticateCLI().catch((err) => { console.error("❌ Fatal error:", err); process.exit(1); }); ```

Give your agent this brain