Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow basic authentication to authorize API access #1713

Merged
merged 7 commits into from
Oct 18, 2020

Conversation

ghys
Copy link
Member

@ghys ghys commented Oct 11, 2020

Closes #1699.

Note, this opens a minor security issue that allows an attacker
to brute force passwords by making calls to the API - contrary to
the authorization page, the credentials parsing for the REST API
is stateless & doesn't have a lock mechanism to lock user accounts
after too many failed login attempts.

Signed-off-by: Yannick Schaus github@schaus.net

Closes openhab#1699.

Note, this opens a minor security issue that allows an attacker
to brute force passwords by making calls to the API - contrary to
the authorization page, the credentials parsing for the REST API
is stateless & doesn't have a lock mechanism to lock user accounts
after too many failed login attempts.

Signed-off-by: Yannick Schaus <github@schaus.net>
@kaikreuzer
Copy link
Member

I like how you do not go into lengthy discussions, but rather produce code ;-)
Will have to look at what implications this PR actually has before merging - let's get M1 first out of the door!

@ghys
Copy link
Member Author

ghys commented Oct 11, 2020

Sure, this one was more to prove a point than anything else, really... :)

@@ -63,6 +71,25 @@ public void filter(ContainerRequestContext requestContext) throws IOException {
Authentication auth = jwtHelper.verifyAndParseJwtAccessToken(authParts[1]);
requestContext.setSecurityContext(new JwtSecurityContext(auth));
return;
} else if ("Basic".equals(authParts[0])) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A small suggestion here - it might be worth to use equalsIgnoreCase as some tools can send auth type lowercase.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ghys This suggestion does not yet seem to be addressed.

@ghys
Copy link
Member Author

ghys commented Oct 12, 2020

I was thinking, maybe this could be made optional with a configurable service "REST API Authentication > Allow Basic Authentication", and disabled by default. Most people wouldn't need it, unless they want to perform admin operations from external tools without doing the OAuth2 dance.

Another option could be "Protect User Operations", also disabled by default. Currently these operations aren't protected at all: getting items, sending commands, the reason being that would probably break most end-user UIs. But if we enable it and make it advanced, the UI developers can enable it to help developing support. I have to check, but I believe the main UI can work with this option enabled, so you can't lock yourself out.

@sikksakk
Copy link

sikksakk commented Oct 12, 2020 via email

@spacemanspiff2007
Copy link
Contributor

I'd rather see the rules (e.g. admin, user) implemented consistently so they work independent of auth type (Oauth, BasicAuth).
With basic auth available using it really is easy and whitelisting hosts would prevent role based operations.

@kaikreuzer
Copy link
Member

maybe this could be made optional with a configurable service "REST API Authentication > Allow Basic Authentication", and disabled by default.

I think an "Authentication" (or "Security"?) System Services entry will be helpful in any case.
And yes, I'd agree that basic auth should be disabled by default, since from a security perspective everything that is not required for the standard operation of the system should not be active as it is a potential vulnerability.

Another option could be "Protect User Operations", also disabled by default.

Yes, it would be at least a first step towards a fully secured setup. 👍

Adding whitelisting etc. can then also be future options for this new configurable service.

Would you want to add such a configurable service (with a switch for basic auth) as part of this PR?

@spacemanspiff2007
Copy link
Contributor

And yes, I'd agree that basic auth should be disabled by default, since from a security perspective everything that is not required for the standard operation of the system should not be active as it is a potential vulnerability.

I'd still like Basic Auth at be least be enabled by default, if not made the default method for authentication.
Everybody that will interact with openhab from the outside will use it (except for the built in UI ofc) because it is easier to use and exactly as secure as OAuth2.
I've asked multiple times what OAuth2 brings to the table that can't be done with Basic Auth and the only factual argument so far has been that the opaque token prevents leaking the used password to a possible attacker while still allowing him access to the openhab instance. I've even backed up my statements with an article from an "international community of API practitioners and enthusiasts" which said that OAuth is not for authentication (!).

I know it's annoying that I stress this so much because it's already implemented but I firmly believe OAuth will only create problems for the users of openhab whilst providing hardly any benefits.

@openhab-bot
Copy link
Collaborator

This pull request has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/oh3-with-nginx-reverse-proxy-and-authentication/106528/3

@9037568
Copy link
Contributor

9037568 commented Oct 16, 2020

