Correct strategy for auth in an API - security

I want to design a GraphQL API. This API would be used by some browser apps and also open to be used directly for people that want to create their own scripts/generate reports etc. The API would rely on a third party app supporting Oauth Openid Connect (okta) for user and role management. It would be written in Django.
Because JWT is a recommended way of protecting GraphQL APIs and also because OIDC uses JWT tokens. I thought of a simple way, where the API would simply accept the JWT tokens issued by okta. This works, but I see a lot of latency when API is asking okta validate the token (this latency might be smaller in production, because I'm testing on a free trial auth0 instead of production okta). So I think that maybe my API should issue its own JWT tokens. I can think of three strategies here:
Leave it as is – only use the OIDC JWTs.
Introduce a login mutation or a login REST endpoint, that would accept OIDC above and issue JWTs that can be used for all other operations.
As above, but also allow the direct use of okta's JWTs (I'm not sure if I can implement it with Django's auth system, so that if a token is recognised, the OIDC is not called).
Which of these three is the correct (and maybe intended by the OIDC designers) way to protect my API?

JWT token doesn't need to be validated by Okta (generally by IdP). You just need to get used public key (it can be found as jwks url in discovery response) and then can you can verify signatures without any IdP call.
IMHO you can get 2-4k validations/sec easily.

Related

OAuth clarification

I've followed a training in Go as an introduction to microservices architecture a while ago. Getting back to this project I realise that I need more context as we've been quickly digging into the details of the implementations at the time...
I've drawn a simplified sequence diagram of 2 simple use cases:
The user logs in
The user is already logged in and make a purchase
(you can comment / modify the diagram at your convenience)
https://drive.google.com/file/d/1gWgkhJipUvWrVidkl7YFt_xlDmZYn_CX/view?usp=sharing
Here are the questions I have:
Here we're dealing with user authentication but what about client authentication? In the case of a web front end client, can I imagine storing an api_key and an api_secret in the env variables for the server that will be hosting this client? Because there use cases where the user is not logged but some services still needs to be available, but at the same time I only want my known clients (the web front and the mobile app) to be able to access those services (putting aside API Gateway solutions, and maybe other API Shields which would probably add another security layer against DOS etc.)
If the client logs in using Google/Facebook, the front app will receive an id_token that needs to be passed to the backend which would then verify the token ( https://developers.google.com/identity/sign-in/web/backend-auth ). In this particular case my OAuth API would not be used. Could please you confirm that it's the way it should be handled?
Many thanks.
EDIT 02/05/2022
Intro / Context
First thing first, Authorization is not Authentication.
Authentication is the process of verifying who a user is,
while authorization is the process of verifying what they have access to.
And like #Max said, OAuth is designed to manage Authorization and Open ID Connect (OIDC) is an extension of OAuth to manage Authentication on top of it.
The diagram I've exposed in my question is known in the OAuth world as the password grant, and, from the official documentation :
Because the client application has to collect the user's password and
send it to the authorization server, it is not recommended that this
grant be used at all anymore.
Authorization of my App (to get access to my APIs)
From the API perspective, we just want to ensure that the incoming requests are coming from the server that is hosting the App. So, in my case, it's simple machine-2-machine communication from backend server to backend server and there's no action required from the user. So, I must implement the Client Credentials Flow
...which would lead me to this flow:
https://drive.google.com/file/d/1qE9JpWRSRPa8z5iNxm7ocGkeT0E149Sv/view?usp=sharing (Feel free to comment / rectify )
Authentication of a user
Because OAuth knows nothing about authentication, I need an OIDC flow. The easiest one is based on the Authorization Code Flow with PKCE from OAuth below (only about authorization) ...
... but the difference is that we're passing an additional scope named openid in the authentication request (step 3), when the app performs the 2nd request to the token endpoint (step 7), the auth server returns an ID Token (which is a JWT containing user info in the payload -> authentication) in addition to the access_token (which do not contain user info but just "random string"). There's other OIDC flows with their pros & cons depending on the situation but it's another topic on its own (https://openid.net/specs/openid-connect-core-1_0.html)
User already identified by Google/Facebook
In case the client logs in using Google, the front app will receive an id_token. This token could be sent to the app server which would then make a request to the API Gateway, which then call the Auth api which would be in charge of verifying the token by calling the 3rd party auth server ( https://developers.google.com/identity/sign-in/web/backend-auth ).
In case of Facebook, we get back an access token, so I don't know how to deal with it ...
https://developers.facebook.com/docs/facebook-login/web
https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow
Using Firebase, there's an onAuthStateChanged callback, so from the App perspective it will prevent request without the user being logged in, but from the API perspective, it doesn't guaranty that a request is coming from a logged in user...
https://firebase.google.com/docs/auth/web/manage-users#get_the_currently_signed-in_user
Warning: the answer below is not complete, it only serves to give a rough idea
Intro
OAuth2 is a protocol for authorization.
Grant Types
Over the OAuth2 protocol, you can use one of the "grant types" or "flow", one of these flows is illustrated in the picture you post and named password grant.
Each of these flows is realized for different scenarios, you rightly point the attention to how securely store the password on a web app.
For example for a front-end authentication (javascript / wasm) you can use a PKCE Flow where the secret_id is not used.
Endpoints
On OAuth2 there are two primary enpoints
Authorize endpoint - where you obtain the authorization code
Token endpoint - where you exchange the authorization code for the token(s)
Tokens
There are two types of tokens on OAuth2
Access Token
Refresh Token
The definition of token on OAuth2 is "an opaque string", you don't need to read it.
The access token is used against the API, this token has an expiration date, when it is expired the system use the refresh token to get another access_token whitout user interaction, when the refresh_token expire the user must re-authenticate again.
You can read the content of the access_token (which is a JWT token) from the JWT.io
Scopes
The Access token has, on its body, the scopes (i.e. Read email, read name, etc).
Scope is a mechanism in OAuth 2.0 to limit an application's access to a user's account.
Identity
On top of the OAuth2 are build other protocols OIDC aka IdToken aka Open Id Connect is one of them, in other terms OIDC protocol use the OAuth2 for establish an Authentication.
With the OIDC there is another token the id_token, this token came with the user's info and is NOT used has Authorizization in front the API.
There are also OIDC flows you can use to get the id_token and/or the access_token.
Conclusion
I suggest you read about OAuth2 from the references below and try different flows using the playground
References
Which oauth2 flow should I use
OAuth2
PKCE in more depth
My advice is to start with the data, which is the deeper area of OAuth.
USE AN AUTHORIZATION SERVER
This will enable you to keep your code simple. It will also handle Google / Facebook and many other forms of authentication for you, with zero impact on your code. The Curity Community Edition is a free and developer friendly option, though there are others. eg Keycloak, Ory Hydra.
PROTECT DATA
OAuth primarily gives you modern ways to protect data. Use scopes and claims to protect data across multiple microservices in a zero trust manner while securely maintaining user context. You will also need to manage joining identity and business data.
IMPLEMENT UI FLOWS CORRECTLY
Mobile apps use the AppAuth pattern. The current best practice for browser based apps is a Backend for Frontend approach. Both of these are tricky.
KEEP CODE STANDARDS BASED
All of the above Curity resources are based on OAuth related standards. If followed your apps will stay simple, with portable code, that can also work with other providers.
OAuth is very architectural though, and the best designs take time to learn, but you can learn the intricacies gradually. Our IAM Primer is a good starting point.

User registration + authentication for microservices integrated with kong

I am in the process of developing a node.js microservices-based application. The individual microservices are developed including one that already handles the authentication, authorization and registration processes. I am reviewing using kong as the api gateway but am looking for clarity regarding how I can handle authentication, etc. through kong but still use the user service I already created, which already has a db associated with it.
Ideally, I would prefer if I could have kong pass the authentication information to the user service to validate the user's login credentials (username and password) and have kong generate the jwt token provided the authentication was successful.
Is this possible with kong or would I have to somehow move all of this functionality to kong, authentication, authorization and registration?
Update
I found the following article outlining an approach using a custom Authentication server, but this seems pretty involved: Custom Authentication Service in Kong API Gateway.
In a nutshell, I would like the user to pass a username/password combination to kong, which kong would somehow pass upstream to the endpoint of the user service I already have. This would then validate the user and confirm or deny user/pass is correct. From this point, kong would generate the jwt and return to the user. If this were possible it would be great. If I have a implement a custom authorization service that could work as well, but definitely not preferred.:-)
So, what I would say from actively using Kong in a couple of places, I would suggest that you either use JWTs, and the JWT plugin with Kong, or use the OAuth2 plugin, but a combination of both is (as far as I know) actually not possible with Kong. But, bear with me for a little while.
Using JWTs
For your situation, using a JWT might be a fairly simple solution. The only misconception I see right now in your assumptions is that Kong actually helps you in crafting JWT tokens, which is not the case. Kong can only validate JWT, but not craft them; this has to be done by your own Authorization Service. Then again, how you want to pass the token to the consuming service is again something which depends on the type of the service.
Here, you might want to use an OAuth2 flow, e.g. the Implicit Grant if your client is an SPA, or some other grant (such as the Resource Owner Password Grant), but in both cases, Kong will not help you in implementing it. You will need to implement the /authorize (for the implicit grant) or the /token endpoints (for the Resource Owner Password Grant) by yourself. Once more: Kong will only validate the JWT which you craft, and not craft the token. Implementing those end points is not really difficult, but requires reading the RFC 6749 (which is worth the effort).
When crafting your tokens, check the documentation of Kong on what Kong expects from the JWT, especially regarding the iss claim, which has to match a certain property inside your API definition. You can either retrieve that manually for your API, or you can use the Kong Admin API to do that (port 8001). The former is easier, the latter requires some coding but is more reliable and flexible.
Using the OAuth2 Plugin
The solution outline you found in that other question suggests a different solution, where you actually implement a real OAuth2 Authorization Server, and make use of the Kong OAuth2 plugin.
This really requires digging into the OAuth2 spec, and also understanding quite well how Kong does this (which is a little special). The answer I provided in that question outlines the necessary steps.
Common properties of the solutions
With both solutions you get the benefit of Kong validating your calls to your API, and only letting calls with a valid token pass. The JWT option leaves you with crafting your token, and will also require your API backend implementation to inspect the token and take out which claims it needs, such as something like a "User ID" or "Scopes" from the JWT token. Kong will not do that work for you, it will just validate that the JWT token is correctly signed.
The second option offloads that more to Kong, where you, when authorizing the user, add an X-Authenticated-UserId and (optionally) an X-Authenticated-Scope to the opaque token, which in turn is enriched to the call to your backend API via headers. Which means that your API does not have to do any extra work in taking the token apart - the information is readily available via Kong-injected extra headers.

OpenID Connect flow with multiple identity providers?

So I have a .NET Web application that can authenticate via OpenID Connect to multiple identity providers (some internal some external). These providers give me "id tokens".
I also have multiple REST APIs that I'd like to protect. Each of these really only needs the user id as we have an internal authorisation mechanism.
However, it's considered bad practice to pass the id_token to the APIs, and really I should get a access token from somewhere. The question is, where's the best place to get the tokens from? Do I really need something like IdentityServer for this, or should I produce a simple REST API that can unwrap an id_token and wrap the username back up as an access_token with the correct audience?
In fact should each API have its own endpoint that turns an id_token into an access_token for that API? Is that what embedding IdentityServer in each API does?
Sorry for the confusion, but it seems so tempting in this case to just pass the id_token through to each API!

Using JWT with Active Directory authentication in NodeJS backend

I am building an intranet web application consisting of an Angular frontend and a Node.JS backend. The application needs to use the corporate Active Directory for authentication and authorization.
I'm considering how to best implement this in a secure way. I am planning to use the Active Directory node module for actually communicating with the AD to authenticate when the user logs in, and to check security group membership for certain restricted actions, etc.
However, I am not quite sure what is the best way to authorize my backend endpoints. The AD module does not offer any token/ticket, even though I suppose Kerberos is used for the actual authentication process. In other authenticated apps I've developed I've generated a jsonwebtoken when the user logs in, and then passed and verified that token in each backend route, is that a good idea also when authenticating against AD?
EDIT: Second part of question spawned to separate thread: Best practices for server-side handling of JWT tokens
Also, I have a more general concern, regarding what the best practice is for actually verifying tokens. Suppose that the "secret" used for JWT generation is compromised (in my scenario many people may have access to the source code of the system, but not to the system itself). Am I right in believing that a malicious user could then, with only this information, generate a token on behalf of any given user, and without ever authenticating with AD use that token in my API requests? A token is typically generated using jwt.sign(payload, secretOrPrivateKey, options).
Alternatively, suppose a malicious user could get hold of an actual token (before it has expired). To me it seems like instead of having to know a user's username and password, the security is now reduced to having to know the username and the JWT secret. Is this a valid concern and what should I do to prevent this?
My best hope so far is using a server side session to store information about the current user after logging in, so that even if a token is maliciously generated and used when accessing backend endpoints, it would fail unless the user has actually gone through the login route, authenticated with AD and stored some information in the session as a result of this.
I also considered actually authenticating with AD in each API endpoint, but that would require the AD username/password to be sent in every request, which in turn would require that sensitive information would have to be stored in the client's sessionstorage or localstorage, which is most likely a bad idea.
So, questions:
1) Is it reasonable to combine AD authorization with JWT as bearer token or what is the preferred way to build a secure backend + frontend utilizing AD for authentication?
2) If JWT is a good idea, what is the best practice for securing endpoints using JWT? Is using a server side session reasonable?
Interestingly enough I have found tons of examples on how to best implement token based authentication (in general, or with NodeJS specifically), but many of them seem flawed in one way or another.
1) Is it reasonable to combine AD authorization with JWT as bearer
token or what is the preferred way to build a secure backend +
frontend utilizing AD for authentication?
It is reasonable, but if you are already using Kerberos and AD to initially authenticate the user, you might consider using s4u2proxy constrained delegation which allows the service to present the user's service ticket to the KDC and acquire (subject to authorisation checks) a ticket for a backend service (and repeat for as many services are necessary).
If you have a lot of backend services that need to be contacted, a single JWT bearing all the authorization claims needed for all the services to enforce authorization policy may be a better option.
2) If JWT is a good idea, what is the best practice for securing
endpoints using JWT? Is using a server side session reasonable?
General key security practices apply:
Never store keys in the clear in non-volatile storage, anywhere.
Ideally do not store encrypted keys in attached storage on the server where, if the server is compromised, they would be subject to offline attack. Make them available to the host only at server startup.
Ensure key material resides in secure memory so that it cannot be swapped to disk (and/or use encrypted swap).
Use public key algorithms so that no secret key need exist on multiple hosts.
Consider using a hardware security module (HSM).

