Checklist
Describe the problem you'd like to have solved
ruby-auth0 has no support for the Device Authorization Flow. As of v6.1.0 there is no way to call POST /oauth/device/code, and no way to exchange a device code at POST /oauth/token with grant_type=urn:ietf:params:oauth:grant-type:device_code.
To be clear about a near-miss: Auth0::Api::V2::DeviceCredentials is the Management API device-credentials resource (listing and deleting device public keys). It is unrelated to the OAuth 2.0 Device Authorization Grant.
Auth0 documents this flow and ships it in at least one other SDK — Auth0.NET exposes StartDeviceFlowAsync(DeviceCodeRequest) returning DeviceCodeResponse, and GetTokenAsync(DeviceCodeTokenRequest), declared on IAuthenticationApiClient and covered by integration tests.
We run a small Rails service that acts as an OAuth proxy for our command-line client. It starts a Device Authorization Flow, hands user_code and verification_uri back to the CLI, then polls for the token. Because the flow is not reachable from this SDK, that service talks to /oauth/device/code and /oauth/token through a hand-written Faraday client instead — including re-implementing the four polling states (authorization_pending, slow_down, expired_token, access_denied).
We would rather depend on this SDK than keep maintaining that.
Describe the ideal solution
Two methods on Auth0::Api::AuthenticationEndpoints, named after the existing start_passwordless_sms_flow / exchange_sms_otp_for_tokens pair so they read like their neighbours:
# Start a Device Authorization flow.
# @see https://auth0.com/docs/api/authentication#device-authorization-flow
# @param scope [string] Space-separated list of requested scopes.
# @param audience [string] Unique identifier of the target API.
# @param client_id [string] Client ID for the application
# @return [json] Returns device_code, user_code, verification_uri,
# verification_uri_complete, expires_in and interval.
def start_device_flow(scope: nil, audience: nil, client_id: @client_id)
request_params = {
client_id: client_id,
scope: scope,
audience: audience
}
request_with_retry(:post, '/oauth/device/code', request_params)
end
# Get access and ID tokens using a device code.
# @see https://auth0.com/docs/api/authentication#device-authorization-flow
# @param device_code [string] The device code returned by start_device_flow.
# @param client_id [string] Client ID for the application
# @return [Auth0::AccessToken] Returns the access_token and id_token
def exchange_device_code_for_tokens(device_code, client_id: @client_id)
raise Auth0::InvalidParameter, 'Must provide a device code' if device_code.to_s.empty?
request_params = {
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
client_id: client_id,
device_code: device_code
}
::Auth0::AccessToken.from_response request_with_retry(:post, '/oauth/token', request_params)
end
Two things that keep this small:
- No new exception classes are required. While polling, Auth0 answers with an HTTP error whose body carries an
error code. Mixins::HTTPProxy#request already raises Auth0::HTTPError subclasses with the raw response body as the message, so a caller can distinguish authorization_pending from slow_down today. A documented accessor for that code would be a nice follow-up, but it is not a prerequisite.
- This does not touch the Fern-generated half. The v6 migration guide states that "This major version change does not affect the Authentication API", and
lib/auth0/api/ is listed in .fernignore, so these additions live in the hand-maintained module and should not be affected by regeneration.
Alternatives and current workarounds
There is no workaround within the SDK — the endpoint is simply not there. request_with_retry could be called directly, but it is not part of the documented public API, so we do not want to build on it.
Our current answer is the separate Faraday client described above.
Additional context
Happy to open a pull request implementing the above, following the shape and conventions in authentication_endpoints.rb, with unit tests alongside the existing ones in test/unit/authentication_endpoints_test.rb. It is essentially the code we already run in production, rewritten to match this codebase.
I am asking first rather than opening it directly because of how the last comparable contribution went: #305 asked for PKCE support, #308 implemented it and was reviewed positively, and it was closed in January 2022 in favour of "a more holistic approach to PKCE support across multiple endpoints" that does not appear to have landed. I would rather know whether targeted additions to Auth0::Api::AuthenticationEndpoints are welcome before writing the code.
(I have separately opened #791 for the PKCE half, which is a single optional keyword on an existing method.)
Environment: auth0 v5.20.0 and v6.1.0, Ruby 3.3 and 4.0.
Checklist
Describe the problem you'd like to have solved
ruby-auth0has no support for the Device Authorization Flow. As of v6.1.0 there is no way to callPOST /oauth/device/code, and no way to exchange a device code atPOST /oauth/tokenwithgrant_type=urn:ietf:params:oauth:grant-type:device_code.To be clear about a near-miss:
Auth0::Api::V2::DeviceCredentialsis the Management API device-credentials resource (listing and deleting device public keys). It is unrelated to the OAuth 2.0 Device Authorization Grant.Auth0 documents this flow and ships it in at least one other SDK — Auth0.NET exposes
StartDeviceFlowAsync(DeviceCodeRequest)returningDeviceCodeResponse, andGetTokenAsync(DeviceCodeTokenRequest), declared onIAuthenticationApiClientand covered by integration tests.We run a small Rails service that acts as an OAuth proxy for our command-line client. It starts a Device Authorization Flow, hands
user_codeandverification_uriback to the CLI, then polls for the token. Because the flow is not reachable from this SDK, that service talks to/oauth/device/codeand/oauth/tokenthrough a hand-written Faraday client instead — including re-implementing the four polling states (authorization_pending,slow_down,expired_token,access_denied).We would rather depend on this SDK than keep maintaining that.
Describe the ideal solution
Two methods on
Auth0::Api::AuthenticationEndpoints, named after the existingstart_passwordless_sms_flow/exchange_sms_otp_for_tokenspair so they read like their neighbours:Two things that keep this small:
errorcode.Mixins::HTTPProxy#requestalready raisesAuth0::HTTPErrorsubclasses with the raw response body as the message, so a caller can distinguishauthorization_pendingfromslow_downtoday. A documented accessor for that code would be a nice follow-up, but it is not a prerequisite.lib/auth0/api/is listed in.fernignore, so these additions live in the hand-maintained module and should not be affected by regeneration.Alternatives and current workarounds
There is no workaround within the SDK — the endpoint is simply not there.
request_with_retrycould be called directly, but it is not part of the documented public API, so we do not want to build on it.Our current answer is the separate Faraday client described above.
Additional context
Happy to open a pull request implementing the above, following the shape and conventions in
authentication_endpoints.rb, with unit tests alongside the existing ones intest/unit/authentication_endpoints_test.rb. It is essentially the code we already run in production, rewritten to match this codebase.I am asking first rather than opening it directly because of how the last comparable contribution went: #305 asked for PKCE support, #308 implemented it and was reviewed positively, and it was closed in January 2022 in favour of "a more holistic approach to PKCE support across multiple endpoints" that does not appear to have landed. I would rather know whether targeted additions to
Auth0::Api::AuthenticationEndpointsare welcome before writing the code.(I have separately opened #791 for the PKCE half, which is a single optional keyword on an existing method.)
Environment:
auth0v5.20.0 and v6.1.0, Ruby 3.3 and 4.0.