I’d like this to be discussion of different approaches to keeping JWTs fresh, so authenticated requests aren’t failing due to expired tokens.
I am hoping to find some examples of nice patterns that I can adopt for my own Elm applications, as I’ve already tried a few, but haven’t come up with a “clearly best” approach to this.
An example to explain from
Assume you’re using a web-app where the JWT is expired, but can be refreshed:
type JWT = JWT
refreshToken : JWT -> Cmd JWT
refreshTokenAsTask : JWT -> Task Never JWT
Your application is initializing, getting new articles from 2 sources concurrently. This happens not only on initialization, but also every once in a while, depening on user-input:
Your approach amounts to proactively refreshing JWTs based on setTimeout (Process.sleep) from Javascript AFAIK?
Does this work reliably? Have you experienced any hiccups when the device running your app is either idle or throwing a lot of other work through the runtime event-loop?
I think it’s nice that you’ve separated the refresh-cycle for the tokens from the sending of request. If the token is never expired, you never have to recover from token-expired errors, or wait for a token to be available. I am however suspicious of using setTimeout for this kind of thing
I would say the biggest difference between what I’m looking for, and what you’ve described, is that I can only refresh tokens that have expired. There’s no window in between. One token at the time.
BTW, I was only linking the “Get refresh token and resend original request” topic as I though it might help to short-circuit having some of that conversation again - rather than proposing that my posts there are the solution.
I have to admit, my approach has not really been production tested, so I think in practice a failure to refresh before token expiry will happen occasionally. Not from “throwing a lot of other work through the runtime event-loop”, as I tend to refresh well before the token expiry - at least 30 seconds. But device going idle could easily be a problem.
One possible solution, might be to run a periodic timer of say 1 second. And re-check the current time vs token expiry time. Then if a device goes idle, it will re-check its token 1 second after waking up, and this could trigger a refresh. Of couse, an HTTP request could easily happen in that 1 second too…
Constrained by the back-end? So I guess no choice but to figure out a way of replaying requests.
I wonder, could an HTTP Task be written such that when it produces a Result of Err, that the Err contains a copy of the original Task, such that it can be replayed?
Which is totally appropriate Read through it to get some input!
Yes - This is why I’d love to see if other people have done anything in the same space, so I don’t have to think as hard about it
I was hoping this thread could be about exploring what I described in the initial post
I have implemented this design before. You can totally andThen your way to “success”, but the problem is that if multiple things run concurrently, they will all try to refresh the token independently. I need a nice way to coordinate this effort - for example:
The first ExpiredTokenError sets the app in a “refreshing state”
Every consecutive failure after the first enqueues a retry for itself
New authenticated-requests are kept pending until the “refreshing state” is done. Then they are batched along with retries.
This works kinda nicely, but as you can imagine, it got a bit complicated
Assume you’re using a web-app where the JWT is expired , but can be refreshed:
So you can refresh an expired token without any additional authentication? So what is the point of the token having an expiry then? I could see that maybe you want to keep open the option of expiring tokens in the future. Even still, why not just ignore the expiry on the backend for now?
We have an application in which you can refresh a token that is not expired (but may be close to the expiry time). The token expires 8 hours after you login (or refresh it). Ten minutes before your token will expire a pop-up dialog explains the situation to the user with a button to refresh the token now. But if, say, you’re out of the room and come back 11 minutes later, your token will be expired (and the refresh button will no longer be there). In this case, you have to login normally again.
Sorry I realise this doesn’t actually answer your question.
I am guessing he has 2 tokens, an auth token and a refresh token. The auth token might expire but the refresh token is still valid. An for some reason, a back-end that only lets you refresh after the auth token has expired.
That does seem unusual to me. Often a JWT token cannot be revoked, once signed it is valid until its expiry. The back end typically does not keep track of what auth tokens it has given out - that’s kind of the point of JWT, to allow stateless authentication. Back-end usually would have no idea if a refresh is requested whilst one or more auth tokens are still within their expiry.
In my case the api tokens expire, but new ones can be obtained using a secret (for server side apps that can keep a secret) I implemented it as a AWS Lambda task with Javascript, but when I had to start managing multiple callbacks - precisely because of this retry behavior - I tried rewriting in Elm (Definitely not the fastest way to solve the problem, if there was any doubt - I spent a lot of time refactoring this)
For something that basically has one job, it was possible to express it as a series of nested applications -
Lambda runtime system (fake Http in particular), working with Javascript host and port interfaces.
Handler application that managed the token and token updates, along with a list of
States from which a request could be generated if they had to wait on authentication or re-authentication.
If a State received an authentication failure to it’s query, it would tell Handler to redo authentication, and the state would be put aside for later.
We use short-lived tokens, 15 minutes. Tokens are issued with an associated UUID, which is present in claims as well as in the database. A user can refresh an expired token if it’s UUID matches the most recent issued token from authentication or refresh.
Authentication and refresh overwrites this UUID, which will invalidate any session that was previously running within a 15 minute window. I suppose my example (and how I’m doing this) is not very relatable to how others are doing this
It’s really the Elm-logic related to refreshing token I’d like to discuss, nevermind the exact details - As long as it fits somewhat within the parameters of the example I suggested
One possible solution, might be to run a periodic timer of say 1 second. And re-check the current time vs token expiry time. Then if a device goes idle, it will re-check its token 1 second after waking up, and this could trigger a refresh. Of course, an HTTP request could easily happen in that 1 second too…
Do you mean sending a request every second? (it is not possible to check current time against token expiry time on the frontend - at least not reliably)
I had issues with users with the correct time on their computers - but the wrong timezone - which meant checking current time against token expiration time always showed the token as expired - anyway one can not rely on the frontend to know the time.
I didn’t mean to send a request every second, just to compare the system time to the timestamp on the token once a second.
But I see your point. I am used to unix boxes where the hardware clock is always running in UTC, so a Posix timestamp should always return a time in UTC. I guess Windows boxes are still doing that thing where they run the hardware clock in local time…?
You could get the issued at timestamp from the token, subtract from the expires at time, to get the life time of the token. Also record the local timestamp at the time you first receive the token, and by adding the life time of the token to that, get a reasonable approximation of the expiry time. Maybe take 30 seconds off that to have a very high chance that your approximation falls ahead of the real expiry?
===
The docs for Time.Posix do indicate that you should get the same timestamp everywhere, irrespective of time zone:
“A computer representation of time. It is the same all over Earth, so if we have a phone call or meeting at a certain POSIX time, there is no ambiguity.”
You are correct in guessing it was a windows problem - posix should be the same - if you have the correct local time on your (windows) machine (but if the machine does not know the correct time then POSIX is also going to be wrong).
You can try this by changing the timezone on a win machine - but keep the same time (same clock). Date.now() will change - a user might adjust the clock without noticing the wrong timezone - which is what happened in my case.
The approach you mention with creating a local timestamp and comparing how much time elapsed should work (for bonus accuracy one can use a web-worker - to get more reliable intervals). But this seems like a patch to an architecture issue?!
A issue I had in my use cases was also - should we keep the user logged in if he/she moves away from the terminal, for a longer period of time? (one solution is to instruct the user to logout - but in a chaotic environment - such as a hospital this might not work - showing a modal - requesting the user to once again input credentials seems like the way to go)
A mixed approach could be to prolong the token only if the user is active and let it expire if he/she is not active - but this depends on the particulars of the environment and application …
I have a feeling the discussion I’ve proposed isn’t happening, and it’s probably because of my example. I’m unsubscribing from notifications now, and would appreciate if we can end it there.
I’ll try to do this sort of thing differently next time!