OpenID Connect with stateless JWT Tokens

I would like to use a self-hosted OpenID Connect (OIDC) server in a combination with JWT as an authorization token (access token in OIDC terms). JWT would be used to protect REST services while the UI are a mix of classical and single-page applications (Angular). This way, the REST layer would be able to do the authorization based on a stateless JWT token so no extra DB connections are necessary, as described here:
https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/
For a single page app, OIDC Implicit Flow is appropriate. However, I see a security problem when Implicit Flow is used in combination with stateless JWT tokens: Tokens are delivered as a fragment part in the URL which means there is no way to remove them (they are easily available in the browser history) nor invalidate them (they are stateless) -> no logout possible.
I see 2 options to mitigate this:
Use a very short-lived tokens (max up to several minutes). This may dramatically hinder usability.
Use an authorization code flow by the means of AJAX. This is not OIDC-compliant but would make a logout possible as tokens would not be exposed in the URL.
The third option would be to give up stateless JWT tokens and use simple bearer tokens with DB checks.
Do I miss something? What would you choose?
one may argue about the risk of fragments ending up in browser history, but "simple" opaque bearer tokens would be subject to the same limitations that you describe for JWT tokens
using a code flow with AJAX is certainly not prevented by the OpenID Connect specification so you could use just that; the implicit flow is only a recommendation for in-browser clients as it optimizes the number of round-trips to get a token to the user agent

Resources