Packages
pow
1.0.0-rc.3
1.0.39
1.0.38
1.0.37
1.0.36
1.0.35
1.0.34
1.0.33
1.0.32
1.0.31
1.0.30
1.0.29
1.0.28
1.0.27
1.0.26
1.0.25
1.0.24
1.0.23
1.0.22
1.0.21
1.0.20
1.0.19
1.0.18
1.0.17
1.0.16
1.0.15
1.0.14
1.0.13
1.0.12
1.0.11
1.0.10
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
0.1.0-rc.1
0.1.0-alpha.8
0.1.0-alpha.7
retired
0.1.0-alpha.6
0.1.0-alpha.5
0.1.0-alpha.4
0.1.0-alpha.3
0.1.0-alpha.2
0.1.0-alpha.1
0.1.0-alpha
Robust user authentication solution
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/extensions/persistent_session/plug/cookie.ex
defmodule PowPersistentSession.Plug.Cookie do
@moduledoc """
This plug will handle persistent user sessions with cookies.
By default, the cookie will expire after 30 days. The cookie expiration will
be renewed on every request. The token in the cookie can only be used once to
create a session.
## Example
defmodule MyAppWeb.Endpoint do
# ...
plug Pow.Plug.Session, otp_app: :my_app
plug PowPersistentSession.Plug.Cookie
#...
end
## Configuration options
* `:persistent_session_store` - see `PowPersistentSession.Plug.Base`
* `:cache_store_backend` - see `PowPersistentSession.Plug.Base`
* `:persistent_session_cookie_key` - session key name. This defaults to
"persistent_session_cookie". If `:otp_app` is used it'll automatically
prepend the key with the `:otp_app` value.
* `:persistent_session_cookie_max_age` - max age for cookie in seconds. This
defaults to 30 days in seconds.
"""
use PowPersistentSession.Plug.Base
alias Plug.Conn
alias Pow.{Config, Plug, UUID}
@cookie_key "persistent_session_cookie"
@ttl Integer.floor_div(:timer.hours(24) * 30, 1000)
@doc """
Sets a persistent session cookie with an auto generated token.
The token is set as a key in the persistent session cache with
the user struct id.
The unique cookie id will be prepended by the `:otp_app` configuration
value, if present.
"""
@spec create(Conn.t(), map(), Config.t()) :: Conn.t()
def create(conn, %{id: user_id}, config) do
{store, store_config} = store(config)
cookie_key = cookie_key(config)
key = cookie_id(config)
value = user_id
opts = session_opts(config)
store.put(store_config, key, value)
Conn.put_resp_cookie(conn, cookie_key, key, opts)
end
@doc """
Expires the persistent session cookie.
If a persistent session cookie exists it'll be expired, and the token in
the persistent session cache will be deleted.
"""
@spec delete(Conn.t(), Config.t()) :: Conn.t()
def delete(conn, config) do
cookie_key = cookie_key(config)
case conn.req_cookies[cookie_key] do
nil -> conn
key_id -> do_delete(conn, cookie_key, key_id, config)
end
end
defp do_delete(conn, cookie_key, key_id, config) do
{store, store_config} = store(config)
value = ""
opts = [max_age: -1, path: "/"]
store.delete(store_config, key_id)
Conn.put_resp_cookie(conn, cookie_key, value, opts)
end
@doc """
Authenticates a user with the persistent session cookie.
If a persistent session cookie exists, it'll fetch the credentials from the
persistent session cache, and create a new session and persistent session
cookie. The old persistent session cookie and session cache credentials will
be removed.
The cookie expiration will automatically be renewed on every request.
"""
@spec authenticate(Conn.t(), Config.t()) :: Conn.t()
def authenticate(conn, config) do
user = Plug.current_user(conn)
conn
|> Conn.fetch_cookies()
|> maybe_authenticate(user, config)
|> maybe_renew(config)
end
defp maybe_authenticate(conn, nil, config) do
cookie_key = cookie_key(config)
case conn.req_cookies[cookie_key] do
nil -> conn
key_id -> do_authenticate(conn, key_id, config)
end
end
defp maybe_authenticate(conn, _user, _config), do: conn
defp do_authenticate(conn, key_id, config) do
{store, store_config} = store(config)
mod = config[:mod]
store_config
|> store.get(key_id)
|> maybe_fetch_user(config)
|> case do
nil ->
delete(conn, config)
user ->
conn
|> delete(config)
|> create(user, config)
|> mod.do_create(user)
end
end
defp maybe_fetch_user(:not_found, _config), do: nil
defp maybe_fetch_user(user_id, config) do
Pow.Operations.get_by(config, id: user_id)
end
defp maybe_renew(conn, config) do
cookie_key = cookie_key(config)
case conn.resp_cookies[cookie_key] do
nil -> renew(conn, cookie_key, config)
_any -> conn
end
end
defp renew(conn, cookie_key, config) do
opts = session_opts(config)
case conn.req_cookies[cookie_key] do
nil -> conn
value -> Conn.put_resp_cookie(conn, cookie_key, value, opts)
end
end
defp cookie_id(config) do
uuid = UUID.generate()
case Config.get(config, :otp_app, nil) do
nil -> uuid
otp_app -> "#{otp_app}_#{uuid}"
end
end
defp cookie_key(config) do
Config.get(config, :persistent_session_cookie_key, default_cookie_key(config))
end
defp default_cookie_key(config) do
case Config.get(config, :otp_app, nil) do
nil -> @cookie_key
otp_app -> "#{otp_app}_#{@cookie_key}"
end
end
defp session_opts(config) do
max_age = Config.get(config, :persistent_session_cookie_max_age, @ttl)
[max_age: max_age, path: "/"]
end
end