How is the system of having both refresh and access tokens 'safer' compared to just using 1 JWT token?
To my understanding in the first scenario the server would, in case of a valid refresh token, respond with an access token (or not if the previous one has not yet expired). In this case, when a middleman would steal the access token, they will have a very limited time to make use of it (since they are usually short lived).
However: if the middleman would steal the refresh token he would be still be able to request new access tokens for as long as the refresh token is valid.
Since usually both the refresh and access token are stored in e.g. a cookie or localStorage; how is this concept more safe than just using 1 JWT with a longer expiry time?
--
I might be missing something very simple here, but I just can't wrap my head around it. Besides being able to control the 'session' i.e. refresh token validity (since it's stored in memory/DB), I do not see benefits to this.
You are probably confused, because you don't need this whole JWT thing. In your scenario maybe a plain old session id would suffice and would be more secure. JWT is not the holy grail, and is tremendously overused.
It only makes sense to have a refresh token besides an access token if you store them in different ways. Putting them both in localStorage for the same origin makes (almost) no sense, you could just use a longer-lived access token and that's it.
But that's not the point. In that case you don't even need a JWT.
In a typical scenario there is usually an identity provider, a "login server" if you like that issues tokens. This is different from the resource server (very confusingly called a client sometimes), which in practice is your backend application. What happens is you send your unauthenticated users to the identity provider, say example-idp.com. They authenticate (log in) on example-idp.com and establish some kind of a session, for example by a refresh token being set in a httpOnly cookie for example-idp.com. Then your user gets redirected to your application which receives either an access token directly or some kind of a code that it can exchange for an access token. Either way the access token will be stored for the application origin, ie. example-app.com. This can be a cookie, but it usually is stored in localStorage. The reason for that is there might be multiple applications (example-app1.com and example-app2.com), to which the javascript client will have to send this same token, and it could not do that if it was stored in a httpOnly cookie (albeit that would be secure against XSS).
The benefit of this is even if example-app.com is compromised (via XSS) and the attacker compromises a short-lived access token, they still won't be able to gain access to a refresh token, because that's set for a different origin, and probably as a httpOnly cookie. While the access token is still useful, XSS requires victim user interaction (ie. the user has to do something to run the attacker's javascript), so it might be difficult for the attacker to get a new one once the old one expires.
So in short if you don't have all this complexity, and just have a plain server-side application with a javascript client downloaded from the same origin, you do not need any token or JWT, the most secure option is a correctly implemented plain old session id. You might argue that a JWT is stateless, but most applications simply don't need to be stateless (also if you need token revocation, it cannot be stateless anyway). If your application does need statelessness, and you would store the refresh token exactly the same way as the access token, you can just go without a refresh token - but please assess the risks before setting expiry.
Where you really need the complexity of access tokens, renewed via refresh tokens and so on is typically single sign-on scenarios, or if you implement different storage (eg. different origins, and httpOnly vs javascript-accesisble) for the different tokens.