Device Code Token Pickup
This document describes what another site / RP sends to SSO after a device authorization has already been approved.
Purpose
The RP already has:
device_code- its own OAuth client credentials or public
client_id
The user has already approved the request at:
https://sso.user.m7.org/device_login
At that point, the RP redeems the device_code at SSO's token endpoint.
Endpoint
POST https://sso.user.m7.org/token
Content type:
application/x-www-form-urlencoded
Required form fields
Always send:
grant_type=device_codedevice_code=<DEVICE_CODE>
Client authentication
The RP must authenticate the OAuth client the same way it does for normal /token calls.
Confidential client
Send one of:
Authorization: Basic base64(client_id:client_secret)client_id=<CLIENT_ID>andclient_secret=<CLIENT_SECRET>
Use whichever token_endpoint_auth_method is configured for that OAuth client.
Public client
If the OAuth client is configured with:
token_endpoint_auth_method = none
then send:
client_id=<CLIENT_ID>
and do not send a client secret.
What not to send
Do not send:
user_code- username / password
- browser cookies
sso_session_idsession_key
Those are not part of the pickup request.
Where this should be called from
Confidential client
Call /token from the RP backend, not browser JavaScript.
Reason:
- the browser must not hold the client secret
Public client
A CLI or other public device client may call /token directly if it is registered as a public client.
Example request
client_secret_post
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=device_code&device_code=<DEVICE_CODE>&client_id=<CLIENT_ID>&client_secret=<CLIENT_SECRET>
client_secret_basic
POST /token
Authorization: Basic <BASE64_CLIENT_ID_COLON_SECRET>
Content-Type: application/x-www-form-urlencoded
grant_type=device_code&device_code=<DEVICE_CODE>
Public client
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=device_code&device_code=<DEVICE_CODE>&client_id=<CLIENT_ID>
Example curl
curl -sS https://sso.user.m7.org/token \
-d grant_type=device_code \
-d device_code='<DEVICE_CODE>' \
-d client_id='<CLIENT_ID>' \
-d client_secret='<CLIENT_SECRET>'
Successful response
On success, SSO returns the pre-issued token package for that device authorization.
Current shape:
token_typeaccess_tokenexpires_inrefresh_tokenrefresh_expires_inbinding_chainbinding_linkscope
Example:
{
"token_type": "bearer",
"access_token": "<ACCESS_TOKEN>",
"expires_in": 900,
"refresh_token": "<REFRESH_TOKEN>",
"refresh_expires_in": 2592000,
"binding_chain": "<BINDING_CHAIN>",
"binding_link": "<BINDING_LINK>",
"scope": "openid profile email groups offline_access"
}
One-time pickup behavior
device_code pickup is one-time.
After successful /token redemption:
- the device authorization is consumed
- the same
device_codeshould not be redeemed again
Common failure cases
User has not approved yet
Expected error:
authorization_pending
Wrong client tries to redeem
Expected error:
invalid_grant
Expired or already-consumed device code
Expected error:
invalid_grant
Client is not allowed to use device flow
Expected error:
unauthorized_client
After pickup
After the RP receives the token package, it should use the returned refresh material for normal refresh flow.
That means later refresh requests go back through the normal /token refresh path using:
refresh_tokenbinding_chainbinding_link
and any other normal refresh requirements that apply to that client.