Skip to content

Allow server admins to retrieve an auth token for any other user#366

Closed
janl wants to merge 5 commits into
apache:1.x.xfrom
janl:feature/delegated-session-req
Closed

Allow server admins to retrieve an auth token for any other user#366
janl wants to merge 5 commits into
apache:1.x.xfrom
janl:feature/delegated-session-req

Conversation

@janl

@janl janl commented Nov 8, 2015

Copy link
Copy Markdown
Member

First proposal is a new endpoint /_login_as/, but could be any
other endpoint, even included in /_session, this is just to demonstrate
the feature.

On success, the endpoint returns a JSON object that looks like this:

{"auth_token": "amFuOjU2M0U0MDREOsGlD2HF6fday16PZGb0vIMDkWCw"}

It returns 404 for nonexistent user or when in admin party mode.

One use-case for this is building delegated authentication mechanisms
in middleware on top of CouchDB (like Oauth), while still being able
to log into CouchDB via it's native auth mechanisms without having
to proxy all requests and their variations, for e.g. replication.

This implementation is for 1.x.x for now (don't judge me), I'll
also supply a 2.x version.

First proposal is a new endpoint /_login_as/<username>, but could be any
other endpoint, even included in /_session, this is just to demonstrate
the feature.

On success, the endpoint returns a JSON object that looks like this:

  {"auth_token": "amFuOjU2M0U0MDREOsGlD2HF6fday16PZGb0vIMDkWCw"}

It returns 404 for nonexistent user or when in admin party mode.

One use-case for this is building delegated authentication mechanisms
in middleware on top of CouchDB (like Oauth), while still being able
to log into CouchDB via it's native auth mechanisms without having
to proxy all requests and their variations, for e.g. replication.

This implementation is for 1.x.x for now (don't judge me), I'll
also supply a 2.x version.
@kxepal

kxepal commented Nov 8, 2015

Copy link
Copy Markdown
Member

Basically that's how Proxy Auth works and should be used for.

@kxepal

kxepal commented Nov 8, 2015

Copy link
Copy Markdown
Member

To make Proxy Auth works only for admins, just require secret for requests and don't share it with anybody else.

@janl

janl commented Nov 8, 2015

Copy link
Copy Markdown
Member Author

@kxepal not quite, I do want to hand out auth tokens for existing CouchDB users, not populate a userCtx from an external source.

@kxepal

kxepal commented Nov 8, 2015

Copy link
Copy Markdown
Member

@janl ok, than I have to ask what problem you're trying to solve. What is the use case. Because I don't see any difference between Proxy Auth.

In both cases we have some middle ware. In both cases it have super privileges to run auth for any user. In both cases this used to run some requests in the name of some other user. And in both cases you know the user name you want for delegate the auth. The difference is in with what object you runs this delegated auth: with special headers or with a cookie.

@rnewson

rnewson commented Nov 8, 2015

Copy link
Copy Markdown
Member

@rnewson

rnewson commented Nov 8, 2015

Copy link
Copy Markdown
Member

Whichever we use its time for sha-2

@rnewson

rnewson commented Nov 8, 2015

Copy link
Copy Markdown
Member

@kxepal the difference is you don't need to put couch behind a proxy

@kxepal

kxepal commented Nov 8, 2015

Copy link
Copy Markdown
Member

@rnewson Can you elaborate this? Especially, how would you make this works without admin password leak?

@kxepal

kxepal commented Nov 8, 2015

Copy link
Copy Markdown
Member

@janl What did I understand from this:

  1. There are User, App and CouchDB
  2. User runs auth on App as Jan
  3. App requests CouchDB POST /_login_as/jan
  4. CouchDB meanwhile must have this user created
  5. CouchDB returns App auth_token
  6. App returns user auth_token
  7. User sets this auth_token as cookie and starts to communicate directly with CouchDB

Is that correct?

Questions:

  1. For this auth schema it seems I have to mirror App users into CouchDB in order to make them auth and to revoke their token when I remove them. Is that correct?
  2. If its is why User cannot auth directly on CouchDB?
  3. If this auth schema doesn't assumes direct User auth with CouchDB and auth_token expires, what is the workflow for it refresh?

I think some use case would be nice to see.

@rnewson

rnewson commented Nov 9, 2015

Copy link
Copy Markdown
Member

@kxepal in the delegated_auth code, an admin calls _delegated_auth and gets a response body that includes a session cookie. the admin then gives that session cookie to someone else to use. In this case, typically, the 'admin' is a machine.

@KlausTrainer

Copy link
Copy Markdown
Contributor

I'm all +1 for making authentication and authorization more flexible
(and thereby more powerful), given that we don't compromise on security.
This is a nice and simple feature, and I don't see a reason to not
support it.

The feature is complementary to proxy authentication in the following
ways:

  1. It can be used without a proxy.

  2. A corresponding user document is required for successful
    authentication.

  3. There is a possibility to invalidate all of a particular user's
    authentication cookies. (This can be achieved e.g. by setting the
    password field when updating the user document. This works even if
    the password doesn't get changed at all.)

I also have a use case for the feature.

Together with @robertkowalski, I've been working on a small plugin that
provides passwordless authentication by sending the authenticating user
a sign-in link via email [1]. The sign-in link contains an
authentication token, which gets exchanged for a CouchDB authentication
cookie the first time a user follows the sign-in link. Thereby, we want
to make sure that that every authentication token can only be used once,
and that for a given user there can only be one valid authentication
token at a time.

Previously, we would always set a new password for each sign-in, and
thereby involuntarily invalidate all authentication cookies for that
user when creating a new authentication token. This is big show-stopper
given the fact that people nowadays tend to switch between different
devices all the time. Not being able to authenticate with more than one
browser at a time is not acceptable. Aside from that, we however still
want to make it possible to invalidate all of a particular user's
authentication cookies.

Although we found a solution that matches our requirements, it took us
quite some time. Implementing the same functionality would have been way
easier if this feature had been available. The resulting code would be
less complex and therefore easier to understand. I guess I don't need to
explain why this is critical when dealing with sensitive stuff like
authentication.

[1] https://github.com/KlausTrainer/couch_email_auth

@kxepal

kxepal commented Nov 10, 2015

Copy link
Copy Markdown
Member

I'd played around with both @janl and Cloudant solutions and figure out the cases when this feature becomes very useful. Sorry, I have to take back my scepticism (:

However, important difference between these two implementations that Jan require to delegate auth for real and existed CouchDB user while Cloudant version acts more as Proxy Auth - you may delegate auth to any user, even non-existed ones.

With given default 30 days timeout this makes things like token revoke very hard to make. I only found the one way is to change the salt what is basically means that I revoke all the tokens. With Jan's approach I can do this per user basis. In the same time it requires my app to register in CouchDB every user for which I will delegated auth with all the required roles. A little bit more work to do, but it brings more order in the house.

Jan, please continue your work here.

However, it's still not clear how users with delegated auth will handle expired token case (when we cannot prolong it automagically). CouchDB will return 401 please auth, but to auth I have to ask some third party service. Probably couch_httpd_auth with proper authentication_redirect will help here, but it's a feature that is hard to make it work properly /: Thoughts?

@kxepal

kxepal commented Nov 10, 2015

Copy link
Copy Markdown
Member

@KlausTrainer Nice use case! Thanks for sharing (:

@KlausTrainer

Copy link
Copy Markdown
Contributor

@kxepal You're welcome :)

Regarding token revocation with delegated authentication: You probably don't want to care about revoking tokens for individual users. The whole point of delegated authentication is to not care about such issues, I guess ;) You either trust the identity provider completely, or you don't trust it at all. That is, you do want to be able to revoke all tokens that have been issued by the identity provider, without revoking all other tokens (cookies) at the same time.

Btw., regarding delegated authentication, I'd like CouchDB to support JSON Web Tokens (JWT). I created a working prototype for JWT support a while ago, see https://github.com/KlausTrainer/couchdb-couch/tree/jwt. The larger parts that are still missing are tests and documentation. I definitely want to get back to that, but feel like working on 2.0 blockers is a bit more important right now ;)

@kxepal

kxepal commented Nov 10, 2015

Copy link
Copy Markdown
Member

@KlausTrainer Well, if I gave someone auth_token for my CouchDB and this someone started to do bad things there, I would like to make sure I can ban him. Yes, I can do that on my third party app side which runs delegated auth, but until he hold auth_token cookie and it's not expired he is still an active user for CouchDB.

JWT support indeed would be nice. Not only as delegated auth, but as alternative to cookie one (because for some cases auth cookies are hard). But indeed, we need to finish blockers and broken compatibility issues first. However, if we can do both - that would be awesome (:

@janl

janl commented Nov 25, 2015

Copy link
Copy Markdown
Member Author

Glad to see this is useful for people.

@kxepal what should I be working on? My next step would be porting it to 2.x, or is there anything else you’d like to see addressed?

@janl

janl commented Nov 25, 2015

Copy link
Copy Markdown
Member Author

However, it's still not clear how users with delegated auth will handle expired token case (when we cannot prolong it automagically). CouchDB will return 401 please auth, but to auth I have to ask some third party service. Probably couch_httpd_auth with proper authentication_redirect will help here, but it's a feature that is hard to make it work properly /: Thoughts?

I like the redirect bit. Would be a config option, like we do with _session: https://github.com/apache/couchdb/blob/master/rel/overlay/etc/default.ini#L83

Good point, too!

@kxepal

kxepal commented Nov 25, 2015

Copy link
Copy Markdown
Member

@janl

  1. CSRF protection by requiring application/json Content-Type in request
  2. Tests are looks too much commented (:
  3. Admin Party TODO

Also, I thought about differences between your and Cloudant version...I think it's possible to have both behaviours:

  • When you require all users to be created in CouchDB with proper roles (yours version)
  • When you can use generic users with custom roles ala Proxy Auth (Cloudant version)

I think both could be useful depending on the use case and that behaviour could be controlled by some config option. How do you feel with it?

@janl

janl commented Nov 25, 2015

Copy link
Copy Markdown
Member Author

as for revocation as it pertains to this PR: it is no different than the cookie auth we use today. I believe the default timeout for that is 10 minutes: https://github.com/apache/couchdb/blob/master/rel/overlay/etc/default.ini#L85

@kxepal

kxepal commented Nov 25, 2015

Copy link
Copy Markdown
Member

as for revocation as it pertains to this PR: it is no different than the cookie auth we use today.

Yea, and persistent cookies may bite you here. However, that's indeed not related to this PR, but about various possible cookie auth configuration. Users just need to be careful with.

@janl

janl commented Nov 25, 2015

Copy link
Copy Markdown
Member Author

I think both could be useful depending on the use case and that behaviour could be controlled by some config option. How do you feel with it?

I’d prefer to do this one feature at a time, if possible. Definitely outside of this PR.

@kxepal

kxepal commented Nov 25, 2015

Copy link
Copy Markdown
Member

Ok, fine.

@janl

janl commented Nov 25, 2015

Copy link
Copy Markdown
Member Author
  1. CSRF protection by requiring application/json Content-Type in request

janl@b4bb645

  1. Tests are looks too much commented (:

janl@b00b8f2

  1. Admin Party TODO

janl@0b9af68

@janl

janl commented Nov 25, 2015

Copy link
Copy Markdown
Member Author

Anything else? :)

@kxepal

kxepal commented Nov 25, 2015

Copy link
Copy Markdown
Member

Yes: you need to add test/etap/240-login-as.t to Makefile.am or it will be excluded for distcheck and these tests will never run.

@janl

janl commented Nov 25, 2015

Copy link
Copy Markdown
Member Author

Oh, the redirect bit, yes, I’ll do that next.

Yes: you need to add test/etap/240-login-as.t to Makefile.am or it will be excluded for distcheck and these tests will never run.

Of course, thanks :D

@janl

janl commented Nov 25, 2015

Copy link
Copy Markdown
Member Author

hm no, wait, where do I have to add this in Makefile.am? All we call there is $(top_builddir)/test/etap/run $(top_srcdir)/test/etap

@janl

janl commented Nov 25, 2015

Copy link
Copy Markdown
Member Author

oh in etap/Makefile.am

@kxepal

kxepal commented Nov 25, 2015

Copy link
Copy Markdown
Member

Yes, because this call will happens after tarball generation and autoconf won't include that test file there because it operates with the whitelist.

@janl

janl commented Nov 25, 2015

Copy link
Copy Markdown
Member Author

Done

@kxepal

kxepal commented Nov 25, 2015

Copy link
Copy Markdown
Member

Cool! Let's squash all together and I think it's done (: LGFM, thanks!
@rnewson +1?

@janl

janl commented Nov 25, 2015

Copy link
Copy Markdown
Member Author

I’ll have to ponder the redirect thing for a little longer. It would definitely work to keep this in the client, sure more work there, but they could have a logic of “if CouchDB API calls come back 401, talk to the auth server first for a new token”. Then we don’t have to know.

@janl

janl commented Nov 25, 2015

Copy link
Copy Markdown
Member Author

Happy to land this as is, and decide upon the redirect helper later :)

@kxepal

kxepal commented Nov 25, 2015

Copy link
Copy Markdown
Member

Indeed. Just an idea: pass URL for auth page with POST /_login_as and include it into cookie data. When it expires, extract that URL from cookie and redirect to there.

@janl

janl commented Nov 25, 2015

Copy link
Copy Markdown
Member Author

nice one! :)

@KlausTrainer

Copy link
Copy Markdown
Contributor

+1 to all of this :)

@kxepal

kxepal commented Nov 26, 2015

Copy link
Copy Markdown
Member

[off] @KlausTrainer , may be you also backport JWT auth to 1.7 for experiment? (;

@janl

janl commented Nov 28, 2015

Copy link
Copy Markdown
Member Author

Linked 2.x versions above.

@kxepal Can you help me port the 1.x tests to the new testing system? I experience very little experience with that.

@kxepal

kxepal commented Nov 29, 2015

Copy link
Copy Markdown
Member

@janl Sure! Will handle it over today.

@janl

janl commented Dec 9, 2015

Copy link
Copy Markdown
Member Author

@kxepal thank you! :)

@kxepal

kxepal commented Dec 9, 2015

Copy link
Copy Markdown
Member

Ohai. Sorry, I eventually didn't expect such lack of free time week ago. Basically, what I did is turn your PR into pumpkin by dropping etap. Nothing personal, just improvements (:

That only means, that we'll have the one test suite for this feature for both versions. Hold on and await PR for your fork to rebase. Thanks!

@janl

janl commented Dec 10, 2015

Copy link
Copy Markdown
Member Author

@kxepal nothing to wait for as far as I am concerned.

@wohali

wohali commented Mar 19, 2017

Copy link
Copy Markdown
Member

@janl @kxepal This PR has merge conflicts now. It'd sure be nice to see this hit along with the 2.0 version. Are we still motivated to do so?

@wohali wohali added this to the 1.7.0 milestone Apr 30, 2017
@alxndrsn

Copy link
Copy Markdown
Contributor

@wohali @janl I may be interested in reviving this for couchdb 2.x, depending on our roadmap. Are there clear reasons why it stalled previously?

@wohali

wohali commented Mar 5, 2018

Copy link
Copy Markdown
Member

I'm going to close this out, simply because new feature development for 1.x has ended.

I'd still very much like to see #373 and associated PRs get merged into 2.x, and/or Cloudant's delegated_auth repo instead.

@wohali wohali closed this Mar 5, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants