ueberauth / guardian

Elixir Authentication
MIT License
3.44k stars 381 forks source link

Setting TTL with encode_and_sign #153

Closed vheathen closed 7 years ago

vheathen commented 8 years ago

Hello!

I'm trying to generate a token with non-default TTL.

iex(1)> alias App.{Repo, Device, DeviceRegistration.Monitor}
[nil, nil, nil]
iex(2)> device = Repo.get_by(Device, id: "db749ee8-1e9f-11e6-9d47-4b5e65de4b92")
[debug] QUERY OK db=2.9ms decode=0.2ms queue=0.2ms
SELECT d0."id", d0."is_enabled", d0."user_id", d0."inserted_at", d0."updated_at" FROM "devices" AS d0 WHERE (d0."id" = $1) [<<219, 116, 158, 232, 30, 159, 17, 230, 157, 71, 75, 94, 101, 222, 75, 146>>]
%App.Device{__meta__: #Ecto.Schema.Metadata<:loaded>,
 id: "db749ee8-1e9f-11e6-9d47-4b5e65de4b92",
 inserted_at: #Ecto.DateTime<2016-05-20 15:31:01>, is_enabled: true,
 updated_at: #Ecto.DateTime<2016-05-20 15:31:01>,
 user: #Ecto.Association.NotLoaded<association :user is not loaded>,
 user_id: nil}
iex(3)> ttl = Application.get_env(:app, :reg_ttl)
{30, :minutes}
iex(4)> claims = Guardian.Claims.app_claims |> Guardian.Claims.ttl(ttl)
%{"exp" => 1463768778, "iat" => 1463766978, "iss" => "StreamTo.TV",
  "jti" => "7dc915a5-ff0b-4d41-bc1b-b19804550897"}
iex(5)> exp_at = claims["exp"]
1463768778
iex(6)> %{code: code, exp: old_exp_at} = Monitor.device_added(device.id, exp_at)
%{code: "LCBZH6WW", exp: 1463768716}
iex(7)> ttl = {old_exp_at - :os.system_time(:seconds) - 1, :seconds}
{1709, :seconds}
iex(8)> claims = Guardian.Claims.app_claims |> Guardian.Claims.ttl(ttl)
%{"exp" => 1463768725, "iat" => 1463767016, "iss" => "StreamTo.TV",
  "jti" => "294a5c14-f5e4-4b57-9f30-e6551825ccaf"}
iex(9)> {:ok, token, _full_claims} = Guardian.encode_and_sign(device, :token, claims)
{:ok,
 "eyJhb...<skipped>...Zv3w",
 %{"aud" => "Device:db749ee8-1e9f-11e6-9d47-4b5e65de4b92", "exp" => 1464026216,
   "iat" => 1463767016, "iss" => "Issuer",
   "jti" => "294a5c14-f5e4-4b57-9f30-e6551825ccaf", "pem" => %{},
   "sub" => "Device:db749ee8-1e9f-11e6-9d47-4b5e65de4b92", "typ" => "token"}}
iex(10)> 

As you can see it doesn't work. Is there a way to manually set TTL for a token available?

Thank you and best regards!

hassox commented 8 years ago

@vheathen not sure I'm following your example. When you use the ttl function you're effectively asking for '30 minutes after the issue time'. If you want to set the ttl manually I believe you can just set the exp and iat in your encode_and_sign call.

Guardian.encode_and_sign(device, :token, %{"exp" => 123456432, "iat" => 124343534})
vheathen commented 8 years ago

@hasson, thanks a lot for the answer!

When you use the ttl function you're effectively asking for '30 minutes after the issue time'.

Yes, you are absolutelly correct. But if you check result token fields you find that token's TTL is default 3 days and not 30 minutes as I believe it supposed to be:

1464026216 - 1463767016 = 259200 / 60 / 60 / 24 = 3 (days)

Even in the following case TTL doesn't change:

iex(21)> Guardian.encode_and_sign(device, :token, %{"exp" => 1100, "iat" => 1000})            
{:ok,
 "eyJ...<skipped>...1Tg",
 %{"aud" => "Device:db749ee8-1e9f-11e6-9d47-4b5e65de4b92", "exp" => 260200,
   "iat" => 1000, "iss" => "App",
   "jti" => "c1e911ce-732f-467f-b569-97f5e26be066", "pem" => %{},
   "sub" => "Device:db749ee8-1e9f-11e6-9d47-4b5e65de4b92", "typ" => "token"}}

Token's time fields: %{ ... "exp" => 260200, "iat" => 1000 ... } Still default 3 days from Guardian config.

Thanks!

hassox commented 8 years ago

Hmm... sorry I can't pull up the code right now but if you pass it the ttl in the encode and sign it will work. It should work with a specified exp and iat so I think that's a bug.

 ttl = {old_exp_at - :os.system_time(:seconds) - 1, :seconds}
{:ok, token, _full_claims} = Guardian.encode_and_sign(device, :token, %{ttl: ttl})
vheathen commented 8 years ago

Thank you very much! I just saw an example in Readme

claims = Guardian.Claims.app_claims
         |> Map.put("some_claim", some_value)
         |> Guardian.Claims.ttl({3, :days})

and tried to do the same, but it doesn't work (in the TTL part). I also tried to set exp manually, but also without success. I believe that this doesn't work because as I can see claims = Guardian.Claims.ttl(claims, {3, :days}) is doing the same - changing exp and iat according to ttl map.

The way with adding :ttl key/value directly to the claims map works!

Anyway, thank you very much for your help! I'm a newbee in the Elixir world so my tries to dig into the sources hadn't given me a lot about this topic yet. :)

Hanspagh commented 8 years ago

I just tested both claims = Guardian.Claims.app_claims |> Guardian.Claims.ttl({5, :days}) and {:ok, jwt, _} = Guardian.encode_and_sign( "thinger", "my_type", %{"exp" => exp, "iat" => iat}) and both are them are not working, the exp for both of them are getting overwritten here I am not entirely sure how to fix, we might need to rethink the way we set the ttl

vheathen commented 8 years ago

It works with

claims = Guardian.Claims.app_claims
         |> Map.put(:ttl, {5, :days})
{:ok, jwt, _} = Guardian.encode_and_sign(resource, :token, claims)

So may be as a temporary workaround it's possible to put ttl key\value to the claims map on Guardian.Claims.ttl(ttl) in addition to iat and exp (which I'm using at the moment to not reinvent exp time calculation)?

Update Actually, it's not a problem to use Map.put() but looks a little ugly.

scrogson commented 7 years ago

Fixed by https://github.com/ueberauth/guardian/pull/164