/token
Exchange an authorization code, device code, refresh bundle, or client credentials for tokens.
Endpoint
- Method:
POST - URL:
https://sso.user.m7.org/token - Content type:
application/x-www-form-urlencoded - Response: JSON
Supported grant types currently advertised by discovery:
authorization_coderefresh_tokenclient_credentialsurn:ietf:params:oauth:grant-type:device_code
The current public examples below use device_code in the request body for device-code pickup.
Common response fields
Token responses vary by grant type, but current successful responses may include:
token_typeaccess_tokenexpires_inrefresh_tokenrefresh_expires_inbinding_chainbinding_linkscopeid_token
When openid is in scope on supported end-user flows, the response may include id_token.
authorization_code
Use this after the browser returns code to your redirect_uri.
Required form fields:
grant_type=authorization_codecoderedirect_uricode_verifier
Client authentication:
- public clients typically send
client_id - confidential clients use their configured token endpoint auth method
Optional:
fingerprintDPoPheader when your client uses DPoP
Example:
curl -sS https://sso.user.m7.org/token \
-d grant_type='authorization_code' \
-d code='AUTHORIZATION_CODE' \
-d redirect_uri='https://app.example.com/callback' \
-d code_verifier='PKCE_CODE_VERIFIER' \
-d client_id='CLIENT_ID'
refresh_token
Use this to rotate a refresh bundle and obtain a new access token.
Current M7 request shape:
grant_type=refresh_tokentoken=<REFRESH_TOKEN>binding_chainbinding_link
Client authentication:
- required or omitted according to the client's configured token endpoint auth method
Optional:
fingerprintnew_fingerprint
Example:
curl -sS https://sso.user.m7.org/token \
-d grant_type='refresh_token' \
-d token='REFRESH_TOKEN' \
-d binding_chain='BINDING_CHAIN' \
-d binding_link='BINDING_LINK' \
-d client_id='CLIENT_ID' \
-d client_secret='CLIENT_SECRET'
Important note:
- The current refresh request uses
token, notrefresh_token, in the form body.
client_credentials
Use this for machine-to-machine access.
Required form fields:
grant_type=client_credentialsaud
Optional:
scopeaccess_expiresclaimsfingerprintlabel
Client authentication:
- confidential clients use
client_secret_basicorclient_secret_post - public clients are not the normal shape for this grant
Example:
curl -sS https://sso.user.m7.org/token \
-u 'CLIENT_ID:CLIENT_SECRET' \
-d grant_type='client_credentials' \
-d aud='https://user.m7.org/api/v2/' \
-d scope='file.read file.write'
Expected response shape:
{
"token_type": "bearer",
"access_token": "ACCESS_TOKEN",
"expires_in": 900,
"scope": "file.read file.write"
}
device_code
Use this after the user approves a device authorization on /device_login.
Required form fields:
grant_type=device_codedevice_code
Client authentication:
- confidential clients use their configured token auth method
- public clients send
client_id
Example:
curl -sS https://sso.user.m7.org/token \
-d grant_type='device_code' \
-d device_code='DEVICE_CODE' \
-d client_id='CLIENT_ID'
Typical success response:
{
"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 offline_access"
}
Common errors
invalid_requestinvalid_clientunauthorized_clientinvalid_grantunsupported_grant_typeauthorization_pending
Notes
client_credentialsreturns access-token material only.- Device-code pickup is one-time. After a successful pickup, the same
device_codeshould not be reused. - Refresh-token rotation should replace the previously stored refresh bundle with the newly returned bundle.
- When
binding_chainandbinding_linkare returned, store them with the refresh token. They are part of the current refresh request contract.