domoticz / domoticz-android

Domoticz Client Application for Android Devices
104 stars 69 forks source link

Implement OAuth as its now in Domoticz Beta #679

Open galadril opened 1 year ago

galadril commented 1 year ago

More info about OAuth: https://github.com/domoticz/domoticz/blob/development/SECURITY_SETUP.md

kiddigital commented 1 year ago

Flow and pseudo code (WIP):

Now this Bearer token should be provided with each request between the App and Domoticz using the 'Authorization' header.

Once the token has expired (receiving 401 from Domoticz), the refresh token can be used to request a new token without the need to go through the above process and asking the User for credentials again.

galadril commented 1 year ago

I could be wrong, but isnt the whole purpose of having a oauth pkce flow to not ask the user for credentials yourself from within the app. I would expect to open a browser with some parameters (challenge etc) to let Domoticz handle the authentication.

And then use the access/refresh tokens afterwards

Thats how i understand it from for example Okta https://developer.okta.com/docs/guides/implement-grant-type/authcodepkce/main/

kiddigital commented 1 year ago

That is indeed how it works on the web. And if you 'sent' the user to Domoticz, it will ask for a User/Pass (currently through standard Basic Auth dialog of the browser and not via a nice login dialog at the moment) and redirect the User back.

I am not an App developer, I do not know if/how an App could send the User to their Domoticz instance, perform the login there and then get redirect back to the App. But I know it can be done as I see it work in multiple apps.

But you are right, ideally the App does not require to ask for User/Pass and just receives the 'code' and can continue from there. Let's make that work! And I am happy to help where I can.

galadril commented 1 year ago

So i can ask the user to fill in the port/ip for remote and local domoticz connections for example.

First the app will then generate a code verifier followed by a code challenge (for the pkce challenge) Then open a browser to allow for user authentication with the next url;

https://{host}:{port}/authorize?
    response_type=code&
    code_challenge={codeChallenge}&
    code_challenge_method=S256&
    client_id=YOUR_CLIENT_ID&
    redirect_uri={yourCallbackUrl}&
    scope=SCOPE&
    state={state}

if the user is authenticated, domoticz login form is going to redirect to the {yourCallbackUrl} adding some authenticationToken parameter and the same state value as the original input:

{yourCallbackUrl}?code={authorizationCode}&state={state}

image

After authenticating, show a permissions acknowledgement form, Domoticz asking the user if you allow that application to connect image

Then i could use this authorizationCode to get an access and refresh token via some POST call like:

curl --request POST \
  --url 'https://{host}:{port}/oauth/token' \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data grant_type=authorization_code \
  --data 'client_id=YOUR_CLIENT_ID' \
  --data 'code_verifier={yourGeneratedCodeVerifier}' \
  --data 'code={yourAuthorizationCode}' \
  --data 'redirect_uri={yourCallbackUrl}'

That token endpoint checks the PKCE code challenge and verifier.. also the redirect url should be the same as the original requested one and validates the client id.

If all goes well, you'll receive an HTTP 200 response with a payload containing access_token, refresh_token, id_token, and token_type values:

{
  "access_token":"eyJz93a...k4laUWw",
  "refresh_token":"GEbRxBN...edjnXbL",
  "id_token":"eyJ0XAi...4faeEoQ",
  "token_type":"Bearer",
  "expires_in":86400
}

If i need to refresh my access token, it would be something like:

curl --request POST \
  --url 'https://{host}:{port}/oauth/token' \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data grant_type=refresh_token \
  --data 'client_id=YOUR_CLIENT_ID' \
  --data 'refresh_token={yourRefreshToken}'

a sample of a yourCallbackUrl would be for the mobile app: domoticz://app

that redirect link has a deeplink back the app in.

This is how im used to implementing PKCE / OAUTH flows

kiddigital commented 1 year ago

Yes, that is correct :)

Domoticz internal OAuth2/OIDC service does not have such nice (2-step) login/consent screens yet, but I am working on a (single-step) login/consent screen.. That will be available soon in an upcoming Domoticz Beta.

But everything else is already there and can be implemented. Just currently the browser does not show a nice login but the standard Basic Auth dialog. But that is not relevant to complete the App side of the flow.

If I can help somehow with testing or ... let me know.

kiddigital commented 1 year ago

@galadril , I updated my initial comment with the Flow/Pseudo code...

The 'client_id' AND the 'client_secret' are both needed. Although using PKCE ensures the code exchange is not compromised, it does not authenticate the client application (more on this can be found here). And the 'client_secret' has to be provided when doing the POST to the token endpoint. This way the specific app instance gets a trust relation with the specific Domoticz instance.

Don't forget to query the /.well-known/openid-configuration first to get the correct URI's for the Authentication and Token endpoints. Somewhere in the future, Domoticz users can configure different URI's if they are using another IAM service instead of the now build-in one.

galadril commented 1 year ago

@kiddigital Did the new Domoticz update bring your oAuth to the stable release??

kiddigital commented 1 year ago

Yes! 😁 Give it a go..

galadril commented 1 year ago

I will.. but yeah moving houses, Android Auto support and Wear OS stopped working came in between..

galadril commented 1 year ago

@kiddigital while going throught this, i just got the idea.. why shouldn't we have a build-in client id/secret for the mobile app? As its part of Domoticz, just like the frontend.. its kinda weird for me to ask from the official Domoticz apps, to first setup the app right?

kiddigital commented 1 year ago

Makes sense indeed.. saves the User a step.

galadril commented 1 year ago

Can you provide me a build-in id/secret?

kiddigital commented 1 year ago

Can you provide me a build-in id/secret?

There is no really build-in id/secret, just entries in the Applications table.

Just create an Application called 'DomoticzMobileApp' with a random secret (7fc692bdd8adaadb2d77b91638770999 for example).

A few default entries will be there by default (disabled except the standard UI) including 'DomoticzMobileApp'.

But the User has to explicitly activate it AND set a secret (either private or public key-pair). Otherwise any app could fake it is a default app and only need User/Pass.

The default apps just make it easier (no typos in ClientID for example).

galadril commented 12 months ago

Domoticz internal OAuth2/OIDC service does not have such nice (2-step) login/consent screens yet, but I am working on a (single-step) login/consent screen.. That will be available soon in an upcoming Domoticz Beta.

was this done already btw?

kiddigital commented 12 months ago

Yes, there is an optional field for the 2FA code for Users that have 2FA enabled.

And by default there is an 'domoticzMobileApp' application available in the 'Applications' config which is disabled by default and has to be enabled.