Device Authorization Grant support
See original GitHub issueFeature Request
1. Motivation
AppAuth-Android does not currently support authentication for Android devices that either lack a browser or have limited input capabilities to fully perform the traditional authentication flow. Adding support of the extension Device Authorization Grant as described in RFC 8628 would allow such devices to obtain tokens from the authorization server with the help of a secondary device with browser and common input capabilities.
2. Description
As described in Section 1 (RFC 8628), such an implementation would require the addition of a request to perform a device authorization and a polling mechanism for the token request.
2.1 Device authorization
A request such as DeviceAuthorizationRequest
could be created with the specifications in Section 3.1 (RFC 8628) and the associated response such as DeviceAuthorizationResponse
with the specifications in Section 3.2 (RFC 8628).
To perform this request, an additional endpoint device_authorization_endpoint
described in Section 4 (RFC 8628) would have to be added to the AuthorizationServiceDiscovery
.
The request would be performed in an AsyncTask
such as DeviceAuthorizationRequestTask
and would expose the reponse through a callback such as DeviceAuthorizationResponseCallback
.
public interface DeviceAuthorizationResponseCallback {
void onDeviceAuthorizationRequestCompleted(@Nullable DeviceAuthorizationResponse response,
@Nullable AuthorizationException ex);
}
No additional exceptions should be created for this request as the authorization server should respond in the same way as the token endpoint as described in the last paragraph of Section 3.2 (RFC 8628). The choice of an AsyncTask
here would be to match the existing implementation of other requests.
To save the new authorization state, AuthState
should implement an additional update
method to save the last DeviceAuthorizationResponse
. The last response will be used to expose parameters such as verification uri, complete verification uri and user code through get accessors as these will be required to perform the authorization request on the secondary device.
The DeviceAuthorizationReponse
would also allow for the creation of a TokenRequest
in a similar manner that the AuthorizationReponse
already does through a member function such as:
public class DeviceAuthorizationReponse {
public final DeviceAuthorizationRequest request;
public final String deviceCode;
public TokenRequest createTokenExchangeRequest() {
if (deviceCode == null) {
throw new IllegalStateException("deviceCode not available for exchange request");
}
return new TokenRequest.Builder(request.configuration, request.clientId)
.setGrantType(GrantTypeValues.DEVICE_CODE)
.setDeviceCode(deviceCode)
.build();
}
}
An additional method should also be provided to allow for the creation of a TokenRequest
with non-documented additional parameters.
The TokenRequest
would implement the additional parameter deviceCode
to allow operations through the extension’s grant type urn:ietf:params:oauth:grant-type:device_code
described in the Section 3.4 (RFC 8628).
2.2 Polling mechanism
Right after the application obtained and displayed the information necessary to perform the authorization on a secondary device, it should start polling for an access token associated to the previously obtained deviceCode
in the DeviceAuthorizationReponse
with the provided TokenRequest
.
It would be handy if the AuthorizationService
offered an API to perform the polling itself.
class AuthorizationService {
public void performTokenPollRequest(
@NonNull TokenRequest request,
@Nullable Long pollingInterval,
@NonNull Long expirationTime,
@NonNull TokenResponseCallback callback)
}
This API would instanciate and execute an AsyncTask
such as TokenRequestPollingTask
that would inherit from the existing TokenRequestTask
. This task would perform the polling loop in its doInBackground
method while calling the super class’ doInBackground
to execute the provided TokenRequest
. The result of this request would be parsed in the loop to interpret the errors described in Section 3.5 (RFC 8628) and decide whether to continue the polling, expand the polling interval, abort due to a critical error or due to the expiration of the device code. A set of additional AuthorizationException
described in the extension should also be implemented.
The issue with this approach is the need to have access to the provided polling interval and device code expiration time that are not stored in the TokenRequest
. A solution to this would be to provide a helper method in AuthState
that would call the AuthorizationService
polling method with the parameters provided by the last DeviceAuthorizationReponse
.
public class AuthState {
private DeviceAuthorizationResponse mLastDeviceAuthorizationResponse;
public void performTokenPollRequest(@NonNull final AuthorizationService service,
@NonNull final ClientAuthentication clientAuth,
@NonNull final AuthorizationService.TokenResponseCallback callback) {
if (mLastDeviceAuthorizationResponse == null) {
AuthorizationException ex = AuthorizationException.fromTemplate(
AuthorizationException.TokenRequestErrors.CLIENT_ERROR,
new IllegalStateException("No device authorization available for token request"));
callback.onTokenRequestCompleted(null, ex);
return;
}
service.performTokenPollRequest(mLastDeviceAuthorizationResponse.createTokenExchangeRequest(),
clientAuth,
mLastDeviceAuthorizationResponse.tokenPollingIntervalTime,
mLastDeviceAuthorizationResponse.codeExpirationTime,
callback);
}
}
Additional methods should also be provided to allow for the creation of the TokenRequest
with non-documented additional parameters and to allow the polling without explicitly providing a ClientAuthentication
.
A thead-safe method for canceling the polling request should also be provided.
Upon a successful TokenRequest
, the app should update the AuthState
with the provided TokenResponse
in onTokenRequestCompleted
.
3. Alternatives or Workarounds
There is currently no simple way of performing a device authorization.
I already have a draft of the implementation described above, but would love your input on my take. Let me know if you are interested in me providing a merge request.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:14
- Comments:8
Top GitHub Comments
@rodrigoGA I’m using:
implementation "com.github.formula-micro:AppAuth-Android:-SNAPSHOT"
Not a maintainer, but I wouldn’t expect it to get merged anytime soon. On the “surface” the request seems perfect though, only thing I found was a simple spelling error.
I forked the repository and merged the request back in november (see my fork), and it’s worked really well for me. I’m using my fork via JitPack.