Let’s say you have a system with multiple elixir nodes for user websockets and managing most tasks, and one separate database server separately that it communicates with. You do not want to over-message the main database server.
Upon login, users send their username & pass which is checked (in some manner) against the hash for them in the database. If it passes, they get a token, maybe with a long expiration (to avoid needing to login again too quickly).
On further sign in attempts, they just send the token to sign in or make server requests they are authorized for.
But in the mean time they manually log themselves out, and we want to now consider this token invalid. The token itself cannot be made to expire. Maybe someone has stolen the token. Or they have changed their password and we don’t want someone who has the old token to use it. We don’t want it being used anymore at all.
So what is the best solution?
Chat GPT suggests:
Invalidating a user token in Elixir typically involves a few steps, depending on how you’ve implemented token management in your application. Here’s a general approach:
1. Use a Token Store
If you’re using a stateless JWT (JSON Web Token) approach, you can’t directly invalidate a token since it’s self-contained. Instead, consider maintaining a blacklist or a revocation list.
2. Implement Token Blacklisting
You can implement a simple in-memory store or a database table to manage invalidated tokens. Here’s a basic outline:
Create a Blacklist Table:
If you’re using Ecto, you might create a migration for atoken_blacklist
table.def change do create table(:token_blacklist) do add :token, :string add :inserted_at, :naive_datetime end end
Insert Invalidated Token:
When a user logs out or when you want to invalidate a token, insert it into the blacklist.def invalidate_token(token) do %TokenBlacklist{token: token, inserted_at: NaiveDateTime.utc_now()} |> Repo.insert() end
Check Token Validity:
Before processing requests, check if the token is in the blacklist.def token_valid?(token) do case Repo.get_by(TokenBlacklist, token: token) do nil -> true _ -> false end end
3. Handle Token Expiration
To manage the token’s lifecycle better, consider setting an expiration date on your tokens. You can implement token expiration directly in the JWT payload.
4. Middleware for Token Validation
You can create a plug to check if the token is valid before proceeding with requests.
defmodule MyAppWeb.Plugs.Auth do import Plug.Conn def init(default), do: default def call(conn, _opts) do token = get_req_header(conn, "authorization") |> List.first() |> String.replace("Bearer ", "") if token_valid?(token) do conn else conn |> put_status(:unauthorized) |> json(%{error: "Unauthorized"}) |> halt() end end end
5. Clear Old Tokens
You may also want to implement a cleanup mechanism to remove old tokens from the blacklist periodically.
Conclusion
With this approach, you can effectively manage token invalidation in your Elixir application. The specific implementation details may vary based on your application’s requirements and the libraries you are using.
Is this the general approach?
One could store the blacklist of tokens in a table, but you would need persistent storage, because if a server restarts you can’t lose the blacklist. So mnesia?
The problem is also if you set the expirations of the tokens very long (really really long) you will be stuck with keeping and checking these blacklisted tokens in your table until then as well.
The alternative for persistent login is (if client is app based) having the app cache their username and password internally and submit that automatically on app load again to get a new token each time. They you can just give them a shortlived token (1 day for example) and if you need to blacklist it still, you only must do so for a day.
You can have the client app request a new token every time the token is about to expire as long as the client system is still set to “auto-login”. So the user doesn’t see that they are continuously getting new login sessions/tokens but this is seamless.
And we no longer have to store long lived token blacklist entries across the whole node system.
What do you think? What might a common way be to handle this? Would mnesia be a good simple Elixir data store for the “blacklisted” tokens to refer to? Worse case if it goes down you have a few black listed tokens working again for 1 day before they expire anyway.
Alternatively instead of just a blacklist, we could keep a whitelist and store ALL login tokens given out and consider only the ones we keep as valid (check against that). This would allow more certain “log out of all devices” behavior because then we can just wipe all tokens for that user in storage when needed.
Perhaps that is safer and better.
Thoughts? Thanks.