I am currently building a Node.js application and trying to use JWT to handle sessions. In every implementation that I've seen the refresh-token is stored in a fast database like redis. When the token has expired, the client sends the refresh-token to get the new access-token, then the server checks if the refresh-token is in the database, then generates a new token.
But since the refresh-token must be generated by the server, cannot be tampered with, and we can also check if it has expired, why do we need to store it. If it is for logout then can't we just store the user_id in the database for the people who have been logged in without storing the refresh-token.
I was also thinking along the lines of storing the newest access-token instead of refresh-token in redis as value for the user_id. The reason being that we will generate a new access-token only when the previous one has expired. So the following scenario cannot happen,
User logs in, gets access-token and refresh-token.
Immediately refreshes their token while the previous one is still valid.
Logs out, and uses their old access token.
Since most implementations blacklist only the access-token provided by the client during logout, I believe this scenario can be possible if the REST API is used by the client.
So what is the use of storing the refresh-token, and would storing the access-token instead be beneficial in any way. Some more information regarding the application,
I am using redis for logged in users, and blacklisted access-token (provided during logout).
I am storing both refresh and access tokens in httpOnly cookies, and sending access tokens as bearer tokens.
refresh-token is sent in POST body while refreshing the access token.
I am not using https
Let me answer each of your questions:
"But since the refresh-token must be generated by the server, cannot be tampered with, and we can also check if it has expired, why do we need to store it":
Refresh tokens are meant to have very long expiration times (possibly months), so that users do not have to log into the application frequently (especially in the case of mobile applications). Therefore, if a malicious person steals a user's refresh token, the user's protected information will be exposed for a long time. In case this happens, a considerably secure application should have mechanisms to, for example, detect sudden changes in the IP address of its users and report them. Now, if a user confirms the existence of strange behavior in his account, it is necessary to revoke all his refresh tokens to protect his information, and to do this, it is necessary to have control of the refresh tokens that a certain user has, so they have to be stored.
"If it is for logout then can't we just store the user_id in the database for the people who have been logged in without storing the refresh-token":
You need to store both, both the "user_id" and the refresh tokens, in such a way that you can have a control of all the refresh tokens of a certain "user_id" (as stated above). If you only want to store the "user_id" of the logged in users (without storing refresh tokens), I don't know how you will check if a user is authorized to renew an access token without having to make the user log in.
"I was also thinking along the lines of storing the newest access-token instead of refresh-token...":
By doing this you are losing advantages of the "stateless" approach offered by JWTs (https://restfulapi.net/statelessness), since you are storing a state of them. If you really want something like this, it's probably best to use a "stateful" approach like sessions.
I don't see the reason to do this. Suppose you have a web application and a mobile application connected to a RESTful API, if the user logs into both applications he will have multiple valid access tokens and refresh tokens, so it is perfectly normal for a user to have multiple valid tokens at the same time.
If a user logs out, simply remove the access token and the refresh token from his device, additionally, remove the refresh token from the database. Use short expiration times on access tokens (15 minutes is very common), and so you have no need to store them as they will expire quickly. That's it.
I have read many articles and viewed many videos but there are a lot of contradictions. I try to avoid any external libraries and build the system from scratch, I have read about oAuth 2 but it is more confusing.
This is the flow that I think is ok untill now:
User fills a form using email and password and submits it.
Server verifies the password if it matches and responds back with a httponly cookie with a signed jwt token that expires in like 10
minutes. (I know I have to protect it against csrf attacks)
User gets logged in and every new request he is making to the server he will send the cookie in the header automatically and the
server will verify the token.
Everything is fine but I have encountered some issues and have some questions:
I want the user to stay logged in even after opening a new session so there is no need to login after the token expired or when he closes the browser.
What should happen if the access token expired?
There should be a refresh token attached to the user in database that gets added when the user logs in with an expiration of ex 7 days, then the server will respond with a cookie containing that refresh token?
On the new request while access token is expired,the user will send the refresh cookie to the server, if it matches the user database refresh token,server will respond with a separate cookie that will renew the access token?
If there is a refresh token where should you store it and what format? (cookie,database or where?)
Should I keep the user logged in based on this refresh token cookie?If is it httponly I can't read it and set the state that user is logged in. How should I do it?
I heard about that revoking the jwt token is problematic. How would you fix it?
How would you do this whole thing?Please explain the workflow, I try to avoid localstorage,as I read everywhere that is not safe for sensitive data.
I have implemented and deployed to production systems that do exactly the kinds of things that you are asking about here so I think that I am qualified to provide you with some guidance to solve your particular issues and answer your questions. The flow that you have listed above in the numbered list is definitely the correct path so far. I do understand your confusion going forward from there because there are many different options for how to approach this problem.
In addition to providing a login route that returns a new JWT to the client when the user submits a login form to the server, I would recommend also implementing a token refresh route that accepts a still valid JWT that was received from the initial login process and returns a new JWT with an updated expiration time. The logic for this new token refresh route should first verify that the provided JWT is still valid by matching it with a user in the database. Then, it should generate a new token using the same JWT generation logic as the login route logic. Then, the application should overwrite the access token data in the database for the user replacing the old access token with the newly generated access token. It is not necessary to keep an old access token in the database once it is no longer valid, which is why I suggest simply overwriting it with a new one. Once all of that is finished and successful, you can return the new JWT to the client and then the client should now use that new JWT when making any additional authenticated calls to the server to maintain an authenticated interaction with the server. This logic flow would keep the user logged in, because the client would have a valid JWT before calling the refresh logic and it would have a valid JWT after calling the refresh logic. The user should only be recognized as not logged in and not authenticated if they are no longer able to provide a valid access token that is associated with a user in the database.
As far as cookies go, whichever method that you use for maintaining the cookies on your client should be used for setting the refreshed access token as it is for setting the initial access token that you receive on login. If the server finds that an access token is no longer valid at some point in the future, if for example your client is not used after login until some time after the access token has expired, then the client should recognize a server response indicating that this is the case and present the user with the login flow on the client again so that a new access token can be acquired and stored in a cookie on the client.
I would not worry about revoking JWTs and instead just let them expire if they do and initiate a new login flow if it is found that a JWT has expired. Also, instead of using local storage I would suggest using session storage to store your JWT so that you have it for the duration of your user's session on the website and it is removed as soon as the browser has been closed. This will prevent the JWT from persisting beyond the session and should assuage your fears about saving sensitive data in the session storage. Also, when generating your JWT, you should also make a point of not storing any sensitive data in it because JWTs are easily reverse-engineered. This can also prevent any sort of sensitive data from being exposed on the client.
EDIT:
The key thing to remember when developing your server API is that you should have two different classes of endpoints. One set should be unauthenticated and one set should be authenticated.
The authenticated set of endpoints would not require an access token to be included in the request. An example of this class of endpoint would be your login endpoint, which does not require an access token because it actually generates an access token for you to use later on. Any other endpoint that does not expose sensitive or important information could be included in this class of endpoints.
The unauthenticated set of endpoints would require an access token to be included in the request, and if no access token or an invalid access token is detected the endpoint would respond with a 401 HTTP response code (indicating an unauthorized request). An example of this class of endpoint would be an endpoint that allows a user to update their personal information. Obviously, a user cannot update their own information if they cannot provide credentials to prove that they are the user whose information they are attempting to update. If the client receives a response with a 401 response code, that would be the signal that the client would need in order to tell the user to re-login so that a new valid access token can be retrieved. This possibility can be avoided on the client if the client is programmed to periodically check the expiration of the JWT that is currently being held on the client and initiate an access token refresh, but obviously you should still have logic in place to detect and respond to a 401 response code so that the client user flow is managed properly.
I'm writing an application with a front end in emberjs and backend/server-side in a nodejs server. I have emberjs configured so that a user can login/signup with an 3rd party Oauth (google, twitter, Facebook). I have a backend written in express nodejs server that hosts the RESTful APIs.
I do not have DB connected to emberjs and I don't think I should anyways since it's strictly client side code. I'm planning on using JWT for communicating between client side and server side. When a user logins with their oauth cred, I get a JSON object back from the provider with uid, name, login, access_token and other details.
I'm struggling with picking a strategy on how to handle user signup. There is no signup process since it's OAuth. So the flow is if the user is not in my db, create it. I do not support email/password authentication. What would be the flow when a user signs in with an OAuth provider for the first time? Should emberjs send all the details to the backend on every sign in so that backend can add new users to the db?
What should be part of my JWT body? I was thinking uid and provider supplied access token. One issue I can think of here is that provider specific access token can change. User can revoke the token from provider's site and signs up again with emberjs.
I'm open to writing the front-end in any other javascript client side framework if it makes it easier.
If we're talking about not only working but also secure stateless authentication you will need to consider proper strategy with both access and refresh tokens.
Access token is a token which provides an access to a protected resource.
Expiration here might be installed approximately in ~1 hour (depends on your considerations).
Refresh token is a special token which should be used to generate additional access token in case it was expired or user session has been updated. Obviously you need to make it long lived (in comparison with access token) and secure as much as possible.
Expiration here might be installed approximately in ~10 days or even more (also depends on your considerations).
FYI: Since refresh tokens are long lived, to make them really secure you might want to store them in your database (refresh token requests are performed rarely). In this way, let's say, even if your refresh token was hacked somehow and someone regenerated access/refresh tokens, of course you will loose permissions, but then you still can login to the system, since you know login/pass (in case you will use them later) or just by signing in via any social network.
Where to store these tokens?
There are basically 2 common places:
HTML5 Web Storage (localStorage/sessionStorage)
Good to go, but in the same time risky enough. Storage is accessible via javascript code on the same domain. That means in case you've got XSS, your tokens might be hacked. So by choosing this method you must take care and encode/escape all untrusted data. And even if you did it, I'm pretty sure you use some bunch of 3rd-party client-side modules and there is no guarantee any of them has some malicious code.
Also Web Storage does not enforce any secure standards during transfer. So you need to be sure JWT is sent over HTTPS and never HTTP.
Cookies
With specific HttpOnly option cookies are not accessible via javascript and are immune to XSS. You can also set the Secure cookie flag to guarantee the cookie is only sent over HTTPS.
However, cookies are vulnerable to a different type of attack: cross-site request forgery (CSRF).
In this case CSRF could be prevented by using some kind of synchronized token patterns. There is good implementation in AngularJS, in Security Considerations section.
An article you might want to follow.
To illustrate how it works in general:
Few words about JWT itself:
To make it clear there is really cool JWT Debugger from Auth0 guys.
There are 2 (sometimes 3) common claims types: public, private (and reserved).
An example of JWT body (payload, can be whatever you want):
{
name: "Dave Doe",
isAdmin: true,
providerToken: '...' // should be verified then separately
}
More information about JWT structure you will find here.
To answer the two specific questions that you posed:
What would be the flow when a user signs in with an OAuth provider for
the first time? Should emberjs send all the details to the backend on
every sign in so that backend can add new users to the db?
Whenever a user either signs up or logs in via oauth and your client receives a new access token back, I would upsert (update or insert) it into your users table (or collection) along with any new or updated information that you retrieved about the user from the oauth provider API. I suggest storing it directly on each users record to ensure the access token and associated profile information changes atomically. In general, I'd usually compose this into some sort of middleware that automatically performs these steps when a new token is present.
What should be part of my JWT body? I was thinking uid and provider
supplied access token. One issue I can think of here is that provider
specific access token can change. User can revoke the token from
provider's site and signs up again with emberjs.
The JWT body generally consists of the users claims. I personally see little benefit to storing the provider access token in the body of a JWT token since it would have few benefits to your client app (unless you are doing a lot of direct API calls from your client to their API, I prefer to do those calls server-side and send my app client back a normalized set of claims that adhere to my own interface). By writing your own claims interface, you will not have to work around the various differences present from multiple providers from your client app. An example of this would be coalescing Twitter and Facebook specific fields that are named differently in their APIs to common fields that you store on your user profile table, then embedding your local profile fields as claims in your JWT body to be interpreted by your client app. There is an added benefit to this that you will not be persisting any data that could leak in the future in an unencrypted JWT token.
Whether or not you are storing the oauth provider supplied access token within the JWT token body, you will need to grant a new JWT token every time the profile data changes (you can put in a mechanism to bypass issuing new JWT tokens if no profile updates occurred and the previous token is still good).
In addition to whatever profile fields you store as claims in the JWT token body, I would always define the standard JWT token body fields of:
{
iss: "https://YOUR_NAMESPACE",
sub: "{connection}|{user_id}",
aud: "YOUR_CLIENT_ID",
exp: 1372674336,
iat: 1372638336
}
For any OAuth workflow you should definitely use the passportjs library. You should also read the full documentation. It is easy to understand but I made the mistake of not reading the the whole thing the first time and struggled. It contains OAuth Authentication with over 300 Providers and Issuing Tokens.
Nevertheless, if you want to do it manually or want a basic understanding, here is the flow that I'd use:
Frontend has a login page listing Sign-in with Google/Facebook etc where OAuth is implemented.
Successful OAuth results in a uid, login, access_token etc. (JSON object)
You POST the JSON object to your /login/ route in your Node.js application. (Yes, you send the whole response regardless if it's a new or existing user. Sending extra data here is better than doing two requests)
The backend application reads the uid and the access_token. Ensure that the access_token is valid by following (https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#checktoken) or asking for user data from the provider using the access token. (This will fail for invalid access token since OAuth access tokens are generated on a per app/developer basis) Now, search your backend DB.
If the uid exists in the database, you update the user's access_token and expiresIn in the DB. (The access_token allows you to get more information from Facebook for that particular user and it provides access for a few hours usually.)
Else, you create a new user with uid, login etc info.
After updating the access_token or creating a new user, you send JWT token containing the uid. (Encode the jwt with a secret, this would ensure that it was sent by you and have not been tampered with. Checkout https://github.com/auth0/express-jwt)
On the frontend after the user has received the jwt from /login, save it to sessionStorage by sessionStorage.setItem('jwt', token);
On the frontend, also add the following:
if ($window.sessionStorage.token) {
xhr.setRequestHeader("Authorization", $window.sessionStorage.token);
}
This would ensure that if there is a jwt token, it is sent with every request.
On your Node.js app.js file, add
app.use(jwt({ secret: 'shhhhhhared-secret'}).unless({path: ['/login']}));
This would validate that jwt for anything in your path, ensuring that the user is logged-in, otherwise not allow access and redirect to the login page. The exception case here is /login since that's where you give both your new or unauthenticated users a JWT.
You can find more information on the Github URL on how to get the token and to find out which user's request you are currently serving.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
(spawned from this thread since this is really a question of its own and not specific to NodeJS etc)
I'm implementing a REST API server with authentication, and I have successfully implemented JWT token handling so that a user can login through a /login endpoint with username/password, upon which a JWT token is generated from a server secret and returned to the client. The token is then passed from the client to the server in each authenticated API request, upon which the server secret is used to verify the token.
However, I am trying to understand the best practices for exactly how and to what extent the token should be validated, to make a truly secure system. Exactly what should be involved in "validating" the token? Is it enough that the signature can be verified using the server-secret, or should I also cross-check the token and/or token payload against some data stored in the server?
A token based authentication system will only be as safe as passing username/password in each request provided that it's equally or more difficult to obtain a token than to obtain a user's password. However, in the examples I've seen, the only information required to produce a token is the username and the server-side secret. Doesn't this mean that assuming for a minute that a malicious user gains knowledge of the server secret, he can now produce tokens on behalf of any user, thereby having access not only to one given user as would be the fact if a password was obtained, but in fact to all user accounts?
This brings me to the questions:
1) Should JWT token validation be limited to verifying the signature of the token itself, relying on the integrity of the server secret alone, or accompanied by a separate validation mechanism?
In some cases I've seen the combined use of tokens and server sessions where upon successful login through the /login endpoint a session is established. API requests validate the token, and also compare the decoded data found in the token with some data stored in the session. However, using sessions means using cookies, and in some sense it defeats the purpose of using a token based approach. It also may cause problems for certain clients.
One could imagine the server keeping all tokens currently in use in a memcache or similar, to ensure that even if the server secret is compromised so that an attacker may produce "valid" tokens, only the exact tokens that were generated through the /login endpoint would be accepted. Is this reasonable or just redundant/overkill?
2) If JWT signature verification is the only means of validating tokens, meaning the integrity of the server secret is the breaking point, how should server secrets be managed? Read from an environment variable and created (randomized?) once per deployed stack? Re-newed or rotated periodically (and if so, how to handle existing valid tokens that were created before rotation but needs to be validated after rotation, perhaps it's enough if the server holds on to the current and the previous secret at any given time)? Something else?
Maybe I'm simply being overly paranoid when it comes to the risk of the server secret being compromised, which is of course a more general problem that needs to be addressed in all cryptographic situations...
I've been playing with tokens for my application as well. While I'm not an expert by any means, I can share some of my experiences and thoughts on the matter.
The point of JWTs is essentially integrity. It provides a mechanism for your server verify that the token that was provided to it is genuine and was supplied by your server. The signature generated via your secret is what provides for this. So, yes, if your secret is leaked somehow, that individual can generate tokens that your server would think are its own. A token based system would still be more secure than your username/password system simply because of the signature verification. And in this case, if someone has your secret anyway, your system has other security issues to deal with than someone making fake tokens (and even then, just changing the secret ensures that any tokens made with the old secret are now invalid).
As for payload, the signature will only tell you that the token provided to you was exactly as it was when your server sent it out. verifying the that the payloads contents are valid or appropriate for your application is obviously up to you.
For your questions:
1.) In my limited experience, it's definitely better to verify your tokens with a second system. Simply validating the signature just means that the token was generated with your secret. Storing any created tokens in some sort of DB (redis, memcache/sql/mongo, or some other storage) is a fantastic way of assuring that you only accept tokens that your server has created. In this scenario, even if your secret is leaked, it won't matter too much as any generated tokens won't be valid anyway. This is the approach I'm taking with my system - all generated tokens are stored in a DB (redis) and on each request, I verify that the token is in my DB before I accept it. This way tokens can be revoked for any reason, such as tokens that were released into the wild somehow, user logout, password changes, secret changes, etc.
2.) This is something I don't have much experience in and is something I'm still actively researching as I'm not a security professional. If you find any resources, feel free to post them here! Currently, I'm just using a private key that I load from disk, but obviously that is far from the best or most secure solution.
Here are some things to consider when implementing JWT's in your application:
Keep your JWT lifetime relatively short, and have it's lifetime managed at the server. If you don't, and later on need to require more information in your JWTs, you'll have to either support 2 versions, or wait until your older JWTs have expired before you can implement your change. You can easily manage it on the server if you only look at the iat field in the jwt, and ignore the exp field.
Consider including the url of the request in your JWT. For example, if you want your JWT to be used at endpoint /my/test/path, include a field like 'url':'/my/test/path' in your JWT, to ensure it's only ever used at this path. If you don't, you may find that people start using your JWTs at other endpoints, even ones they weren't created for. You could also consider including an md5(url) instead, as having a big url in the JWT will end up making the JWT that much bigger, and they can get quite big.
JWT expiry should be configurable by each use case if JWTs are being implemented in an API. For example, if you have 10 endpoints for 10 different use cases for JWT's, make sure you can make each endpoint accept JWTs that expire at different times. This allows you to lock down some endpoints more than others, if for example, the data served by one endpoint is very sensitive.
Instead of simply expiring JWTs after a certain time, consider implementing JWTs that support both:
N usages - can only be used N times before they expire and
expire after certain amount of time (if you have a one use only token, you don't want it living forever if not used, do you?)
All JWT authentication failures should generate an "error" response header that states why the JWT authentication failed. e.g. "expired", "no usages left", "revoked", etc. This helps implementers know why their JWT is failing.
Consider ignoring the "header" of your JWTs as they leak information and give a measure of control to hackers. This is mostly concerning the alg field in the header - ignore this and just assume that the header is what you want to support, as this avoids hackers trying to use the None algorithm, which removes the signature security check.
JWT's should include an identifier detailing which app generated the token. For example if your JWT's are being created by 2 different clients, mychat, and myclassifiedsapp, then each should include it's project name or something similar in the "iss" field in the JWT e.g. "iss":"mychat"
JWT's should not be logged in log files. The contents of a JWT can be logged, but not the JWT itself. This ensures devs or others can't grab JWT's from log files and do things to other users accounts.
Ensure your JWT implementation doesn't allow the "None" algorithm, to avoid hackers creating tokens without signing them. This class of errors can be avoided entirely by ignoring the "header" of your JWT.
Strongly consider using iat (issued at) instead of exp (expiry) in your JWTs. Why? Since iat basically means when was the JWT created, this allows you to adjust on the server when the JWT expires, based on the creation date. If someone passes in an exp that's 20 years in the future, the JWT basically lives forever! Note that you automatically expire JWTs if their iat is in the future, but allow for a little bit of wiggle room (e.g 10 seconds), in case the client's time is slightly out of sync with the servers time.
Consider implementing an endpoint for creating JWTs from a json payload, and force all your implementing clients to use this endpoint to create their JWTs. This ensures that you can address any security issues you want with how JWTs are created in one place, easily. We didn't do this straight off in our app, and now have to slowly trickle out JWT server side security updates because our 5 different clients need time to implement. Also, make your create endpoint accept an array of json payloads for JWTs to create, and this will decrease the # of http requests coming in to this endpoint for your clients.
If your JWT's will be used at endpoints that also support use by session, ensure you don't put anything in your JWT that's required to satisfy the request. You can easily do this if you ensure your endpoint works with a session, when no JWT is supplied.
So JWT's generally speaking end up containing a userId or groupId of some sort, and allow access to part of your system based on this information. Make sure you're not allowing users in one area of your app to impersonate other users, especially if this provides access to sensitive data. Why? Well even if your JWT generation process is only accessible to "internal" services, devs or other internal teams could generate JWTs to access data for any user, e.g. the CEO of some random client's company. For example, if your app provides access to financial records for clients, then by generating a JWT, a dev could grab the financial records of any company at all! And if a hacker gets into your internal network in anyway, they could do the same.
If you are are going to allow any url that contains a JWT to be cached in any way, ensure that the permissions for different users are included in the url, and not the JWT. Why? Because users may end up getting data they shouldn't. For example, say a super user logs into your app, and requests the following url: /mysite/userInfo?jwt=XXX, and that this url gets cached. They logout and a couple of minutes later, a regular user logs into your app. They'll get the cached content - with info about a super user! This tends to happen less on the client, and more on the server, especially in cases where you're using a CDN like Akamai, and you're letting some files live longer. This can be fixed by including the relevant user info in the url, and validating this on the server, even for cached requests, for example /mysite/userInfo?id=52&jwt=XXX
If your jwt is intended to be used like a session cookie, and should only work on the same machine the jwt was created for, you should consider adding a jti field to your jwt. This is basically a CSRF token, that ensures your JWT can't be passed from one users's browser to anothers.
I don't think I'm an expert but I'd like to share some thoughs about Jwt.
1: As Akshay said, it's better to have a second system to validate your token.
a.: The way I handle it : I store the hash generated into a session storage with the expiricy time. To validate a token, it needs to have been issued by the server.
b.:There is at least one thing that must be checked the signature method used. eg :
header :
{
"alg": "none",
"typ": "JWT"
}
Some libraries validating JWT would accept this one without checking the hash. That means that without knowing your salt used to sign the token, a hacker could grant himself some rights. Always make sure this can't happen.
https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
c.: Using a cookie with a session Id would not be useful to validate your token. If someone wants to hijack the session of a lambda user, he would just have to use a sniffer (eg : wireshark). This hacker would have both information at the same time.
2: It is the same for every secret. There is always a way to know it.
The way I handle it is linked to the point 1.a. : I have a secret mixed with a random variable. The secret is unique for every token.
However, I am trying to understand the best practices for exactly how
and to what extent the token should be validated, to make a truly
secure system.
If you want the best security possible, you should not blindly follow best practices. The best way is to understand what you're doing (I think it's ok when I see your question), and then evaluate the security you need. And if the Mossad want to have access to your confidential data, they 'll always find a way. (I like this blog post : https://www.schneier.com/blog/archives/2015/08/mickens_on_secu.html )
Lots of good answers here. I'll integrate some of the answers I think are most relevant and add some more suggestions.
1) Should JWT token validation be limited to verifying the signature of the token itself, relying on the integrity of the server secret alone, or accompanied by a separate validation mechanism?
No, because of reasons unrelated to the compromise of a token secret. Each time a user logs in via a username and password, the authorization server should store either the token that was generated, or metadata about the token that was generated. Think of this metadata as an authorization record. A given user and application pair should only have one valid token, or authorization, at any given time. Useful metadata is the user id associated with the access token, the app id, and the time when the access token was issued (which allows for the revocation of existing access tokens and the issuing of a new access token). On every API request, validate that the token contains the proper metadata. You need to persist information about when each access tokens was issued, so that a user can revoke existing access tokens if their account credentials are compromised, and log in again and start using a new access token. That will update the database with the time when the access token was issued (the authorization time created). On every API request, check that the issue time of the access token is after the authorization time created.
Other security measures included not logging JWTs and requiring a secure signing algorithm like SHA256.
2) If JWT signature verification is the only means of validating tokens, meaning the integrity of the server secret is the breaking point, how should server secrets be managed?
The compromise of server secrets would allow an attacker to issue access tokens for any user, and storing access token data in step 1 would not necessarily prevent the server from accepting those access tokens. For example, say that a user has been issued an access token, and then later on, an attacker generates an access token for that user. The authorization time of the access token would be valid.
Like Akshay Dhalwala says, if your server-side secret is compromised, then you have bigger problems to deal with because that means that an attacker has compromised your internal network, your source code repository, or both.
However, a system to mitigate the damage of a compromised server secret and avoid storing secrets in source code involves token secret rotation using a coordination service like https://zookeeper.apache.org. Use a cron job to generate an app secret every few hours or so (however long your access tokens are valid for), and push the updated secret to Zookeeper. In each application server that needs to know the token secret, configure a ZK client that is updated whenever the ZK node value changes. Store a primary and a secondary secret, and each time the token secret is changed, set the new token secret to the primary and the old token secret to the secondary. That way, existing valid tokens will still be valid because they will be validated against the secondary secret. By the time the secondary secret is replaced with the old primary secret, all of the access tokens issued with the secondary secret would be expired anyways.
IETF have a RFC in progress in the oAuth Working Group see : https://tools.ietf.org/id/draft-ietf-oauth-jwt-bcp-05.html