YipyipExAuth v0.2.0-alpha.1 YipyipExAuth.Plugs View Source

Function plugs to create and delete sessions. create_session/3 can be used in combination with YipyipExAuth.Plugs.ProcessRefreshToken for token refreshing.

Link to this section Summary

Functions

Create or update a session. If a session exists in the conn, the session is updated, otherwise a new one is created. The session is put on the conn by YipyipExAuth.Plugs.ProcessRefreshToken.

Delete the persistent session identified by the session_id in the access token payload.

Link to this section Functions

Link to this function

create_session(conn, config, extra_access_payload \\ nil, extra_refresh_payload \\ nil)

View Source
create_session(Plug.Conn.t(), YipyipExAuth.Config.t(), any(), any()) ::
  Plug.Conn.t()

Create or update a session. If a session exists in the conn, the session is updated, otherwise a new one is created. The session is put on the conn by YipyipExAuth.Plugs.ProcessRefreshToken.

In both cases, new access / refresh tokens are created and stored in the conn's private map. The server-side session stored in the session store is created / updated as well.

If a new session is created, this plug must be preceded by YipyipExAuth.Utils.set_token_signature_transport/2 and YipyipExAuth.Utils.set_user_id/2 or an error will be raised.

The tokens' signatures are split off and sent as cookies if the session's token signature transport mechanism is set to :cookie. By default, these are http-only strictly-same-site secure cookies.

Optionally, it is possible to store extra payload in the access- and refresh tokens, which can be used to implement things like role-based authorization or forced logout after password change.

Raises on session store errors. No recovery is possible from this error - the session HAS to be stored or there is no point in handing out tokens.

Examples / doctests

use YipyipExAuth.Utils.Constants
alias Plug.Conn
alias YipyipExAuth.Utils
alias YipyipExAuth.Models.{Session, Tokens}
import YipyipExAuth.Plugs

# only available when Mix env = test
alias YipyipExAuth.TestSupport.FakeSessionStore
import YipyipExAuth.TestSupport.Shared

@config YipyipExAuth.Config.from_enum(
          session_ttl: 68400,
          refresh_token_ttl: 3600,
          session_store_module: FakeSessionStore
        )


# error if user id not set for new session
iex> %Conn{} |> Utils.set_token_signature_transport(:bearer) |> create_session(@config)
** (RuntimeError) Set user id using Utils.set_user_id/2

# error if signature transport not set for new session
iex> %Conn{} |> Utils.set_user_id(1) |> create_session(@config)
** (RuntimeError) Set token signature transport using Utils.set_token_signature_transport/2

# creates session if none present in conn
iex> conn = build_conn()
...> |> Utils.set_user_id(1)
...> |> Utils.set_token_signature_transport(:cookie)
...> |> create_session(@config)
iex> %Session{} = Utils.get_session(conn)
iex> %Tokens{} = Utils.get_tokens(conn)

# renews session if present in conn, updating only refresh_token_id, refreshed_at and last_known_ip
# existing session's user id or signature transport will not change despite attempted override
iex> old_session = %Session{token_signature_transport: :bearer, user_id: 43}
iex> conn = build_conn()
...> |> Conn.put_private(@private_session_key, old_session)
...> |> Utils.set_token_signature_transport(:cookie)
...> |> Utils.set_user_id(1)
...> |> create_session(@config)
iex> session = Utils.get_session(conn) |> Map.from_struct()
iex> old_session = Map.from_struct(old_session)
iex> Enum.map(~w(id user_id created_at expires_at token_signature_transport)a, & session[&1] == old_session[&1])
[true, true, true, true, true]
iex> Enum.map(~w(refresh_token_id refreshed_at last_known_ip)a, & session[&1] == old_session[&1])
[false, false, false]

# returns signatures in cookies if requested, which removes signatures from tokens
iex> conn = build_conn()
...> |> Utils.set_token_signature_transport(:cookie)
...> |> Utils.set_user_id(1)
...> |> create_session(@config)
iex> cookies = conn |> Conn.fetch_cookies() |> Map.get(:cookies)
iex> <<access_sig::binary>> = Map.get(cookies, @config.access_cookie_name)
iex> <<refresh_sig::binary>> = Map.get(cookies, @config.refresh_cookie_name)
iex> true = Regex.match?(~r/\w\.\w/,  conn |> Utils.get_tokens() |> Map.get(:access_token))
iex> true = Regex.match?(~r/\w\.\w/,  conn |> Utils.get_tokens() |> Map.get(:refresh_token))

# allows adding extra payload to tokens
iex> conn = build_conn()
...> |> Utils.set_token_signature_transport(:bearer)
...> |> Utils.set_user_id(1)
...> |> create_session(@config, %{much: :extra}, %{really: true})
iex> %{epl: %{much: :extra}} = Utils.get_access_token_payload(conn)
iex> %{epl: %{really: true}} = Utils.get_refresh_token_payload(conn)
Link to this function

delete_session(conn, config)

View Source
delete_session(Plug.Conn.t(), YipyipExAuth.Config.t()) :: Plug.Conn.t()

Delete the persistent session identified by the session_id in the access token payload.

Note that the access token remains valid until it expires, it is left up to the client to drop the access token. It will no longer be possible to refresh the session, however.

Examples / doctests

# instructs browsers to clear signature cookies
iex> build_conn()
...> |> Plug.Test.put_req_cookie(@config.access_cookie_name, "anything")
...> |> Plug.Test.put_req_cookie(@config.refresh_cookie_name, "anything")
...> |> delete_session(@config)
...> |> Conn.fetch_cookies()
...> |> Map.get(:cookies)
%{}