Device Flow
OAuth 2.0 Device Authorization Grant flow (RFC 8628) (sometimes shortened to Device Authorization Flow or Device Flow) decouples user-agent authorization (i.e., user logon) from fetching tokens. Device Flow can be used by input-constrained devices such as a smart TVs, IoT devices, and printers. The Device Flow contains two different paths; one occurs on the device requesting authorization, and the other occurs in a web browser. The browser flow path occurs in parallel to part of the device flow path.
Example Device Flow Script
A sample device client script is available at https://cilogon.org/deviceflow.sh . Simply enter your client_id and client_secret at the top of the script and run it as "sh deviceflow.sh". (This assumes bash is set as your shell.) There are a few program prerequisites (e.g., curl, jq, and optionally qrencode). The script checks for the presence of these programs and notifies you if they are not installed.
Example SSH Device Flow Module
The https://github.com/stfc/pam_oauth2_device module is known to work well for CILogon device flow authentication to SSH.
Device Authorization Request
A client on the device first contacts the device_authorization server with client_id, client_secret (for Confidential Clients), and (optionally) scope. The device authorization server responds with device_code (used when contacting the token endpoint), user_code, and verification_uri.
Device Authorization Endpoint: https://cilogon.org/oauth2/device_authorization
Request Parameters (POST):
client_id - The CILogon OAuth2/OIDC client identifier.
client_secret - For Confidential Clients, the secret associated with the client_id. For Public Clients, this value is omitted.
scope - (Optional) A space-separated list of scopes to request for this transaction. Public Clients can request only the "openid" scope. If this parameter is omitted, then the registered scopes for the client are assumed.
Response Parameters (JSON):
device_code - A device code to be used when contacting the token endpoint (below).
user_code - A code to be entered by the user in a web browser.
expires_in - A time in seconds indicating how long the user has to enter the user code.
verification_uri - A URL where the user can enter the user code.
verification_uri_complete - A URL that contains the user code so the user does not have to type it in.
interval - A time in seconds that the device client must wait between queries to the token endpoint.
Example:
export CLIENT_ID="cilogon:/client_id/6e8fdae3459dac6c685c6b6de37c188c"
export CLIENT_SECRET="euWajTysidMofassawoigDiweoj1olwa"
curl --data-urlencode "client_id=$CLIENT_ID" \
--data-urlencode "client_secret=$CLIENT_SECRET" \
--data-urlencode "scope=openid email profile" \
https://cilogon.org/oauth2/device_authorization
{
"device_code": "NB2HI4DTHIXS6Y3JNRXWO33OFZXXEZZPN5QXK5DIGIXTGNJYMIZWCYZSGA2WMMJTHEZGENTDG42DSZJSH52HS4DFHVQXK5DIPJDXEYLOOQTHI4Z5GE3DENZTGE2DCMRRHAYDAJTWMVZHG2LPNY6XMMROGATGY2LGMV2GS3LFHU4TAMBQGAYA",
"user_code": "PLK-NGF-V77",
"expires_in": 900,
"interval": 5,
"verification_uri": "https://cilogon.org/device/",
"verification_uri_complete": "https://cilogon.org/device/?user_code=PLK-NGF-V77"
}
User Authentication
After the device client receives a response from the device_authorization endpoint, it displays the user_code and either verification_uri or verification_uri_complete (preferred) to the user, and prompts the user to open a web browser on another device in order to log on. If the verification_uri_complete URL is used, the user_code is entered automatically, bypassing the first screen shown below.
Token Request
After the user_code and verification_uri have been shown to the user, the device client polls the token endpoint at a regular interval until one of the following occurs: the user logs in, the user denies the transaction, the device code expires, or some other error is returned.
Token Endpoint: https://cilogon.org/oauth2/token
Request Parameters (POST):
client_id - The CILogon OAuth2/OIDC client identifier.
client_secret - For Confidential Clients, the secret associated with the client_id. For Public Clients, this value is omitted.
device_code - The device_code returned by the device_authorization endpoint (above).
grant_type - This value must be "urn:ietf:params:oauth:grant-type:device_code".
Response Parameters upon Error (JSON):
error - Typically either authorization_pending or slow_down. These values indicate that the device client should continue to poll the token endpoint. slow_down is a special case of authorization_pending indicating that the client should slow the polling interval by 5 seconds. Other errors indicate that the device client should stop polling, due to expired user_code, user denying the transaction, or some other service error.
error_description - A human-readable version of the error.
Example:
export CLIENT_ID="cilogon:/client_id/6e8fdae3459dac6c685c6b6de37c188c"
export CLIENT_SECRET="euWajTysidMofassawoigDiweoj1olwa"
export \
DEVICE_CODE="NB2HI4DTHIXS6Y3JNRXWO33OFZXXEZZPN5QXK5DIGIXTGNJYMIZWCYZSGA2WMMJTHEZGENTDG42DSZJSH52HS4DFHVQXK5DIPJDXEYLOOQTHI4Z5GE3DENZTGE2DCMRRHAYDAJTWMVZHG2LPNY6XMMROGATGY2LGMV2GS3LFHU4TAMBQGAYA"
curl --data-urlencode "client_id=$CLIENT_ID" \
--data-urlencode "client_secret=$CLIENT_SECRET" \
--data-urlencode "device_code=$DEVICE_CODE" \
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
https://cilogon.org/oauth2/token
{
"error":"authorization_pending",
"error_description":"authorization pending"
}
Response Parameters upon Success (JSON):
access_token - An access token which can be used when contacting the userinfo endpoint.
id_token - An id_token which contains user attributes.
refresh_token - For clients with refresh tokens enabled, a refresh token which can be used to obtain a new access token.
token_type - This value is typically "Bearer".
expires_in - A time in seconds indicating how long the token is valid.
Example:
export CLIENT_ID="cilogon:/client_id/6e8fdae3459dac6c685c6b6de37c188c"
export CLIENT_SECRET="euWajTysidMofassawoigDiweoj1olwa"
export \
DEVICE_CODE="NB2HI4DTHIXS6Y3JNRXWO33OFZXXEZZPN5QXK5DIGIXTGNJYMIZWCYZSGA2WMMJTHEZGENTDG42DSZJSH52HS4DFHVQXK5DIPJDXEYLOOQTHI4Z5GE3DENZTGE2DCMRRHAYDAJTWMVZHG2LPNY6XMMROGATGY2LGMV2GS3LFHU4TAMBQGAYA"
curl --data-urlencode "client_id=$CLIENT_ID" \
--data-urlencode "client_secret=$CLIENT_SECRET" \
--data-urlencode "device_code=$DEVICE_CODE" \
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
https://cilogon.org/oauth2/token
{
"access_token":"NB2HI4DTHIXS6ZDF...",
"refresh_token":"NB2HI4DTHIXS6ZDF...",
"id_token":"eyJ0eXAiOiJKV1Q...",
"token_type":"Bearer",
"expires_in":900
}
The id_token is a JWT (JSON Web Token) which can be decoded by standard JWT libraries, the jq program, or online at https://jwt.io .
Example:
echo "eyJ0eXAiOiJKV1Q..." | jq -R 'split(".") | .[1] | @base64d | fromjson'
{
"email": "johndoe@gmail.com",
"given_name": "John",
"family_name": "Doe",
"name": "John Doe",
"iss": "https://cilogon.org",
"sub": "http://cilogon.org/serverA/users/12345",
"aud": "cilogon:/client_id/6e8fdae3459dac6c685c6b6de37c188c",
"token_id": "https://cilogon.org/oauth2/idToken/4ebf936e829678468e/162731549",
"auth_time": 1627315490,
"exp": 1627316391,
"iat": 1627315491
}