In my humble opinion, this seems essential to phx_gen_auth if it is to become part of the standard Phoenix framework. A lot of people use Phoenix just for their API, so a lot of people will want to be able to do:
mix phx.new --no-html --gen-auth # I don't know what the actual flag will be
It already is merged in to phoenix and itās still a valid tool even if limited to session based authentication with logins ā especially given how much more diverse, with various different tradeoffs, authentication for apiās is.
Iām often puzzled at the dismissal for APIs and their technologies in this forum. Iāve seen multiple API authentication questions be shot down immediately here: āJWT is unsafeā, ātokens are unsafeāā¦ Thatās understandable, but we have to authenticate our single page apps somehow, and people use tokens.
Itās the difference between theory and pragmatism.
As someone whoās new to Elixir and Phoenix, itās incredibly off-putting and a shame considering how suitable Phoenix is for the task of building APIs. Itās a pleasure.
Thanks for the clarification, itās just been a little frustrating as someone new to the community whoās been trying to implement an auth solution for API use.
Iāve seen tons of questions spiral into theoretical debates and not get solved, and I wish that wasnāt the case, because it can be very difficult for beginners like me to find up-to-date information and pointers.
Iāll likely be writing a tutorial sometime on implementing API auth, once I get a little bit better at Phoenix and Elixir
And yes, Phoenix rocks for API development. Itās a pleasure.
The cool thing about claims on JWT is that you can use them on the client and store arbitrary data (say a user object) and not have to fetch that data on client again, unless to update it regularly.
e.g. you store a token in client, and in first load you have the userās profile already loaded, rather than doing another roundtrip to the server or storing it in local storage.
Afaik they can be decrypted, but not modified, no?
Thatās not true. You just need a binary_to_term implementation and you can read the data just fine from anywhere. By default tokens are just signed but not encrypted. Thereās nothing more secure about phoenix tokens over JWTs. Theyāre just smaller and therefore less overhead if all you do is supply them to clients to send back later.
Itās for sure not convenient to decode a phoenix token on the client side, but itās not impossible. As mentioned itās just base64 encoded binary terms. Base64 decoding is easy in js and for the binary terms there are libraries, which can do that. Thereās no security to be gained by using phoenix tokens over JWTs, just convenience in one or another way.
For SPAs my team just use regular cookie ; it works fine. JWT are not meant for app authentication. Projects/boilerplates with Vue or React using JWT for app auth are everywhere and it is a shame.
Though I guess if you want your API to support token authentication for server-to-server usage, then it feels clumsy to implement both authentications on the same routes. But thatās just a custom plug.
Yes, this is a good point and something that we should look out for on the forums.
I consider myself a bit of a pragmatist and wanted to give my perspective on the JWT for API discussion. Maybe it will be of use to you.
The biggest challenge that I have always faced when authenticating my API is how do I securely initiate the session with a few caveats:
The session can live securely for a long-ish period of time (letās say 1-3 weeks)
The session cannot be intercepted (by typical means, security always hits a wall at some point)
The session can be revoked as needed
I believe that the best way to achieve these things is a cookie-based authentication mechanism. I personally use Pow, but phx_gen_auth also works well for this purpose. Heck, I even used Devise (Ruby) with a cookie-based SSO mechanism to provide the initial authorization. Cookies are great for security, but I donāt really like working with them when possible. The biggest reason is that it can be harder to implement non-web clients like mobile apps or Chrome Extensions.
That gets into the next point: how do we actually authenticate to the API itself? I think JWT is great for this purpose. If thereās an endpoint like /api/token.json, then it can return a short-lived JWT (10-20 minutes?) that is authorized based on the cookie.
All future API calls to endpoints other than /api/token.json could be authorized with that short-lived JWT. The token could be refreshed every X minutes, or when the client sees a particular HTTP code / error message. (Thereās a lot of hand-waving here, that actually gets fairly complex in practice as its easy to shoot yourself in the foot).
JWT vs Phoenix Tokens? I donāt think itās particularly that important tbh. One thing that I like about JWT is that itās implemented in pretty much every language, so I could make an API call to a non-Elixir microservice using the same token that the Elixir service uses. JWT is such low overhead that I would typically reach for it. It might be worth pointing out that both JWT and Phoenix Token supports encrypted payloads that canāt be viewed without the secret. Pretty easy to add if you need that.
tl;dr. I like:
Cookie-based auth using something like Pow or phx_gen_auth
A token endpoint that is authorized with that cookie, if a valid user is found, sign and return a token
All other endpoints (or other services) use the token returned by the endpoint
Refresh the token on some time interval / error condition
A-ha! Thanks for the reply - you replied to my comment on Hacker News earlier.
I ended up going with the phx.gen.auth generator and basically ripping sessions out, while keeping token generation for API authentication. The server just returns a base64 encode of the binary bytes generated as token on the server. On the client, I use next-auth to store the session token and user. Itās surprisingly elegant and Iām happy with it.
I tried Guardian but the implementation was a little messy and the generator was much cleaner. I also found Pow just wasnāt great for API use, too many open ends when it comes to things like email confirmation and whatnot.
I hope my repository serves as a starting point for others looking to retrofit the auth generator for APIs! I managed to do it with a good degree of success.
Iām implementing a similar approach for my teamās Absinthe API. Iām curious about folksā approaches to session expiration.
The plan is to have our Remix app set a cookie to store the bearer token. Iām considering just telling the frontend team the following specs:
Set your cookie expiration to <60 days (even though our backend session is set to 60 days).
This bearer token may be expired at any time (if we detect a security breach). In those scenarios, itās your responsibility to have the user re-authenticate.
Ideally, the frontend app continuously checks for the session cookie expiration and shows an alert to the user that their session will expire shortly and they will be logged-out. Itād be unfortunate if the user submitted a form only to find out theyāre no longer logged-in.