Stateless authorization of API endpoints with express and JWT - node.js

I'm creating a REST API to store some information about some items.
It is nothing highly sensitive or anything but I still want to properly secure it. Also in regards of maybe having to secure something else in the future.
To sign the users in I'm using OIDC with Google and Azure to retrieve user information from the user information endpoint. After that I want to store the user in a database along with their permissions. For them to access the API I want to generate and sign a JWT Access Token and Refresh Token. So far so good.
I want the acces to the API to be stateless (with the Access Token) for scalability. I'm not so much worried about the sign in process being stateless. The refreshing of Access Tokens via the Refresh Token also doesn't have to be stateless, but it would be nice to have.
I was reading through some other questions and articles online regarding XSS and CSRF. To me it all boiled down to two things:
Don't use local or session storage to prevent XSS-Attacks grabbing tokens stored there. Solution seemed to be to use cookies (http only cookies, samesite).
Don't use cookies as to prevent CSRF.
I'm now kind of stuck because the two options seem to recommend not using either.
I thought about how this might be solvable and was reading through OWASP recommendations mentioning generating a fingerprint during sign in and storing it in the JWT as user context.
In my mind I have the following process.
Sign the user in using OIDC and receive information about the user from the user endpoint.
Look up the user in the database and get their permissions.
Create a unique fingerprint for the user.
Store the fingerprint in a hardened cookie (http only, secure, samesite).
Create a JWT Access Token using the users id, permissions and an encrypted string of the fingerprint.
Create a JWT Refresh Token using the users id, permissions and an encrypted string of the fingerprint.
Sign both JWTs.
Return the Tokens to the client with the hardened cookie set.
Now if the user wants to access a protected resource he sets the Authorization Header with the Access Token and sends the request, which will then also include the hardened cookie. The server can decrypt the fingerprint from the Access Token and compare it to the fingerprint from the cookie.
If the user wants to use the Refresh Token to get an expired Access Token the fingerprint would also be validated before issuing a new Access Token.
Access Tokens would be short lived e.g. 15 minutes. Refresh Tokens would be long lived e.g. 1 day.
In my mind this solves both problems:
Leaking of the tokens would be useless without also having the fingerprint from the cookie or the cookie itself.
Leaking the cookie via something like CSRF would be useless as the Tokens would not be available.
Only if an attacker would simultaneously get hold of both he would be able to have access.
My questions are:
Am I overlooking something?
What would be a good way to generate this fingerprint? Use the "sub" from the user endpoint?
Thanks for your help already!

Related

jwt access token and refresh token flow

here is my auth's flow:
The user receives two tokens (access token with expiration time and refresh token without expiration time) after logging in
for each user , The refresh token is stored in the database in a json column called refreshTokens(which is an array).
on the client side, Both access token and refresh token are stored on the local storage.
when user needs to be verified, If the access token is expired, a new access token is created using the refresh token and sent back to the user and keeps the user logged in.
When the user logs out, the refresh token stored in the database (in the refreshTokens list) is removed.
my questions are:
is this flow, secure?
do i need to save refresh token on the cookie or is local storage good enough?
That flow is according to how OAuth works and how tokens can be stored in a secure way, so "yes" to both questions. The part that is missing is how the tokens are obtained in the first place: for that the Authorization Code grant type using PCKE is the preferred way, over the legacy Implicit grant type.
An important part of this flow being secured is that in point 4 you use the list of refresh tokens kept in the database to verify that the RT was not revoked. Other than that it looks ok. You can add more security by adding expiration times to refresh tokens. Then, even if the user doesn't actively log out (you don't clear RTs from the DB), the RT will not be usable after some time.
Keeping the tokens in local storage is good enough in my opinion.
Few stuffs that popped up in my mind while reading this :
Refresh Token also needs expiration time. In fact, I consider refresh tokens as a double edge sword. Imagine a scenario where a person gets access to the refresh token, not only he gets access to the resources, he will practically gain more time with the resources. I prefer to re-generate refresh token along with access token when refresh token is being used to re-generate the access token. And, also set an expiry to it.
It is cool to store the refresh token in database. However. I do have an alternative though, you can easily use RSA algorithm and then do token generation using private key and verify using public key. This is yo mitigate the scenario of needing to store the refresh tokens.
On client side, local storage is a BIG NO from my side. What I prefer is that you use HttpOnly Cookies and set flag as true. HttpOnly Cookies are not rendered in JS and is sent to server securely as per Http protocol. This is fix the chances of compromising tokens.
Your rest concept of it is good enough.

How safe is an acess token?