I know it's annoying that I stress this so much because it's already implemented but I firmly believe OAuth will only create problems for the users of openhab whilst providing hardly any benefits.

Absolutely.

Introduce a new AnonymousUserSecurityContext for unauthenticated
users that should be allowed the "user" role according to service
configuration.

Signed-off-by: Yannick Schaus <github@schaus.net>
@ghys
Copy link
Member Author

ghys commented Oct 16, 2020

@kaikreuzer I added the options.

image

I also contemplated adding the support for API tokens in this PR (they would prevent external tools from having to perform the OAuth2 flow to get a token, while not requiring to enable Basic auth) and Karaf commands as mentioned here: openhab/openhab-webui#332 (comment)
But perhaps it would be best to open another PR for that, I think I can do it before the next milestone.

Btw, not particularly happy to mention this, but this would bring us on par with what another popular home automation platform has implemented and somehow didn't get nearly as much backlash as we see here...

@spacemanspiff2007
Copy link
Contributor

Btw, not particularly happy to mention this, but this would bring us on par with what another popular home automation platform has implemented and somehow didn't get nearly as much backlash as we see here...

We can go into a lengthy discussion what Home Assistant does better (or worse) than Openhab but this will not bring any new factual arguments to the table. Imho the goal should not be to implement Home Assistant in Java but rather to develop well thought and consistent concepts and to build a better smart home system.
Home Assistant does generate 10 year long living tokens by default and you even can name the integration - so it's basically a static user/pw combination.

And while one could say it doesn't matter if you generate a basic auth header or a header with a token it really does.
Many devices allow pushing their state with simple requests to a destination. A basic auth header can be generated with user:password directly in the url. There is no possibility to modify the header, just the url.
E.g. almost all IP cameras that push the movement detection this way and polling doesn't make sense there.
If you enforce oauth you break integrations without providing any more security and the question still stands why one would do that.

Copy link
Member

@kaikreuzer kaikreuzer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, thanks! Just some minor comments.

@@ -63,6 +71,25 @@ public void filter(ContainerRequestContext requestContext) throws IOException {
Authentication auth = jwtHelper.verifyAndParseJwtAccessToken(authParts[1]);
requestContext.setSecurityContext(new JwtSecurityContext(auth));
return;
} else if ("Basic".equals(authParts[0])) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ghys This suggestion does not yet seem to be addressed.


<config-description uri="system:restauth">
<parameter name="allowBasicAuth" type="boolean" required="false">
<label>Allow Basic Authentication</label>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add default=false here, just to make it clear what is used when not being set?

<description>Allow the use of Basic authentication to access protected API resources.</description>
</parameter>
<parameter name="noUnauthenticatedUserRole" type="boolean" required="false">
<advanced>true</advanced>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add default=true here, just to make it clear what is used when not being set?

Object value = properties.get(CONFIG_ALLOW_BASIC_AUTH);
allowBasicAuth = value != null && "true".equals(value.toString());
value = properties.get(CONFIG_NO_UNAUTHENTICATED_USER_ROLE);
noUnauthenticatedUserRole = value != null && "true".equals(value.toString());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Argh, I am really getting a knot in my brain here (German proverb) by this double negation. How about changing this parameter to something like "enableAuthForUserResources"?

Copy link
Member Author

@ghys ghys Oct 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was more to the tune of "prevent unauthenticated requests from getting an implicit user role in the security context", so I tried to rename it to "noImplicitUserRole" / "No implicit user role for unauthenticated requests", that's perhaps clearer. Still a double negation, so if you still disagree I'll change it to "enableAuthForUserResources" :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And if we change it to "ImplicitUserRole" and have default=true, knowing that on the long run our wish is to get the setting to OFF? It would be in line with the basic auth setting, i.e. enabling something means your system becomes less secure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I insisted on having false as the default but that's not really a requirement.

@kaikreuzer
Copy link
Member

But perhaps it would be best to open another PR for that, I think I can do it before the next milestone.

Fully agree, that should be a separate PR.

We can go into a lengthy discussion what Home Assistant does better (or worse)

I think @ghys main point here was that there are also very clever guys at HA, who have put a lot of thought into shaping their Auth API and it isn't by accident that they have chosen something different than Basic Auth.

A basic auth header can be generated with user:password directly in the url.

That's deprecated and should not be used. See https://www.ietf.org/rfc/rfc3986.txt sections 3.2.1 and 7.5.

almost all IP cameras that push the movement detection this way and polling doesn't make sense there.

IP cams are the best example how to NOT do any secure software. They are hacked together in no time and I have hardly seen more buggy IoT devices out there. They are the backbone of Mirai and other botnets for a reason...

I firmly believe OAuth will only create problems for the users of openhab whilst providing hardly any benefits.

This opinion is pretty much against any security best practice of the last decade. Again, Basic Auth has some major flaws that a token based authentication prevents:

  • It sends the password as clear text over the wire
  • It continuously sends the password with every single request
  • If the password is compromised, it has to be changed
  • It does not allow any advanced protection against replay attacks, such as TTL, timestamps etc

But never mind - this PR introduces Basic Auth for the ones that really want it despite its shortcomings, so everyone should be served.

@spacemanspiff2007
Copy link
Contributor

That's deprecated and should not be used. See https://www.ietf.org/rfc/rfc3986.txt sections 3.2.1 and 7.5.

I was not aware that it should not be used, yet it still is widely supported.

This opinion is pretty much against any security best practice of the last decade. Again, Basic Auth has some major flaws that a token based authentication prevents:

I would have really like a constructive discussion like this in the original issue, but I'll comment anyway.
When using a long living token:

  • It sends the token as clear text over the wire
  • It continuously sends the token with every single request
  • If the token is compromised, it has to be changed
  • It does not allow any advanced protection against replay attacks, such as TTL, timestamps etc

I still don't see any difference to Basic Auth!

OAuth has become the standard because it allows to delegate access and allows separation of access granting and api infrastructure - a separation we don't have in the case of openhab.

ghys and others added 2 commits October 18, 2020 18:29
Signed-off-by: Yannick Schaus <github@schaus.net>
…F/config/config.xml


Signed-off-by: Yannick Schaus <github@schaus.net>

Co-authored-by: Kai Kreuzer <kai@openhab.org>
@ghys
Copy link
Member Author

ghys commented Oct 18, 2020

OAuth has become the standard because it allows to delegate access and allows separation of access granting and api infrastructure - a separation we don't have in the case of openhab.

Which is something that one day we might want to do, for instance there's a Synology SSO server (https://global.download.synology.com/download/Document/Software/DeveloperGuide/Package/SSOServer/All/enu/Synology_SSO_API_Guide.pdf) which is also OAuth2; openHAB runs on Synology so it's not that far fetched that we might offer delegating to it for the identity & credentials management.
Some admins might also want to enable a multi-factor authentication provider to open a session, and the sign in/authorize page might offer a way to plug-in such providers.

Imho the goal should not be to implement Home Assistant in Java but rather to develop well thought and consistent concepts and to build a better smart home system.

Agreed, my remark was not merely whataboutism or emulation at all costs (though there's no shame in borrowing good ideas), but simply pointing out the fact that they've implemented it like this for 2 years, so we might profit from this hindsight and AFAIK there were no major complaints.
So the assessment that it would be a complete nightmare for openHAB users is perhaps more of an opinion than a fact. My guess is that 80% of users won't care about this at all, perhaps 10% will be fine with using non-expiring, special-purpose, scoped tokens to integrate services and the remaining 10% can now enable Basic auth for their instances if they're fine with sharing passwords around.
I'm just getting another idea: allowing Basic auth could be a per-user setting as well, so regular user accounts can have an authentication experience potentially including MFA or SSO, and service accounts can access the API with Basic auth.

@kaikreuzer
Copy link
Member

allowing Basic auth could be a per-user setting

That would imho make the configuration pretty complex - I do not see much value in this.

service accounts can access the API with Basic auth.

Service accounts should use individual tokens (api keys), which are created for them and which can anytime be revoked without requiring the user to change his password.

Signed-off-by: Yannick Schaus <github@schaus.net>
Signed-off-by: Yannick Schaus <github@schaus.net>
@kaikreuzer kaikreuzer merged commit e26c49b into openhab:master Oct 18, 2020
@kaikreuzer kaikreuzer added the enhancement An enhancement or new feature of the Core label Oct 18, 2020
@kaikreuzer kaikreuzer added this to the 3.0.0.M2 milestone Oct 18, 2020
@openhab-bot
Copy link
Collaborator

This pull request has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/openhab-3-0-milestone-1-discussion/106330/105

@bwosborne2
Copy link

bwosborne2 commented Oct 18, 2020

Note, this opens a minor security issue that allows an attacker to brute force passwords by making calls to the API - contrary to the authorization page,`

Why should Basic AUTHENTICATION ( identifying WHO you are)
affect AUTHORIZATION ( what an authenticated entity is PERMITTED to do) They could stuill do Basic AUTHENTICATION and then proceed to gat an AUTHORIZATION token.?
If you are concerned, add the third part of AAA, ACCOUNTING ( what an entity actually DID)

As part of my daily job I administer RADIUS AAA servers and utilize REST API clients.

@bwosborne2
Copy link

@ghys needs to claim a bounty for this.
https://www.bountysource.com/issues/64726789-introduce-basic-auth-on-http-server

@ghys
Copy link
Member Author

ghys commented Oct 18, 2020

Note, this opens a minor security issue that allows an attacker to brute force passwords by making calls to the API - contrary to the authorization page,`

What I was trying to say is, formerly the only place where you could submit your password is in the form on the /auth page, where there are mechanisms to lock out for an increasing number of seconds after a unsuccessful attempt, so trying to guess the password with e.g. a dictionary attack becomes very unpractical very fast.
Now you can transmit your password in a Basic auth credential, where there's no such mechanism in place, so you can keep hammering the API several times a second until you've guessed the correct password, which can happen very fast if that password is too obvious.
With tokens the risk of that happening is minimal because tokens are by design not easy to guess.

@bwosborne2
Copy link

From what I understood, the OpenHAB UIs only access OH through the REST API. The REST Tokens are used for authorization, not authentication.

Some systems use a complex shared secret for authentication between trusted systems but still require authorization tokens for each use of the API. Others use username / password ( the lockout should be a server function anyway, independent of the client) and then require an authorization token.

You are the architect here, though.

@ghys
Copy link
Member Author

ghys commented Oct 18, 2020

Yes, the tokens are generated by already authenticated users (which might have used a much more secure way of doing so, like multi-factor) to authorize the bearer of this token to access the API on their behalf, eventually for a very limited scope.
If you pass the username/password to the API directly, like in Basic auth, you authenticate and authorize the user at the same time.

@CrazyIvan359
Copy link

CrazyIvan359 commented Oct 19, 2020

Just my 2c, I agree with @ghys and @spacemanspiff2007 on the pros/cons of tokens and basic auth, but I think tokens fit better for service access. All over the web you see mounting use of SSO+2FA to authenticate users, but as Yannick says, services get a token to access the API on behalf of the user. Both are transmitted in the clear (unless you are running https, which I am via nginx but still have local access on http) but with basic auth you have a username and password, both of which users (myself included tisk tisk) are very likely to reuse.

Add together that and the lack of throttling on auth attempts via basic auth, inside your average home network. Seems like a not ideal setup given the scope of access some installations have to hardware in people's homes. I'm no security expert, but I can't see the firewall on a $50 router being rock solid, never mind the cheap modem/router you get from most ISPs.

I'd rather provide a token that is in the URL or a header than user/pass for basic auth. For ease of use, tokens could default to full admin access but be restricted from there if desired.

I'm not suggesting the removal of what this PR adds, I just agree that it doesn't seem like the best way to provide access+security.

wborn added a commit to wborn/openhab-core that referenced this pull request Oct 19, 2020
Caused by openhab#1713

Signed-off-by: Wouter Born <github@maindrain.net>
kaikreuzer pushed a commit that referenced this pull request Oct 19, 2020
Caused by #1713

Signed-off-by: Wouter Born <github@maindrain.net>
ghys added a commit to ghys/openhab-core that referenced this pull request Oct 19, 2020
This adds API tokens as a new credential type. Their format is:
`oh.<name>.<random chars>`

The "oh." prefix is used to tell them apart from a JWT access token,
because they're both used as a Bearer authorization scheme, but there
is no semantic value attached to any of the other parts.

They are stored hashed in the user's profile, and can be listed, added
or removed managed with the new `openhab:users` console command.

Currently the scopes are still not checked, but ultimately they could
be, for instance a scope of e.g. `user admin.items` would mean that the
API token can be used to perform user operations like retrieving info
or sending a command, _and_ managing the items, but nothing else -
even if the user has more permissions because of their role (which
will of course still be checked).

Tokens are normally passed in the Authorization header with the Bearer
scheme, or the X-OPENHAB-TOKEN header, like access tokens.
As a special exception, API tokens can also be used with the Basic
authorization scheme, **even if the allowBasicAuth** option is not
enabled in the "API Security" service, because there's no additional
security risk in allowing that. In that case, the token should be
passed as the username and the password MUST be empty.

In short, this means that all these curl commands will work:
- `curl -H 'Authorization: Bearer <token>' http://localhost:8080/rest/inbox`
- `curl -H 'X-OPENHAB-TOKEN: <token>' http://localhost:8080/rest/inbox`
- `curl -u '<token>[:]' http://localhost:8080/rest/inbox`
- `curl http://<token>@localhost:8080/rest/inbox`

2 REST API operations were adding to the AuthResource, to allow
authenticated users to list their tokens or remove (revoke) one.
Self-service for creating a token or changing the password is more
sensitive so these should be handled with a servlet and pages devoid
of any JavaScript instead of REST API calls, therefore for now they'll
have to be done with the console.

This also fixes regressions introduced with openhab#1713 - the operations
annotated with @RolesAllowed({ Role.USER }) only were not authorized
for administrators anymore.

Signed-off-by: Yannick Schaus <github@schaus.net>
ghys added a commit to ghys/openhab-core that referenced this pull request Oct 19, 2020
This adds API tokens as a new credential type. Their format is:
`oh.<name>.<random chars>`

The "oh." prefix is used to tell them apart from a JWT access token,
because they're both used as a Bearer authorization scheme, but there
is no semantic value attached to any of the other parts.

They are stored hashed in the user's profile, and can be listed, added
or removed managed with the new `openhab:users` console command.

Currently the scopes are still not checked, but ultimately they could
be, for instance a scope of e.g. `user admin.items` would mean that the
API token can be used to perform user operations like retrieving info
or sending a command, _and_ managing the items, but nothing else -
even if the user has more permissions because of their role (which
will of course still be checked).

Tokens are normally passed in the Authorization header with the Bearer
scheme, or the X-OPENHAB-TOKEN header, like access tokens.
As a special exception, API tokens can also be used with the Basic
authorization scheme, **even if the allowBasicAuth** option is not
enabled in the "API Security" service, because there's no additional
security risk in allowing that. In that case, the token should be
passed as the username and the password MUST be empty.

In short, this means that all these curl commands will work:
- `curl -H 'Authorization: Bearer <token>' http://localhost:8080/rest/inbox`
- `curl -H 'X-OPENHAB-TOKEN: <token>' http://localhost:8080/rest/inbox`
- `curl -u '<token>[:]' http://localhost:8080/rest/inbox`
- `curl http://<token>@localhost:8080/rest/inbox`

2 REST API operations were adding to the AuthResource, to allow
authenticated users to list their tokens or remove (revoke) one.
Self-service for creating a token or changing the password is more
sensitive so these should be handled with a servlet and pages devoid
of any JavaScript instead of REST API calls, therefore for now they'll
have to be done with the console.

This also fixes regressions introduced with openhab#1713 - the operations
annotated with @RolesAllowed({ Role.USER }) only were not authorized
for administrators anymore.

Signed-off-by: Yannick Schaus <github@schaus.net>
ghys added a commit to ghys/openhab-core that referenced this pull request Oct 19, 2020
(I included these fixes in openhab#1735 but extracted them in a stanalone
PR because it's easier to review and a little more urgent.)

As a result of the refactoring in openhab#1713, the operations annotated with
`@RolesAllowed` containing `Role.USER` are not anymore automatically
considered accessible to all users, regardless of their actual roles.

4 operations are therefore now denied to admins if they only have the
`Role.ADMIN` role, as the first admininistrator is created only with
that role the UI encounters unexpected access denied errors and breaks.
(See openhab/openhab-webui#422).

Closes openhab/openhab-webui#422.

Signed-off-by: Yannick Schaus <github@schaus.net>
@ghys ghys mentioned this pull request Oct 19, 2020
kaikreuzer pushed a commit that referenced this pull request Oct 20, 2020
(I included these fixes in #1735 but extracted them in a stanalone
PR because it's easier to review and a little more urgent.)

As a result of the refactoring in #1713, the operations annotated with
`@RolesAllowed` containing `Role.USER` are not anymore automatically
considered accessible to all users, regardless of their actual roles.

4 operations are therefore now denied to admins if they only have the
`Role.ADMIN` role, as the first admininistrator is created only with
that role the UI encounters unexpected access denied errors and breaks.
(See openhab/openhab-webui#422).

Closes openhab/openhab-webui#422.

Signed-off-by: Yannick Schaus <github@schaus.net>
ghys added a commit to ghys/openhab-core that referenced this pull request Oct 20, 2020
This adds API tokens as a new credential type. Their format is:
`oh.<name>.<random chars>`

The "oh." prefix is used to tell them apart from a JWT access token,
because they're both used as a Bearer authorization scheme, but there
is no semantic value attached to any of the other parts.

They are stored hashed in the user's profile, and can be listed, added
or removed managed with the new `openhab:users` console command.

Currently the scopes are still not checked, but ultimately they could
be, for instance a scope of e.g. `user admin.items` would mean that the
API token can be used to perform user operations like retrieving info
or sending a command, _and_ managing the items, but nothing else -
even if the user has more permissions because of their role (which
will of course still be checked).

Tokens are normally passed in the Authorization header with the Bearer
scheme, or the X-OPENHAB-TOKEN header, like access tokens.
As a special exception, API tokens can also be used with the Basic
authorization scheme, **even if the allowBasicAuth** option is not
enabled in the "API Security" service, because there's no additional
security risk in allowing that. In that case, the token should be
passed as the username and the password MUST be empty.

In short, this means that all these curl commands will work:
- `curl -H 'Authorization: Bearer <token>' http://localhost:8080/rest/inbox`
- `curl -H 'X-OPENHAB-TOKEN: <token>' http://localhost:8080/rest/inbox`
- `curl -u '<token>[:]' http://localhost:8080/rest/inbox`
- `curl http://<token>@localhost:8080/rest/inbox`

2 REST API operations were adding to the AuthResource, to allow
authenticated users to list their tokens or remove (revoke) one.
Self-service for creating a token or changing the password is more
sensitive so these should be handled with a servlet and pages devoid
of any JavaScript instead of REST API calls, therefore for now they'll
have to be done with the console.

This also fixes regressions introduced with openhab#1713 - the operations
annotated with @RolesAllowed({ Role.USER }) only were not authorized
for administrators anymore.

Signed-off-by: Yannick Schaus <github@schaus.net>
wborn pushed a commit that referenced this pull request Oct 25, 2020
This adds API tokens as a new credential type. Their format is:
`oh.<name>.<random chars>`

The "oh." prefix is used to tell them apart from a JWT access token,
because they're both used as a Bearer authorization scheme, but there
is no semantic value attached to any of the other parts.

They are stored hashed in the user's profile, and can be listed, added
or removed managed with the new `openhab:users` console command.

Currently the scopes are still not checked, but ultimately they could
be, for instance a scope of e.g. `user admin.items` would mean that the
API token can be used to perform user operations like retrieving info
or sending a command, _and_ managing the items, but nothing else -
even if the user has more permissions because of their role (which
will of course still be checked).

Tokens are normally passed in the Authorization header with the Bearer
scheme, or the X-OPENHAB-TOKEN header, like access tokens.
As a special exception, API tokens can also be used with the Basic
authorization scheme, **even if the allowBasicAuth** option is not
enabled in the "API Security" service, because there's no additional
security risk in allowing that. In that case, the token should be
passed as the username and the password MUST be empty.

In short, this means that all these curl commands will work:
- `curl -H 'Authorization: Bearer <token>' http://localhost:8080/rest/inbox`
- `curl -H 'X-OPENHAB-TOKEN: <token>' http://localhost:8080/rest/inbox`
- `curl -u '<token>[:]' http://localhost:8080/rest/inbox`
- `curl http://<token>@localhost:8080/rest/inbox`

2 REST API operations were adding to the AuthResource, to allow
authenticated users to list their tokens or remove (revoke) one.
Self-service for creating a token or changing the password is more
sensitive so these should be handled with a servlet and pages devoid
of any JavaScript instead of REST API calls, therefore for now they'll
have to be done with the console.

This also fixes regressions introduced with #1713 - the operations
annotated with @RolesAllowed({ Role.USER }) only were not authorized
for administrators anymore.

* Generate a unique salt for each token

Reusing the password salt is bad practice, and changing the
password changes the salt as well which makes all tokens
invalid.

Put the salt in the same field as the hash (concatenated
with a separator) to avoid modifying the JSON DB schema.

* Fix API token authentication, make scope available to security context

The X-OPENHAB-TOKEN header now has priority over the Authorization
header to credentials, if both are set.

* Add self-service pages to change password & create new API token

Signed-off-by: Yannick Schaus <github@schaus.net>
splatch pushed a commit to ConnectorIO/copybara-hab-core that referenced this pull request Jul 11, 2023
* Allow basic authentication to authorize API access

Closes openhab#1699.

Note, this opens a minor security issue that allows an attacker
to brute force passwords by making calls to the API - contrary to
the authorization page, the credentials parsing for the REST API
is stateless & doesn't have a lock mechanism to lock user accounts
after too many failed login attempts.

Signed-off-by: Yannick Schaus <github@schaus.net>
GitOrigin-RevId: e26c49b
splatch pushed a commit to ConnectorIO/copybara-hab-core that referenced this pull request Jul 11, 2023
Caused by openhab#1713

Signed-off-by: Wouter Born <github@maindrain.net>
GitOrigin-RevId: b2c045d
splatch pushed a commit to ConnectorIO/copybara-hab-core that referenced this pull request Jul 11, 2023
(I included these fixes in openhab#1735 but extracted them in a stanalone
PR because it's easier to review and a little more urgent.)

As a result of the refactoring in openhab#1713, the operations annotated with
`@RolesAllowed` containing `Role.USER` are not anymore automatically
considered accessible to all users, regardless of their actual roles.

4 operations are therefore now denied to admins if they only have the
`Role.ADMIN` role, as the first admininistrator is created only with
that role the UI encounters unexpected access denied errors and breaks.
(See openhab/openhab-webui#422).

Closes openhab/openhab-webui#422.

Signed-off-by: Yannick Schaus <github@schaus.net>
GitOrigin-RevId: d262b6f
splatch pushed a commit to ConnectorIO/copybara-hab-core that referenced this pull request Jul 11, 2023
This adds API tokens as a new credential type. Their format is:
`oh.<name>.<random chars>`

The "oh." prefix is used to tell them apart from a JWT access token,
because they're both used as a Bearer authorization scheme, but there
is no semantic value attached to any of the other parts.

They are stored hashed in the user's profile, and can be listed, added
or removed managed with the new `openhab:users` console command.

Currently the scopes are still not checked, but ultimately they could
be, for instance a scope of e.g. `user admin.items` would mean that the
API token can be used to perform user operations like retrieving info
or sending a command, _and_ managing the items, but nothing else -
even if the user has more permissions because of their role (which
will of course still be checked).

Tokens are normally passed in the Authorization header with the Bearer
scheme, or the X-OPENHAB-TOKEN header, like access tokens.
As a special exception, API tokens can also be used with the Basic
authorization scheme, **even if the allowBasicAuth** option is not
enabled in the "API Security" service, because there's no additional
security risk in allowing that. In that case, the token should be
passed as the username and the password MUST be empty.

In short, this means that all these curl commands will work:
- `curl -H 'Authorization: Bearer <token>' http://localhost:8080/rest/inbox`
- `curl -H 'X-OPENHAB-TOKEN: <token>' http://localhost:8080/rest/inbox`
- `curl -u '<token>[:]' http://localhost:8080/rest/inbox`
- `curl http://<token>@localhost:8080/rest/inbox`

2 REST API operations were adding to the AuthResource, to allow
authenticated users to list their tokens or remove (revoke) one.
Self-service for creating a token or changing the password is more
sensitive so these should be handled with a servlet and pages devoid
of any JavaScript instead of REST API calls, therefore for now they'll
have to be done with the console.

This also fixes regressions introduced with openhab#1713 - the operations
annotated with @RolesAllowed({ Role.USER }) only were not authorized
for administrators anymore.

* Generate a unique salt for each token

Reusing the password salt is bad practice, and changing the
password changes the salt as well which makes all tokens
invalid.

Put the salt in the same field as the hash (concatenated
with a separator) to avoid modifying the JSON DB schema.

* Fix API token authentication, make scope available to security context

The X-OPENHAB-TOKEN header now has priority over the Authorization
header to credentials, if both are set.

* Add self-service pages to change password & create new API token

Signed-off-by: Yannick Schaus <github@schaus.net>
GitOrigin-RevId: 8b52cab
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement An enhancement or new feature of the Core
Projects
None yet
Development

Successfully merging this pull request may close these issues.

OAuth2 makes it difficult to consume the REST API
9 participants