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):

Response Parameters (JSON):

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):

Response Parameters upon Error (JSON):

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):

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

}