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

API Authentication #385

Closed
koenmtb1 opened this issue May 4, 2020 · 23 comments · Fixed by #624
Closed

API Authentication #385

koenmtb1 opened this issue May 4, 2020 · 23 comments · Fixed by #624

Comments

@koenmtb1
Copy link
Contributor

koenmtb1 commented May 4, 2020

Is your feature request related to a problem? Please describe.
I'm currently trying to give a third party access to my API (authenticated with Kratos, authorization through Keto and Oathkeeper as a gateway).

The documentation describes that “Self-Service User Login and User Registration for API Clients” will be addressed in a future release.

I'd like to discuss how we could implement this. Because the current system uses cookies which doesn't really work for API clients, storing them isn't great because then the third party API wouldn't be stateless and including them in a request just isn't reliable for me.

Describe the solution you'd like
An auth method which is easy for a developer to implement when trying to connect to my API (not specifically mine of course but anyone who is using Kratos to protect an API and a third party needs access to it).

Describe alternatives you've considered
This is what I'd like to discuss, I might be able to PR the solution we find. We have a few "standard" options, but other creative options we can discuss:

1. JWT Tokens

Tokens which contain information about the API. Pretty secure, easy to implement

2. oAuth

Does this in an okay way but what if someone doesn't use oAuth and how would this work for authorization? (I'm not an expert on oAuth so more clarification on this would be great) Very secure, hard to implement

3. Basic Auth

Not really an option IMO due to the lack of security. But could use the technology to send over encrypted keys for example. Not secure, easy to implement

4. Certificates

Sending data across with certificates could be an option we could explore. Unsure

Most of the time the third party would already have an account but it's possible that they don't so how do we differ "APIs keys" from being connected to an account or not being connected to an account.

@aeneasr
Copy link
Member

aeneasr commented May 4, 2020

I'm currently trying to give a third party access to my API (authenticated with Kratos, authorization through Keto and Oathkeeper as a gateway).

Do you plan on doing that with OAuth2? So that a third party acts on behalf of on of your users? Best example is probably a "create your facbook photo backup app" where Facebook asks the user "are you ok with giving all your photos to this app?"?

@koenmtb1
Copy link
Contributor Author

koenmtb1 commented May 4, 2020

I get that part of OAuth, it would be an option but my API allows third parties to perform actions on behalf of users in their system but who aren't registered in my system. (My system supports "alliances" between companies, they can make reservations in each others systems but they all have their own "user system") So the third party system would needs to be able to retrieve "public" data (which is just freely available, no auth necessary), but also perform actions for users who aren't registered with my system.

@aeneasr
Copy link
Member

aeneasr commented May 4, 2020

Ok so in those cases it's not really about third parties consuming your users' data but instead using your app like a database of sorts? So the developer / your customer (who you call third party) stores stuff in your application. So not on behalf of someone but on behalf of themselves, right?

So basically, after registration, you want to be able to show in a UI: "Yo this is your API Key use it to make calls against my backend and store e.g. the orders people in your coffee shop placed".

Am I understanding that correctly?

@koenmtb1
Copy link
Contributor Author

koenmtb1 commented May 4, 2020

Yes but keeping with the coffee shop trend, I am also a coffee shop. There are 20 different coffee shops in the area and they all sell different kinds of coffee, so clients can come to my coffee shop and buy 1 kind of coffee from me but also buy a few from others. (the clients that buy through me all have an account registered in Kratos)

@aeneasr
Copy link
Member

aeneasr commented May 4, 2020

This is too abstract for me :D What I'm trying to drill down on is if you have users in kratos and want third parties to act on behalf of those users in your kratos, or if you have users in kratos and they just interact with your system like they would with a website but through an API.

@koenmtb1
Copy link
Contributor Author

koenmtb1 commented May 4, 2020

Okay so... I think the 2nd option.

