TheThingsNetwork / lorawan-stack

The Things Stack, an Open Source LoRaWAN Network Server
https://www.thethingsindustries.com/stack/
Apache License 2.0
975 stars 306 forks source link

Introduce new account app (to replace oauth app) #1422

Closed kschiffer closed 3 years ago

kschiffer commented 5 years ago

Summary

We currently have a lot of issues revolving around our OAuth flow, account switching, token management, respective branding, etc. This is an attempt to make all of these more actionable under a new account app that will take care of all of these concerns.

Why do we need this?

What is already there? What do you see now?

What is missing? What do you want to see?

An app that acts as OAuth provider and fully featured and branded configuration UI around all account and authentication concerns

Environment

Web

How do you propose to implement this?

I think a big problem with the current implementation is the lacking clarity and context for the user. It is hard to recognize the OAuth app as an independent authentication entity. Reasons are:

I've created three wireframes that give an idea of how I envision the account app:

See wireframes ![account_login](https://user-images.githubusercontent.com/5710611/66202268-9e386880-e6a5-11e9-9c53-48fe07486778.png) ![account_overview](https://user-images.githubusercontent.com/5710611/66202274-a09ac280-e6a5-11e9-9c22-03d8885e69ba.png) ![account_profile_settings](https://user-images.githubusercontent.com/5710611/66202278-a2fd1c80-e6a5-11e9-9875-c72a597c1b27.png) (notice the account switcher integrated in the user dropdown in the top right)

Can you do this yourself and submit a Pull Request?

Yes. @johanstokking @htdvisser, please let me know whether you think its good to continue.

johanstokking commented 5 years ago

References https://github.com/TheThingsIndustries/lorawan-stack/issues/1659

kschiffer commented 4 years ago

Regarding the whole login/logout confusion issue between console and oauth provider, here's my plan:

If I don't hear any complaints about this plan I'll start implementing this.

johanstokking commented 4 years ago

Sounds like a great plan. I agree we don't need explicit logout of Console and stay logged in via OAuth in the UX, although it will work that way underneath.

What do you need for cross-origin logout? Wouldn't it be a simple two-step approach; logout "page" to remove Console stored access token, then redirect to generic logout page where the Account app logs out? Or do you want this without redirection but post to a logout endpoint?

htdvisser commented 4 years ago

For logout, we can get some inspiration from OpenID Connect logout. Nice summary can be found here: https://medium.com/@robert.broeckelmann/openid-connect-logout-eccc73df758f

kschiffer commented 4 years ago

Sounds like a great plan. I agree we don't need explicit logout of Console and stay logged in via OAuth in the UX, although it will work that way underneath.

What do you need for cross-origin logout? Wouldn't it be a simple two-step approach; logout "page" to remove Console stored access token, then redirect to generic logout page where the Account app logs out? Or do you want this without redirection but post to a logout endpoint?

Two-step approach is OK. My concern was that the logout of the account app needs to be CSRF disabled, meaning that anyone with a logout link could lure someone into logging out from their account. This is also currently possible with the v2 stack (clicking here will log you out). I think for now this is acceptable but not exactly best practice.

kschiffer commented 4 years ago

I've done quite some work now for the new account app:

I did this by hardcoding an access token, to be able to use the stack API. I'm currently unable to advance with a proper implementation, since I don't know what would be the best way to obtain an access token for the account app. Right now, the "oauth app" is just a SPA that connects to the auth endpoints of the oauth server (login, logout and other account related endpoint that do not need an access token). @htdvisser mentioned to me that our initial idea was to have the account app as separate oauth client, using the password grant type. I think we should open a discussion about this, since:

I already said to @htdvisser that the whole delegated authentication/authorization aspect of this is somewhat overwhelming to me and I don't feel knowledgeable enough to be responsible for such a security-sensitive matter.

What we should aim for is a flow that makes the authentication flow of our official clients feel like native authentication (see also my other comment), meaning that logins and logouts are global and there should be no distinction between the authenticated state of the clients and the authorization provider.

So, I'd need some help and input to go on with the account app. How can we coordinate this?

johanstokking commented 4 years ago

depending on the source, the password grant type is discouraged, even for "official" and trusted clients

Yes, password grant must not be used, see specification and reasoning here: https://tools.ietf.org/html/draft-ietf-oauth-security-topics-13#section-3.4

kschiffer commented 4 years ago

Currently blocked by #2148

kschiffer commented 4 years ago

So after being involved with this for a while now, I think I need some input.

The original idea of the account app was to replace the current oauth app and extend functionality towards profile and session management (see OP). The issue with this is that in order to implement such functionality, the app would need an access token, essentially meaning that the account app would be both an OAuth provider and client.

My idea was to introduce a new OAuth client (account) and frontend-wise, to blend the functionality with the current OAuth app. For the user, it would appear as though both OAuth provider (login, register, validate account, etc) and the account app client (profile settings, session management, authorization management) are one and the same application, while in the background, there is a technical separation between the OAuth provider and the client. On the backend, the account client would become a part of the oauth package. Frontend-wise we could also treat both things as the same app, using the same entry point (account.js). Likewise, the routing would reflect this blending, e.g.:

Alternatively, we could keep both concerns separated, leaving the OAuth provider (/oauth via pkg/oauth) as it is and treating the account app (/account via pkg/account) as a completely separate thing, like we do with the console. The OAuth provider would then be only responsible for authentication matters, including authorization and user account registration, but would not have an authenticated view of its own as it currently has. Instead, after the login, it would redirect to the account app by default, which would then retrieve a token automatically.

I feel like the first solution might avoid some of the confusion which we currently have revolving around authentication and account management, but I cannot foresee whether it might introduce other complications. The second solution seems cleaner, but the question is how to communicate/brand the separation. E.g. should both be communicated as "Account app" still? Or should it be the The Things Stack Single Sign On and The Things Stack Account Application?

Please let me know your opinions.

johanstokking commented 4 years ago

Hmm, taking one step back, what it is the purpose of making the account app an OAuth client? What functionality via OAuth do we want in there?

kschiffer commented 4 years ago

All of those necessitate an access token since that is the only way to authorize the respective RPCs.

htdvisser commented 4 years ago

We need a separation of the OAuth provider -- which could be external, for instance "The Things Network Community" -- and User management -- which is about managing fields of the User entity stored with The Things Stack, including the points @kschiffer wrote in the comment above.

Our /api/v3/* endpoints do not accept cookie auth, because they are CORS-enabled. They only work with API keys and Access Tokens.

kschiffer commented 4 years ago
  • Profile settings (name, email, profile picture)
  • Session management (review, revoke)
  • Authorization management (review, revoke)
  • (Potentially other TTES related meta settings in the future)

All of those necessitate an access token since that is the only way to authorize the respective RPCs.

We could also make these part of the console but it would mean that the console would mix concerns to some extend. I could see situations where it would be better to perform account management without the overhead of all the other console functionalities, but maybe that's me overengineering this 🤷‍♂️.

Generally, the question would be if we want to see the console only as a tool to manage network related matters. We can either open it up to be a general purpose management platform for all TTS related matters or we can be more atomic and stick with the separation and use different apps/clients for different concerns. It's a strategic question. I see cases for both.

htdvisser commented 4 years ago

I don't think the console (all consoles, because there's a console in each cluster) should have full rights on the user. Consoles shouldn't be used to manage authorized OAuth clients, sessions, primary email address, contact info. Having a dedicated User management app (the account app) is much better.

kschiffer commented 4 years ago

Indeed, good point. Then it comes back to the initial question.

My take is:

Blended approach:

Seperated approach:

I lean towards the blended approach.

johanstokking commented 4 years ago
  • Profile settings (name, email, profile picture)
  • Session management (review, revoke)
  • Authorization management (review, revoke)
  • (Potentially other TTES related meta settings in the future)

This is account app, right? Not OAuth?

  • Introduces a new app next to the oauth app, which needs sensible branding and communication

What is the OAuth app for then still?

Can you list the features for the user of what the OAuth client adds to the account app, that we cannot build in the account app, potentially via new endpoints?

kschiffer commented 4 years ago

This is account app, right? Not OAuth?

Yes, this is account app. But the plan was that what we currently have as OAuth app becomes part of the account app.

What is the OAuth app for then still?

For authentication (as part of the OAuth flow), user registration, client authorization, password reset. Basically everything that has to do with authentication of users and authorization of clients.

Can you list the features for the user of what the OAuth client adds to the account app, that we cannot build in the account app, potentially via new endpoints?

The issue is more the other way around, we cannot simply extend our current OAuth app with the functionalities we want for the account app, since we'd need to get an access token. It's a bit of a chicken and egg problem. If there was a different means of authorization for the account app functionalities (sessions, authorizations, etc), e.g. via the session cookie that the OAuth app has, then it would be different. But from what I see this is unfeasible.

johanstokking commented 4 years ago

Is the OAuth app a stand-alone thing? Is it implementing the redirect flows for OAuth clients? Or is it an OAuth client itself?


Emphasis mine:

My idea was to introduce a new OAuth client (account) and frontend-wise, to blend the functionality with the current OAuth app. For the user, it would appear as though both OAuth provider (login, register, validate account, etc) and the account app client (profile settings, session management, authorization management) are one and the same application, while in the background, there is a technical separation between the OAuth provider and the client. On the backend, the account client would become a part of the oauth package.

I see here;

I'm trying to understand the proposal, but I don't fully understand what is what still.

So what are the features that we want?

  1. Create account
  2. Forget password
  3. Login
  4. Change password
  5. View and edit profile
  6. Manage sessions
  7. OAuth stuff
    1. Consent screen (OAuth authorization code flow)
    2. Manage OAuth authorizations

I guess that 1-6 can be implemented without touching OAuth at all, if we go with session authentication. That is possible, right?

kschiffer commented 4 years ago

Yeah sorry. We have a number of unfixed terms here that make it quite difficult to explain things. At least it gives a good insight into the complexity of the issue 😅.

So let's take the status quo:

OAuth provider The entity that provides authorization (and in our case also authentication) according to the OAuth 2.0 specification. In our case that's the pkg/oauth package, which in turn uses the OAuth app (see below) to implement the necessary user interface to obtain user information (render login, registration, authorization views, etc.).

OAuth app This is the react web application on the frontend. Our frontend code consists of different app (with different entry points oauth.js / console.js) which share common parts such as react components and utilities

OAuth client A registered client that uses OAuth for authentication and authorization, like the console or CLI.

oauth package The Go package that is responsible for implementing the OAuth provider. That's pkg/oauth.

So you see, there are three or four different layers at play here. The problem is that if we keep the way we do authorization at the moment, we'd need to have something that plays all roles outlined above at the same time.

I guess that 1-6 can be implemented without touching OAuth at all, if we go with session authentication. That is possible, right?

Yes. 1-4 are already possible without oauth / access token. 5 and 6 currently need an access token. 7. ii is not yet implemented. If 5 and 6 would be possible without access token then we would indeed not need another oauth client to do this. Still we need to consider then that every functionality we might want to add to the account app in the future must then not use access token as sole means of authorization.

johanstokking commented 4 years ago

If 5 and 6 would be possible without access token then we would indeed not need another oauth client to do this. Still we need to consider then that every functionality we might want to add to the account app in the future must then not use access token as sole means of authorization.

Right. This makes a lot of sense to me. This is also better groundwork for "sudo mode", where users can do things only in the account app, after re-entering your password for example. So yes, there might be some overlap between what OAuth clients and the account app can do, but that overlap doesn't seem big to me. I see value in keeping things dedicated to the account app.

kschiffer commented 4 years ago

Alright, sounds good. @htdvisser Do you have any objections? And if not, can you point me to the relative files/packages for authorization, because I suppose you won't have time to work on this any time soon (?).

htdvisser commented 4 years ago

As I commented before, our /api/v3/* endpoints do not accept cookie auth, because they are CORS-enabled. If you want to start accepting cookie auth, you would need to take a good look at our CORS headers, because we really don't want an attacker to make cookie-authenticated cross-origin requests to these endpoints.


Our API is all gRPC, and auth is done with the Authorization header, which is propagated to gRPC as request metadata. Implementation of session auth could look like this:

kschiffer commented 4 years ago

One thing to consider here is that this also means that the account app and the v3 API need to be served from the same origin. Otherwise, cookie authentication wouldn't work as the auth context comprises different origins. Not sure how acceptable that is. At least in practice, we seem to be using the same origins in our deployments.

johanstokking commented 4 years ago

Yes, they will be on the same origin.

johanstokking commented 4 years ago

@kschiffer please let us know how we can unblock the progress here.

kschiffer commented 4 years ago

I wrote a summary here: https://github.com/TheThingsNetwork/lorawan-stack/pull/2850#issuecomment-657497193

johanstokking commented 4 years ago

OK, let's continue that conversation here.

@kschiffer After working on this for a bit:

  • The frontend part of the oauth app would basically consist of the authorization screen only

Yes

  • I think it's very annoying having to retain the is.oauth.ui.*  configs just for that [...]
  • I'm also a little bit unsure about the exact restrictions of how we can alter the stack configuration without breaking the CC
  • I'm a bit out of luck with finding a viable solution here and really need some input

We can't break configuration in V3, but we can introduce new configuration, deprecate the old configuration, use the old configuration as fallback, and file a TODO issue labeled bump/major to remove the old config.

So my suggestion would be to introduce new configuration that would be a complete replacement and enables the new account app. For backward compatibility, it should work without the user specifying this new configuration, i.e. rely on sane default values and old config via is.oauth.ui.*.

johanstokking commented 3 years ago

@kschiffer what is the status here?

kschiffer commented 3 years ago

Currently rebasing my work on the new scaffold, which is ongoing here: #3453

Next up is rebasing, fixing and PRing the authenticated views and adjusting the current designs to the new branding.

johanstokking commented 3 years ago

@kschiffer what is the status here?

kschiffer commented 3 years ago

The Account App has been introduced in 3.11. Currently still missing are:

johanstokking commented 3 years ago

OK. https://github.com/TheThingsNetwork/lorawan-stack/issues/488 is already closed it seems.

This has been a big issue. Can you file one or two new issues for the above to replace this one, so it can be closed?