OAuth2.0 Device Authorization Grant (device_code)

Hello experts,

I do have an API Endpoint that requires the OAuth2.0 Device Authorization Grant flow (rfc8628)

I searched in the code on the latest main branch (3.2.0-SNAPSHOT) if the OAuthClientService does support this mechanism.
My current conclusion is → no.

Question 1: Is there something ongoing to support the device_code process? I found some comments by the binding of HomeConnect but as for now I couldn’t find more details.

In general it’s not that complicated regarding the auth flow and I thought about the issue how to realize. Till now I couldn’t find more specific infos, maybe my fault, how to support an interaction with the user.

Maybe in short what I’m looking for, or what the new binding (I’m currently on it) is:

  1. Create an BridgeHandler where the user could send his login request (OAuth2)
    If the user has configured the Bridge he should “apply” to the change and the UI should then show the returned device_code (in optimal visualisation the generated QR Code)
  2. The Bridge is refreshing the page all the time in f.e. 10 sec. and showing the latest state.
  3. In the meantime the customer added the code / entered the code on the other platform to accept the openHAB Server (as device)
  4. openHAB Server is receiving an token by the auth service and the Bridge is getting “ONLINE”

Next steps would then autodiscovery etc.

Question 2: Is there already an add-on which does do this? Checked a lot but didn’t found anything? Or is this still not working?

Big thanks for answers!
Marc

I’m curious about what service only supports this OAuth flow? As per the RFC:

This OAuth 2.0 [RFC6749] protocol extension enables OAuth clients to request user authorization from applications on devices that have limited input capabilities or lack a suitable browser.

Since the OH UI is running entirely in a regular browser, this shouldn’t be necessary, it can use the authorization grant flow without problem.

Either way, if the service only offers this flow, I think the easiest way would be to redirect the user like in the auth grant flow instead of displaying a uri to be entered on a different device. The Bridge handler would then only need to poll the auth server until the user have authenticated.

Well in short why the service is providing only this specifc flow:

It enables OAuth clients on such devices […] to obtain user authorization to access protected resources
by using a user agent on a separate device.

Additionally the service doesn’t provide any Web UI and as I know so far is, that a browser based UI for OAuth2 isn’t a required component.

Back to the question - Is it possible to configure the Bridge configuration UI? I didn’t found anything in the docu nether in the community forum. Maybe I searched with the wrong keywords.

Some bindings requiring regular OAuth authorization solve this by providing a servlet (see e.g. the spotify binding). I would suggest you do something similar, but since the OAuthClient doesn’t support this flow you’ll have to do some things differently.

  1. Have the BridgeHandler make the first request (passing the client ID and receiving the Device code, User Code and Verification URI) after the user have configured the Bridge.
  2. The Handler then starts polling for the Access token.
  3. The user then has to navigate to the servlet, which can provide a link to the verification URI, and authenticate with the provider (the servlet will need to get a handle to the BridgeHandler, see how the spotify binding solves this).
  4. After this is done the Handler should receive an Access token in json format, which can be parsed into an AccessTokenResponse (see here for how this can be done).
  5. Finally, import the AccessTokenResponse to the OAuthClient, which can handle storing and refreshing the token just like for other bindings.

What kind of devices do they expect to connect to their service? If they assume all devices are simple and cannot provide a full browser with input capabilities? I could understand why they would want to offer it as an alternative, but I find it strange that this is the only option.

I don’t believe anyone else have had the need to use this flow, that’s why searching came up empty.

This is true, with the exception of a login form that allows the user to authenticate with the provider. OAuth is designed to enable machine-to-machine communication, without further user interaction after the first authentication (some services may require periodic re-authentication though)

1 Like

Hey Marc,
I have implemented Device Flow client (see here) and also created compatible OAuth 2 Device Flow server. Sources are available over here:

If you look there - there is device side of authentication, meaning polling for the code, basic user interface (servlet) to display it for end user, as well as completion of process. Once authentication is completed a DeviceAuthentication service is being published.
Few points to be taken - A) in my design it works fine with service cause it is being used for both websocket and mqtt connections to central server. It might not be good design for multi-binding approach.
B) You can request (usually) multiple device codes and your binding/thing handler needs to either forget old requests or memorize them for time of their validity (I do neither).
C) You might need an extra parameters added to request so server can identify your device in a reliable way. For my own service I used sha(instance-id), however I plan to switch to more reliable identifier such as client certificate.

Here is how UI looks a like:

If you try to get device flow working on OH side then for consuming side (device) it is, as you found, rather simple. I’ve done this using third party library called nimbus-ds instead of writing N-th time the handling code.

Now, for your question:

  1. Can bridge have configuration?
    Yes, bridge is yet another kind of thing which shares whole configuration framework.
2 Likes

Thanks a lot I will have a close look on this.

Thanks a lot for that tipp. I’m investigating it and your solution is, what I feared, now my next target to get it running. As soon as I do have the API bound I will come up with the topic “what kind of devices”

Big thanks

Few notes on top of above message exchange. While it is true that oauth is designed to permit server-server interactions the key point is semantics of what or who these servers are. I believe this is the key to understand how it should work. I had multiple attempts towards oauth 2 and only when I sorted out my head about component roles it become clearer to me. Reading code of any oauth client is much simpler when terminology is stabilized.

I do not wish to start an encyclopedia on it. Take it as “plain man understanding of oauth”. If you have a closer look on oauth there are several things:

  1. Authorization Server, place which confirm user identity and ship tokens,
  2. Resource Server, which holds data accessible for an user and require token to release it
  3. Client Application, which in practice could be another server instance or just a browser instance on user device.

This triangle forms three angles which user is familiar this or other way. Finally interactions between these elements and sequence of them define so called oauth “flows”.
Because oauth defines “resource server” element there is obviously also a “resource instance” and “resource owner”. In most of situations the owner part or even an resource is being omitted yet it is in some ways inherent to deployments unless we rely on oauth (actually openid connect, cause oauth is fairly limited in this field) just to proof user identity. The resource owner is usually user of the system, however if we dive more it might be that actual user receives a grant from another owner (ie. thins defined by UMA specification). The result of working authorization sequence can be summarized into single phase

the user grants a permit (represented by refresh token) to his resources held by certain server to an end application (client).

Now, when we look at most common flows we will find few. One is called called authorization code flow (historically called 3-legged in oauth 1), resource owner credentials flow (refereed also as password credentials), client credentials (historically called 2 legged), implicit and also device flow.

Looking at authorization code - in many situations it is best suited for server applications, hence it is extensively used when we have a standalone application which has separate communication path to authorization server. Yet, it also requires client application to expose an public endpoint.
Main advantage of device flow compared to above is that it allows user to confirm that certain device has access to its data (or produce data on behalf of an user) without necessity for this user to launch browser session on that device. Quite often issue on small devices is that device on which oauth client is working might not have a keyboard available. They also have limited capabilities to display information, thus code is just few characters. Another point to note - device flow does not require device to expose any endpoint as whole process is based on associated HTTP calls happening over two separate communication paths - device -> authorization server and user -> authorization server.

Anyhow, that would be it :wink:
The client code I wrote works with any server which is compatible with device flow. You can test it by creating file called org.connectorio.cloud.device.auth.cfg (next to org.ops4j.pax.logging.cfg) with following contents:

authURI=<place to obtain device code>
tokenURI=<place to obtain token>
clientId=<client id>

I am actually curious if you will get it running with your provider. If you’re up for a test then I can help you getting client deployed. I found for example that client id should be somehow dynamic if you have multiple devices owned by same user account. :slight_smile: If you use same client id/user account then SSO session retained by user browser can propagate information from one device in context of another. Spec does not cover that yet authorization served I worked with didn’t like that at all!

Cheers,
Łukasz

1 Like