Clients that come to my coffee shop have an account which they use to place orders. (aka they can use my website and sign in through my website and place an order for themselves)
But I also have the other coffee shops that will create "orders" (for their clients which I don't know anything about), they can possibly do this with their "own" account but it will happen through an API.

@aeneasr
Copy link
Member

aeneasr commented May 4, 2020

Ok, I see - that makes sense. So what you want is have a token (not a cookie) that is bound to the "client" registered in your Kratos?

@koenmtb1
Copy link
Contributor Author

koenmtb1 commented May 4, 2020

Yes, but part of my question is if having a token that is generated once secure enough?

@aeneasr
Copy link
Member

aeneasr commented May 4, 2020

Typically yes - the session cookie is nothing different. Most API Keys are permanent (think GitHub Personal Access Tokens for example). Since you can revoke the token easily (blacklist) there is no need for a refresh dance.

Most if not all mobile apps allow you to sign in once and re-use the issued token for along period of time, typically the lifetime of your account!

@koenmtb1
Copy link
Contributor Author

koenmtb1 commented May 4, 2020

Okay great, I do see it a lot so would make sense.
How would we implement this? I guess it's best to only generate a key when it's asked for? And can someone create multiple?

And what about the 1st option from before? How could we implement the "perform actions on behalf of a user"? (best to clarify this even though I might not need it, I can possibly implement it so that the full "API Authentication" would be available)

@aeneasr
Copy link
Member

aeneasr commented May 4, 2020

And what about the 1st option from before? How could we implement the "perform actions on behalf of a user"? (best to clarify this even though I might not need it, I can possibly implement it so that the full "API Authentication" would be available)

This would be achieved by combining ORY Hydra with ORY Kratos which enables OAuth2 and OpenID Connect, which are the protocols that should be used here. By the way, don't confuse "hard to implement" with "secure". OAuth2 and OpenID Connect are very hard to get right because it is so damn difficult to implement them. That's why we're building ORY Kratos :)

How would we implement this? I guess it's best to only generate a key when it's asked for? And can someone create multiple?

Yeah so I think the flow starts a bit different than what we use now. So instead of going through a browser flow where we make the init request and redirect to the UI we would probably just make the init request and expect a JSON response with details on how to proceed. Those details would probably contain the CSRF Token and probably more or less the payloads we have right now (forms/fields etc).

The client then renders that data as a form (e.g. in your mobile or native app or your CLI with input prompts) and when filled out sends it using an API request to the endpoint specified e.g. in the action portion of the form payload.

The response, since it's an application/json request, would then include the session token and would not, as it is the case for browsers, initiate an HTTP redirect.

@aeneasr
Copy link
Member

aeneasr commented May 4, 2020

Alternatively we could create a session/fork feature which would allow a user to fork his/her session. This would then create an API token that the user can use to authenticate in systems that work with kratos. This would be more along the lines of the "Github Personal Access Token" as opposed to my previous comment which focuses on login for applications that don't have a browser (native mobile apps, native desktop apps written in C, ...).

@koenmtb1
Copy link
Contributor Author

koenmtb1 commented May 4, 2020

The response, since it's an application/json request, would then include the session token and would not, as it is the case for browsers, initiate an HTTP redirect.

Looks good, but here and for the "perform actions on behalf of a user" part, should we have options about how long that session is valid? And what if a user logs in again because the token wasn't saved? I guess we would have to invalidate that previous session somehow?

Alternatively we could create a session/fork feature which would allow a user to fork his/her session. This would then create an API token that the user can use to authenticate in systems that work with kratos. This would be more along the lines of the "Github Personal Access Token" as opposed to my previous comment which focuses on login for applications that don't have a browser (native mobile apps, native desktop apps written in C, ...).

I'm guessing we can add this as an extra option comparable to the profile? Where users can create and revoke API tokens at any point? Maybe with a name and/or description?

@koenmtb1
Copy link
Contributor Author

I'd like to propose the following:

  • A new settings option (just like the password option) called something like "access tokens". In it a list of existing access tokens are listed with a name and possibly a "last seen" field.
  • An option for API clients and mobile applications to request an access token.
  • Ability to suspend access tokens.
  • Have to whoami route accept the access token in the Authentication header.
  • Somehow able to say how long the token should be active for or if it should be active indefinitely? (still questions here)

What do you think about this? Should anything be different?

@aeneasr aeneasr added this to the v0.5.0-alpha.1 milestone Jul 8, 2020
@aeneasr
Copy link
Member

aeneasr commented Jul 31, 2020

Thank you for all the input! I'm now going to be working on this. The general idea would be to simply return a session identifier (can be used as a Bearer Token). It has the same properties as a session cookie but instead of it being a cookie, it's a pass-by-reference string that you can use against e.g. /session/whoami.

The bearer token will behave exactly like a session cookie. It's valid for the same amount of time like the session cookie. It will be refreshed similarly to the session cookie (#615). It will keep track of the same properties like the session cookie (user agents, IPs, used at, ...).

@aeneasr
Copy link
Member

aeneasr commented Aug 4, 2020

A primary limitation of this approach is that social sign in will not be working with this method due to the fact that OAuth2 is a inherent browser protocol.

This implies that we'll have new API endpoints

  • /self-service/api/flows/login (with and without refresh)
  • /self-service/api/flows/requests/login
  • /self-service/api/flows/registration
  • /self-service/api/flows/requests/registration

which will support the password but not the oidc strategy. We're still figuring out how the oidc strategy would work - it is most likely going to involve including ORY Hydra in the mix and adding OAuth2 Token Exchange to ORY Hydra.

For the password strategy we are going to add two new API endpoints as well

  • /self-service/api/flows/login/strategies/password
  • /self-service/api/flows/registration/strategies/password

which will be able to perform login and registration without a browser involved. This will come in very handy when working with mobile clients who have some serious limitations when it comes to cookie handling, browser handling, and so on.

The result of the authentication process will be a Session Token which can be sent in the HTTP Authorization Header and will yield the same result as calling e.g. /sessions/whoami with a valid cookie.

There's still thinking needed around what the registration endpoint will return when the session hook is missing - in which case we would need to return something indicating that the user now needs to sign in.

Similarly we also haven't figured out yet how to implement the advanced flows (e.g. changing ones password) given that they need a fresh re-auth session and can't use cookies to store state between requests.

@etca2z
Copy link

etca2z commented Aug 11, 2020

If the solution is to combine Kratos and Hydra, just curious why not use the Code Grant + PKCE flow? This is the recommended flow for oidc mobile clients. AppAuth is one oidc client library to implement such flow in iOS and Android apps.

@aeneasr
Copy link
Member

aeneasr commented Aug 12, 2020

Code Grant + PKCE is useful for third party sign in (e.g. "Sign in with Google") but rarely used in first party applications. We'll support OAuth2 Auth Code + PKCE at some point via Hydra though for those who want to use this in first party scenarios or who want to provide social sign in.

@davidthor
Copy link

@aeneasr I came upon this thread while looking for a Personal Access Token feature. We have a need to support authentication from our CLI triggered by a CI system like Github Actions, CircleCI, Jenkins, etc. Obviously username/password works for this, but personal access tokens allow our users to generate tokens specifically for these use cases and manage expiration/rotation more explicitly.

It looks like you mentioned you were working on this, but the feature doesn't seem to be in the PR you cited. Did I miss that feature somewhere, or would that warrant another issue to track and implement?

@aeneasr
Copy link
Member

aeneasr commented Feb 27, 2021

Hey David, we have API auth but it is scoped to user credentials for now. Personal API Tokens are usually something you generate after a user signed in / has a valid session. These tokens might also have their own subject ID. There are currently no real plans to add Personal API Tokens, but adding them should be easy actually as the implementation is pretty much trivial! So we could think about an RFC / design draft

@davidthor
Copy link

Hey David, we have API auth but it is scoped to user credentials for now. Personal API Tokens are usually something you generate after a user signed in / has a valid session. These tokens might also have their own subject ID. There are currently no real plans to add Personal API Tokens, but adding them should be easy actually as the implementation is pretty much trivial! So we could think about an RFC / design draft

Thanks @aeneasr – that would be helpful! It'd be great to allow logged in users to create, rotate, revoke, and even name these tokens, and ideally the login/auth flows from our application can treat these tokens just like a password to maintain a uniform integration experience.

@zepatrik
Copy link
Member

zepatrik commented Mar 1, 2021

Can you maybe create a new issue for that to move the discussion there @davidthor ? Thanks a lot 👍

@aeneasr
Copy link
Member

aeneasr commented Mar 1, 2021

There's also more to it like permissions, when the token was used last, ...

The question also is if this is really something Kratos should solve or if this is another component entirely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants