Articles
OAuth Device Authorization Interview Questions: Polling, Expiration, and Phishing Risks
Practice oauth device authorization interview questions with worked failure traces, recovery choices, and verification tests grounded in official documentation.
An OAuth device authorization interview answer should describe two devices, two different codes, a deadline-bound polling loop, and an approval decision that can still be socially engineered. A legitimate identity-provider page does not prove the user is authorizing the device they intended. Correct endpoint handling and correct user intent are different security boundaries.
This article uses RFC 8628 and official provider documentation. The polling timeline, cancellation tests, and threat review are original interview exercises, not an employer assessment or a live authentication session. Practice Build an OAuth-based data migration client with device authorization added as a constrained-client scenario.
Why use device authorization instead of an authorization-code redirect?
Device authorization supports clients that cannot conveniently perform browser-based interaction or receive the usual redirect. A television or constrained-input device starts an authorization attempt, while a user completes interaction through a separate browser-capable device. The client then polls for the result. The normative protocol is RFC 8628.
Do not choose it merely because polling looks easier to implement. A browser-capable application may have a more suitable redirect-based flow. Ask about input capability, browser availability, callback support, client registration, and provider policy. Device authorization is not a universal replacement for authorization code with PKCE.
The useful interview comparison is where the user's browser interacts and how the client learns the outcome. In this flow, the constrained client does not need an incoming browser redirect. It learns through token-endpoint responses associated with its own authorization attempt. That changes UI, timeout, and retry ownership.
Name the application boundary too. Receiving an access token does not automatically define the application's local account, session lifetime, allowed API actions, or logout behavior. You still need a documented session and permission model. OAuth grants access; any identity features and account-linking decisions depend on the provider and protocol configuration you actually use.
Which code is displayed, and which code is polled?
The device-authorization response includes a device code for the client and a user code for the browser interaction, along with a verification URI, expiry, and polling information. Treat the device code as sensitive attempt material; do not display it as the short code for the user or place it in analytics logs.
The human-facing code is not the eventual access token and should not be treated as a password that permanently signs the user in. Its purpose is associating the browser approval with an outstanding device attempt. The user-facing verification instructions should make the requested action understandable before approval.
Microsoft's device-flow documentation shows its endpoint exchange and response fields. GitHub's OAuth-app documentation provides another implementation, including provider-specific registration and error handling. Use those as concrete integrations, not interchangeable endpoint templates.
Keep a stable local attempt identifier separate from both codes. It helps suppress stale responses after a restart or cancellation without exposing sensitive protocol material. A fresh attempt must not silently reuse an old attempt's deadline or device code. If two TVs are being configured, their authorization state must remain separate even for the same account.
What is the exact polling contract?
RFC 8628 requires waiting at least the supplied interval, or five seconds when it is absent. authorization_pending continues polling. slow_down permanently adds five seconds to the interval for subsequent requests in that attempt. Other error responses stop polling; denial and expiry are terminal outcomes. Connection timeouts require a reduction in polling frequency, with exponential backoff recommended. See RFC 8628's token-response rules.
That contract is not “retry any non-200 response every five seconds.” Parse the protocol response, distinguish transport failure from a returned OAuth error, and respect provider-specific requirements. An HTTP library's generic retry policy can break the flow by immediately issuing another token request or creating concurrent attempts.
Serialize polls. If a request takes longer than expected, an interval timer should not launch another request while it remains active. Use a single owner for the attempt, with cancellation and an expiry budget. Jitter or additional backoff can make requests later; they must not make the client poll earlier than permitted.
Use a monotonic clock for elapsed deadlines where available. A wall-clock change should not unexpectedly extend a local attempt indefinitely. Retain the server's expiry value as the protocol limit and choose a conservative local deadline policy. Network delay makes exact remaining lifetime uncertain; it does not justify polling forever until a server happens to return expiry.
Exercise: predict the next legal poll
Here is an original zero-response-latency fixture. The device response arrives at time zero, specifies a five-second interval, and has a 40-second local expiry budget. There is one outstanding request at most. Each wait starts after the preceding response.
| Response received at | Response | Earliest next request in this fixture | Why |
|---|---|---|---|
| 5 seconds | authorization_pending | 10 seconds | Interval remains five seconds |
| 10 seconds | slow_down | 20 seconds | Interval increases to ten seconds |
| 20 seconds | authorization_pending | 30 seconds | The increased interval persists |
| 30 seconds | slow_down | None before expiry | Next interval is fifteen seconds; deadline is 40 |
The client stops scheduling rather than issuing a request at 45 seconds. A pending response does not reset the original deadline. The timeline is a teaching fixture; actual response duration and provider behavior need to be included in implementation tests.
Now change the response at 20 seconds to a connection timeout. Your policy must reduce polling frequency. Doubling the current interval to 20 seconds is an example of the RFC's recommended backoff approach, not the sole valid implementation. With this exercise's deadline, waiting those 20 seconds reaches expiry, so another poll is not scheduled.
Change it again to access_denied. Stop immediately and show a useful terminal state. Retrying denial until the user changes their mind disregards the protocol and creates confusing behavior. A new authorization attempt should follow explicit user intent, not an invisible loop producing fresh codes.
How should cancellation and late responses behave?
Give the attempt a state machine: pending, waiting, polling, completed, denied, expired, canceled, or failed. Define terminal transitions so that a late network response cannot accidentally reopen a canceled attempt. Those names are an original implementation model, not fields mandated by the RFC.
Imagine the user cancels while a token request is in flight. The network operation may be canceled locally, but the server could already have processed it. When a late success arrives, compare its attempt identity and the current terminal state before persisting a token or creating an application session. If it is discarded, ensure discarded token material is not leaked into logs or another active attempt.
Starting over should create a new attempt generation and cancel ownership of the old one. A stale denial must not close the new screen; a stale success must not sign the user into the wrong local session. Test both reorderings with a controllable clock and fake token endpoint rather than relying on a slow real server.
Do not claim client cancellation automatically revokes an issued token or withdraws server-side consent. Provider APIs and policies determine those operations. Local UI cleanup, authorization-session termination, token revocation, and application logout are separate actions. Be explicit about which are available and which require further integration.
How can phishing work on the real provider's page?
The attacker can initiate an authorization attempt for an attacker-controlled client/device and persuade a victim to approve its user code on the legitimate provider. The victim's browser may use the correct HTTPS origin throughout. The failure is approving the wrong transaction, not necessarily submitting credentials to a fake website. RFC 8628 discusses this remote-phishing risk in its security considerations.
An original defensive review asks what the approval screen communicates: application identity, requested permissions, and a clear warning to approve only an action the user initiated on a device they control. A code comparison and recognizable device context can help the user notice a mismatch. Neither makes a live social-engineering attempt impossible.
Rate limits and sufficient code entropy address guessing and abuse pressure; they do not establish that the approving user understood the requesting device. Short expiry reduces the useful window without preventing an attacker from relaying a fresh code in real time. Avoid advertising any one control as “phishing-proof device flow.”
At an organization boundary, consider whether device authorization is required at all. Microsoft documents Conditional Access controls for authentication flows, including restricting device-code flow. That is provider-specific policy capability, not a universal RFC feature. A narrower enabled population can reduce exposure but needs a rollout plan for legitimate constrained-device use.
What should you store and log?
Store only what the active attempt needs, under an appropriately protected local boundary. Avoid embedding a reusable client secret in a distributable device application and calling it confidential solely because the variable is named secret. The client's capabilities and provider registration determine the security model.
Log a non-sensitive attempt identifier, state transitions, elapsed time, interval changes, and sanitized error category. Do not log access tokens, refresh tokens, device codes, or full token responses. A debugging session can become a credential disclosure if engineers paste those payloads into a ticket. Make redaction part of the client wrapper, not a reminder after an incident.
If authorization succeeds, store tokens according to the platform and provider's supported security guidance and application threat model. Limit scopes and avoid unnecessary persistence. Plan token expiry, refresh, revocation, device reset, and account unlinking as lifecycle concerns beyond the polling loop.
Provider constraints also matter. Google's limited-input-device documentation describes supported clients, scopes, and exchange behavior. Do not promise any arbitrary scope or provider supports device authorization because RFC 8628 exists. Check the specific application's registration and current policy before implementing the flow.
Build tests around time and intent boundaries
Use a fake clock to assert no request occurs before the allowed interval, every slow-down increase persists, timeouts back off, and expiry terminates scheduling. Test an omitted interval separately from an explicit valid interval. Add nonzero response duration and verify that a timer cannot overlap two requests.
For terminal behavior, inject denied, expired, unrecognized OAuth error, canceled-in-flight success, and late response from a replaced generation. A successful test asserts both the visible state and absence of unintended follow-up requests or token persistence. Network success is not the only observable behavior.
For user intent, review the setup UI on the actual constrained device and the separate approval screen. Can the user tell which device they are authorizing? Is the displayed code readable without exposing other secrets? Does a generic “approve to fix your account” message undermine the warning? Evaluate those interactions without conducting phishing against real users.
Finish with an integration test in the provider's approved test environment. Confirm registration, permitted scopes, terminal errors, and token lifecycle. Unit tests demonstrate your client policy; they do not verify a provider's current production configuration. Document which boundary each test covers and what remains an integration assumption.