satom99

satom99

In a REST API, how do you handle tokens and permissions?

On tokens, once you have retrieved the token from the Authorization header, how do you verify you have issued that token in the past? Do you store it in your database and check whether an entry for it exists (and thus make sure you have indeed issued it) and what user it is liked to? Do you have a separate table for tokens just so you can have numerous tokens for an user, over a single one that is perhaps stored in the users table in a token column? Do you use Phoenix.Token and verify/4 it to get its stored user id and go from there? Why would you use Phoenix over generating a random string of a certain length each time you want to issue a token - how would this be done in Elixir?

On permissions, lets say we have a specific resource post that you want an user to have access to edit but not delete. How do you store and handle this kind of permissions? Perhaps a table named post_permissions with a bit with the user’s permissions? That would fit the case where there’s a single resource to handle, how would it go though for a system where post is just one of the many restricted resources?

Showing Posts 1 to 10

wfgilman

wfgilman

Just use Guardian: GitHub - ueberauth/guardian: Elixir Authentication · GitHub

I am using Guardian to do everything you discuss in your post.

Graeme

Graeme

The library guardian handles most of these things. The token does not have to be stored in a database, instead it is encrypted using a secret key. If someone provides a valid token (ie: it was encrypted with the correct key) then you know it was issued legitimately without checking a database. Unfortunately you usually do need to store some state in the database to handle sign out (or a token being invalidated for other reasons). In the case of Guardian they recommend storing the token in a database so that it can be removed when you revoke it (GuardianDb exists to make this easier.

how do you verify you have issued that token in the past?

You just attempt to decrypt it. If you use JWT there is a standard method for this.

Do you store it in your database and check whether an entry for it exists (and thus make sure you have indeed issued it) and what user it is liked to?

That’s one way to do it. Another way is to encode a date that the token was issued and then just store in the database the date the user last logged out. Any token with a date older than that will be rejected. This means that a logout would kill all tokens for that user (which may be a bug or a feature).

Why would you use Phoenix over generating a random string of a certain length each time you want to issue a token - how would this be done in Elixir?

If you are going to store each token in the database with some metadata anyways then that would probably work fine if it is a sufficiently long cryptographically random string. Honestly I like that technique but if you use a JWT (or similar) then you have the potential for stateless logins. You could have ten load balanced servers and as long as they all have the same secret key they can individually validate tokens and derive the user and their claims without hitting a database or requiring any single point of failure.

How do you store and handle this kind of permissions?

If you use JWT you could use claims for this. From my experience a lot of people end up just rolling their own permissions table that links to users or something similar.

Graeme

Graeme

I agree that the link wfgilman posted solves all of the problems except it is important to keep in mind that the logout technique using the default revoke! is not 100% secure. From the guardian readme:

By default, JWTs are not tracked. This means that after ‘logout’ the token can still be used if it is stored outside the system. This is because Guardian does not track tokens and only interprets them live. When using Guardian in this way, be sure you consider the expiry time as this is one of the few options you have to make your tokens invalid.

That means that if you issue a token that is valid for 10 days and then a user’s cookie gets stolen, they cannot just click logout or change their password or anything in order to secure their account. The token will continue to work for the full 10 days.

wfgilman

wfgilman

Good point. I use GuardianDB to store the tokens and a sweeper to delete all expired tokens. In your api pipeline in the router you can ensure that a resource can be loaded from the token provided (i.e. checked against the database) and invalidate the request if not.

satom99

satom99 OP

Thanks for the replies. :slight_smile: In a previous topic, I was told not to use Guardian over Phoenix.Token when not encoding permissions into the token itself. Which gets me to a few questions; do you agree? In the case I am storing the tokens manually and only need to verify and sign it (with a secret key), wouldn’t Phoenix’ approach just do fine? And in the case I am anyway checking against a database whether a token is still valid, why would I need to encode an user id into the token whilst I can just get what user it’s linked to from the previous validating step? Thus, wouldn’t generating a random string securely do the trick instead?

On both cases, signing and generating a random string, why Phoenix.Token and not Joken or any other library? How would you generate a random token, perhaps doing this or using a more dedicated library?

What are the pros and cons from a security perspective? In what way is a signed token safer compared to a random string?

OvermindDL1

OvermindDL1

Two standard ways when in-system like that. Either put the permissions in the database and look up the user’s permissions when needed, or store the permissions inside the Phoenix.Token directly, saving the database call, however that also means it will not be cleared out if, say, they are removed permission from later, however using very short-lived tokens is perfectly fine for that.

@satom99 is already using Phoenix.Token, which can do everything Guardian can do in this context but without the extra dependency and cost of JSON deserialization

People really need to quit recommending Guardian for areas where it is ill-suited. This is a perfect user-case for what they are already using, Phoenix.Token. Guardian is great for things like passing credentials to a remote non-connected server. If you are using it in the same server then it is just needless overhead.

And doing that you then remove the entire advantage of using Guardian at all (which is to keep you from having to hit a database when doing a cross-site call), why use Guardian at all in this case??

Still hitting the database, you may as well put the permissions in the database itself then and don’t use a token at all but instead just the session to hold the link, of which you can easily wipe if, say, they log out elsewhere so the session is then wiped from the database preventing replay attacks. Guardian on the other hand would let you trivially perform replay attacks, so quit suggesting it please! ^.^;

Stateless logins are nice, but you generally want short-lived tokens then to minimize replay attack abilities, and JWT is useful only if passing to another server; again if you are doing this all in the same server that you control then use Phoenix.Token, it is not as heavy, can hold arbitrary data, is faster, but does require that the server is on the BEAM or so all using the same key, JWT allows you to send data safely from server-to-server.

JWT claims are just simple on/off, fine for very simple things, but not good for more detailed permission types.

Exactly! This is the replay attack, they just replay the old token. If it is all in-system then better to keep it in the database and don’t use tokens at all!

If you are having Guardian hit the database anyway, they why are you using Guardian at all?! It makes no sense!

  • If you are doing the authentication and authorization all in the same server and you’ll be hitting the database anyway, don’t use tokens, just use Phoenix’s Session and the database, you have significantly greater control.
  • If you are doing the authentication and authorization all in the same server and you don’t always need to hit the database for short-lived authorization permissions, use Phoenix.Token and put the allowed permission inside the token itself (like via Phoenix.Token.sign("perms", [:read, :edit]) or whatever, you can encode whatever you want in it in any form that you want).
  • If you are doing authentication on one server and doing authorization on ‘another’ server and those two servers do not use the same secure phoenix keys, then and only then is JWT (Guardian) useful. Don’t use it otherwise, it is not safe and/or not secure in other contexts.
Graeme

Graeme

I probably didn’t make it clear enough, but I was not suggesting or recommending anything. I was trying to offer up information on how one tool addresses the problem (and in fact it seems like it is the most popular and/or well known tool) for the sake of discussion… and it seems to have worked because your post has a lot of great information :stuck_out_tongue:

My question as well, that’s why I thought it was important to point it out. If your logins are no longer stateless (and they probably should not be in this case or even in most cases) then I have no idea why you would want Guardian.

What I was trying to do is explain that systems like Guardian try to simplify authentication by making it stateless, etc but then the simplicity all gets rolled back to re-introduce necessary features (like logout) but you’re still left with the security and efficiency concessions that were made in order to achieve the simplicity that you don’t get anyways.

Furthermore, it’s also worth mentioning that there is a school of thought that JWT itself is completely unsuited for this type of work and that it is only meant for short lived, single use tokens (like you said). If that’s true then it actually sounds like Guardian is fundamentally flawed and almost nobody should use it since their own examples seem to use stateless JWT for persistent single server logins.

Thank you for adding so much to the conversation, OvermindDL1. I learned a lot from your post! I think you made it clear that stateless authentication isn’t really something to strive for in a case like this and that Guardian is not the right tool. Very curious if there are any guardian fans who disagree.

Does this end up being similar to the random string approach except you are letting Phoenix (which just uses Plug) generate a session ID which essentially becomes the random string? I can see that this is a quick and easy way to achieve that because it takes care or storage as well, but I think the default storage system does not persist when you restart your server so everyone would get logged out on restart unless you implement some sort of database-backed session persistence.

And make sure that if you do this you do not also include some sensitive information. It is signed but not encrypted.

This sounds right and I didn’t realize it until reading your post. They should just say that up front in their readme :slight_smile:

OvermindDL1

OvermindDL1

Not so much a random string as it is an encoded cryptographically random binary, so it is better than what most people would ever do. ^.^;

Actually one of its default stores (I think ‘the’ default maybe? Don’t recall) is storing it in an encrypted Cookie, which will persist. I just store a session ID on it, which is generated by my database and can be revoked in an instant at any time. ^.^

Yeah I wish they would… :frowning:

wfgilman

wfgilman

@OvermindDL1 I really appreciate your thoughts here, I learned a lot.

We’re using long-lived tokens which just serializes some basic information, like user ID. We’re checking the token against the database on each request to ensure it’s valid. Guardian works great for this use case because it has a lot of conveniences for verifying the token in the header request and tracking tokens in the database. I would have to roll my own if I was using Phoenix Token. It’s not stateless, but for long-lived tokens, it doesn’t sound like there is a stateless authentication solution for an API.

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
ryanwinchester
apply_graft/2 doesn’t rewrite an add_many sub-workflow’s deps on an add step. Grafted jobs cancel with “upstream job was deleted” Version...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews