Using Guardian for authentication on subdomains?

I’ve configured a SubdomainRouter to allow people to visit: foobar.localhost:4000.

How can I use Guardian to authenticate people only on that subdomain? End result I’m aiming for is if they login to foobar.localhost:4000 they are not logged in to barbaz.localhost:4000.

Does anyone have any experience doing something like this with Guardian? Or is this automatic because of the way the session info is saved to the cookie? Is the cookie bound to the subdomain automatically?

conn
|> Guardian.Plug.sign_in(user)

You should not really store JWT’s into a cookie, they can get too large, they are designed for direct connections or javascript or so.

If your main and subdomains share a database then just auth through that, don’t use Guardian.
If they do not share a DB then you can have them handshake a token (similar to how OAuth works) and have the user carry that between them.
Etc… a few other ways, depends on the exact setup.

That’s strange, I thought Guardian could also be used for browser authentication.

I’ve been reading this article: Simple Guardian - Browser login


Login
The first part is to login the user. Lets see some code:

def login(conn, params) do  
  case User.find_and_confirm_password(params) do
    {:ok, user} ->
       conn
       |> Guardian.Plug.sign_in(user)
       |> redirect(to: "/")
    {:error, changeset} ->
      render conn, "login.html", changeset: changeset
  end
end  

The only “Guardian” part is the Guardian.Plug.sign_in line. This line generates the JWT, stores it in the session (and on the assigns) and proceeds. At this point, you’re “logged in”.

So even the author of the package suggest saving JWT to the session (cookie). Is this correct?

Guardian is a JWT library for Phoenix.
JWT’s are designed to authenticate and confirm a limited set of specific permissions, designed to help prevent the need to access a database and so forth at remote endpoints, primarily designed for server-to-server authentication carried by an intermediary (the browser).

Although you ‘can’ use it for normal browser authentication it is crazy heavyweight in comparison to standard Phoenix Tokens with no benefits unless you can manage to squeeze out some DB query tests on an incredibly busy site. ^.^

You ‘can’, just be aware that depending on what is stored on it may cause it to get larger then the allowable cookie size as JWT’s are not designed to be stuffed into a cookie.

Instead of using Guardian/JWT for authentication, what would you recommend?

As stated above:
If both ‘sites’ share a database, just use a normal phoenix token that is built in to phoenix.
If not, well the way I’d choose depends on how they communicate and/or what information needs to be transferred between, so more information please? :slight_smile:

Thanks @OvermindDL1 I really appreciate your valuable insight. When you say tokens, do you mean an actual type of instance of something like “Phoenix.Token” or just a random string of letters?

I found this guide: https://blog.codeship.com/ridiculously-fast-api-authentication-with-phoenix/

And it seems they’re just generating a random string.

def registration_changeset(model, params \\ :empty) do
  model
  |> changeset(params)
  |> put_change(:token, SecureRandom.urlsafe_base64())
end

Can you point me in the right direction?

A random token string like that is fine if everything, including the token, is stored in the database (which that article is not really doing it seems), or Phoenix.Token can secure some data in to it, like a user_id or so, basically a reduced JWT (and much reduced in size, but the server needs the phoenix style key, which if both servers are phoenix then all fine). There are lots of ways to do it. :slight_smile:

What information do your two domains need to transfer to verify, or do they share a database?

It’s a single app with custom, user enterable subdomains. I’m thinking of creating a Sessions table and associate user’s with a team to authorize.

Session
user_id:integer
room_id:integer
token:string

Then pass this header with all the requests from my React components. Authorization: Token token="yourtokenhere"

Oh yeah this is precisely what Phoenix.Token was built for then.

So you would advise against a simple string field called token? You recommend using the actual Phoenix.Token module?

A simple randomly generated string is fine if that is stored in the shared DB and checked against and such. Phoenix.Token has TTL and storage of small data other very useful stuff as well though, so you can have a bit of extra security there.

EDIT: Plus if you use a randomly generated string you have to worry about enough entropy, Phoenix.Token handles that for you as well.