I have been reading about OAuth 2.0 Authorization Code flow to protect APIs in microservices architectures but I dont understand how an access token issued by the Auth Server is supposed to protect an API hosted in another server.
Is that same access token also kept in the API and when the client tries to access it with the access token issued by the Auth Server, the API checks if contains it? If so, does that mean that the access token is sent both to client and the protected API in the authentication process?
I hope to have explained my problem well. Thanks in advance.
Anunay gave a pretty good analogy of how JWTs work at a high level as portable, trustable identifier, but since OAuth supports more than just JWT authentication it might warrant sharing a bit more detail.
Token introspection
In your question you rightly assumed that tokens need some way of being trusted, and that one such way would be to store the token in a private database and do a lookup whenever a token is presented to determine its validity. You would absolutely be able to instrument a valid OAuth server using a method like this by issuing the token using whatever form you wish and writing an introspection endpoint that performs the lookup. The OAuth spec is intentionally abstract so that the functional behavior of token introspection can take many forms.
One of the reasons for this level of abstraction is because while storing the tokens for direct lookup might be easy, it means that you have to store copies of these tokens in some form in a private database for comparison. This storage would in turn make you a honeypot for bad actors, both internally and externally, who would seek to impersonate your users en-masse. It's for this reason that many implementations of OAuth prefer to issue and validate tokens using public/private key encryption instead of direct lookups. This process is very much like the one Anunay described in his comment in that it issues tokens that are signed with a private key and verified with a public one. With this process, you no longer need to keep everyone's token in a private database, and instead simply need to secure private and public keys that are used to sign and verify tokens respectively.
JSON Web Tokens (JWTs) and reducing number of introspection calls
Anunay's response specifically referred to a common token structure that is generated using public/private key encryption and issued to users, JSON Web Tokens. These tokens are structured in such a way that they include the user information a backend service might need like the User ID, email address, and sometimes more, in a raw format that is directly readable to the backend API. In addition to this raw information however, JWTs include a duplicate copy of the data, but this duplicate copy is private-key encrypted. In order to trust a JWT token, all you have to do is use the public key and ensure that the private-key encoded payload is verifiable by applying the public key to the raw payload. Since public keys rarely change, many backend services cache the keys used for verification and elect not to do a token introspection on the issuing server since they already can verify the payload. This is how you'd optimize throughput on backend services that are protected via OAuth.
Since public keys can only be used to verify payloads and not produce them, these public keys are often broadcast by the servers that issued the tokens allowing anyone to "trust" the tokens it issues if they so choose. To learn more about this process, I'd recommend you research OpenID Connect.
Access token can be understood as an passport that government issue to the citizen based on proof of identity.
When you take it to another country, they look at the document and trust it because they trust the country and you because you are the holder of that document with you details.
They trust the fact that passport cannot be fiddled with and allow you entry
Now for access token, in very simple terms, authorization server verifies the user. Once verified it issues the user a JWT token (Access Token). This token is signed with private key. It has your details and is encoded along with signature. Now you can take this token to any third party who has got the public key and trust the authorization server. Now when you share the access token with this third party, it use public key to verify the token and check for expiry. If valid it allows you in.
So API doesn't really need to talk to auth server or keep any details about the token. All its needs is a public key to decode the token.
Now there are two important things. One if you ever let loose your access token, or some one who is not intended to get hold of your token gets it, he can do what ever he wants and auth server will not be able to do much. However as you see this approach reduces the chattiness of the systems specially microservices.
So to address this we limit the expiry of access token. Like passport, it comes with expiry. Shorter you keep it,user have to go and get the token refreshed with auth server. Every time he does so, auth server gets a change to verify creds and other details. If they do not match access token will not be refreshed.

Should I use OAuth or JWT?

Im building a REST services for existing product. Now to authenticate these, there needs to be some mechanism. To give specifications, I have a Db which stores userid and password . I have to authenticate using these credentials.
In above should I use OAuth or JWT? I prefer to use JWT to generate token first and pass token along every request.
Also From my understanding, I understand OAuth should be used when you have multiple consumers like games/apps using Facebook login. In my case, I don't have any have multiple consumers.
Please advise
Although it is true that OAuth is an authorization framework, it does help with JWT. JWT is a token specification, meaning, how you manage and issue tokens is largely left undefined. For instance, when your token reaches it's expiration, do you want your user to be abruptly logged out? By default, if you're using tokens with a certain duration, this will happen if you're checking for expired tokens, which you should be doing.
An OAuth Authentication server can serve the purpose of issuing an Access Token in JWT, and a Refresh Token. The access token will be included in every request, and the refresh token can be used when the access token is expired or about to expire to acquire a new access token. This is useful when taking into account the potential need to revoke a users access to your application. If you set a short access token life, then you'll be able to revoke access more quickly by removing their refresh token.
The technologies are different and not directly comparable because they solve different problems and are intended for different uses.

JWT, Stateless Authentication, and Security

I am working on an application where scalability is a big concern. In the past I've used session-based authentication, but decided to go with a stateless server this time around in order to facilitate horizontal scaling.
I am not security expert, but in researching JWTs, it began to seem like these are very insecure. The whole reason we hash passwords is so that if our database is compromised, the attacker cannot impersonate a user. With JWT, we store a secret on the server. If the attacker gains access to the secret, can't they impersonate any user they want? Doesn't this mean that using JWTs would have the same level of security as storing plain text passwords?
I have read that people will sometimes use reddis to cross reference JWTs, but then the server isn't stateless, and I fail to see the benefit of using JWTs at all.
Could someone help clarify this issue for me?
Session based authentication systems, at least any that are worth using, also store a secret on the server. Just like the JWT, the secret is used to sign the data stored in the cookie that session based authentication uses. So this is no different than a JWT.
All of this is totally unrelated to password storage, as the password is only used when you don't have a cookie/JWT.
EDIT:
Not sure what to say about using Redis in conjunction with a JWT... What is being stored in Redis, the token? That seems pointless, as all the server needs to know is the secret to decode the token.
Here are some of the benefits to a using a JWT:
It's stateless, as you've already mentioned
It's not subject to CSRF/XSRF attacks. These attacks work by tricking your browser into sending the cookie to a server that didn't generate the cookie. This can't happen w/a JWT b/c the browser doesn't send the JWT automatically like it does w/cookies.
JWT's are standardized. There is a well defined way to generate them, which means that JWT's are more portable and the process has been vetted by the security community.
The server consuming a JWT token (resource server) does not need access to any secret. All it needs is the public key that belongs to the private key with which the token is digitally signed.
The authorization server that issues the token needs to keep its signing key secret obviously. But the nice thing about token based authentication is that this server can be created by an external party with much more resources/expertise to keep these things secret (Google, Facebook, Microsoft etc).
The resource server does not need to check the database to validate the token as you would need in case of username and password. This helps the scalability of the system and takes away a single point of failure.
If a client/user loses the JWT token, an attacker can impersonate the client/user until the token expires. A good reason to keep the lifetime of tokens short.
I don't see the point of storing JWT tokens in in a Reddis cache. There's no need to share tokens between servers as each call comes with a token in the Authorization HTTP header. Storing them in a cache only increases the risk of tokens being stolen.

Understanding authentication flow with refresh and access tokens on nodejs app

I know there are already many posts about Oauth, Oauth2, JWT, etc.. I have read many and I more confused than ever so I am looking for some clarification. I will propose my view on the subject and I hope somebody can tell me if my implementation is secure enough or what I am doing wrong and how to improve it.
I am building an API Rest server for serving my resources to my users. Let's suppose it is a bank app where users can deposit, withdraw and transfer money.
I am using nodejs, hapijs, jsonwebtokens, and bcrypt for my server. I want to implement two token authentication flow (Oauth2).
This is the way I am doing it:
User logs in to the auth server by giving some credentials (username and password).
The server verifies the user's credentials, if they are valid, it will grant access to the user and return a refresh token and an access token.
These tokens are saved into the local storage of the browser or mobile device.
The access token:
is signed as a jsonwebtoken.
contains issued date, expiration date (5 min), user data (id, username).
The refresh token:
is signed as a jsonwebtoken and encrypted with bcrypt.
contains a unique identifier
may contain an expiration date
is saved in the database.
As long as the access token is valid, that means, it has not expired and contains valid user data, the resource server serves the user the requested resources.
When the access token is no longer valid, the auth server requests the client to provide a refresh token in order to issue a new access token
The server receives the refresh token from the user, decrypts it, compares it to the one in the database, checks if it has been revoked, and checks its unique identifier.
If the refresh token passes all tests, the server issues a new access token to the client.
If the refresh token fails one test, the server requests the user to re-authenticate.
Notes: I am trying to avoid the usage of cookies.
Questions:
If the user is able to steal an access token, I guess it can also steal the refresh token. So, how can I make the refresh token more secure?
Is my perspective of the Oauth2 flow correct?
What can I improve?
Am I missing something?
The reason OAuth2 is so confusion to many people is because it uses different authentication flows depending on what kind of client is used.
OAuth2 distinguishes two client type, confidential or public. Next to that, there are 2 grant flows that are redirection based (auth code and implicit) which are meant to be used with a browser or browser control.
The other two flows (resource owner password and client credentials) are meant to be used from non-browser apps (CLI, background services, trusted mobile clients).
I've described the different flows and when to use them in more detail in this answer here.

Resources