Token based authentication from phx_gen_auth

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
1 Like

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.

1 Like

I didnā€™t know it was already merged. I agree itā€™s still a valid tool.

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.

3 Likes

Itā€™s not about token, itā€™s about JWT versus Phoenix Tokenā€¦

Itā€™s not about Phoenix API, itā€™s about API in general :slight_smile:

And I agree Phoenix is really nice to build API.

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 :slight_smile:

And yes, Phoenix rocks for API development. Itā€™s a pleasure.

1 Like

The problem is how people use jwt for authentication, itā€™s not really done for that.

http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/

Nothing related to Phoenix, except we can use Phoenix Token instead as they are lighter than jwt.

5 Likes

Thank you for the pointers. What solution can we use for API centric/SPA development?

Do Phoenix tokens store information like user ID securely, to easily use it in a Plug?

Thanks!

It stores whatever You wantā€¦

The only difference is Phoenix Token cannot be decrypted client side, while JWT can be.

Isnā€™t that a possible benefit though?

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?

I used to read token expiry (to see if I needed to refresh) in JWT, that is not possible with Phoenix Token.

Oh, thatā€™s a big gotcha then.

No, not really, it was my refresh strategy that was not good.

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.

2 Likes

Well, I had this answer before for a similar question :slight_smile:

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.

2 Likes

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.

6 Likes

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
2 Likes

A-ha! Thanks for the reply - you replied to my comment on Hacker News earlier. :slightly_smiling_face:

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.

This is my boilerplate so far, with the above customizations: GitHub - matteing/elixir-boilerplate-testground: Making a nice boilerplate in Phoenix for API development.

Here is the Plug where I ripped out sessions and tied everything together: elixir-boilerplate-testground/auth.ex at main Ā· matteing/elixir-boilerplate-testground Ā· GitHub

Here is the auth token creation function, with the added encoding step: elixir-boilerplate-testground/user_token.ex at main Ā· matteing/elixir-boilerplate-testground Ā· GitHub

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.

10 Likes

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:

  1. Set your cookie expiration to <60 days (even though our backend session is set to 60 days).
  2. 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.