tokumei v0.6.4 Tokumei.Session.SignedCookies
Cookie based session storage.
Tokumei.Session.SignedCookies
can be added as a middleware.
A session is a map of binary keys and values.
A session added to a response under the tokumei-session
header will be sent to the client.
A valid session sent from the client is added to the request under the tokumei-session
header.
defmodule MyApp do
use Tokumei.NotFound
use Tokumei.Router
import Raxx.Response
use Tokumei.Session.SignedCookies
@secret "eggplant"
route ["session", first_value] do
:PUT ->
new_session = %{"first" => first_value}
ok("Session set", [{"tokumei-session", new_session}])
end
route ["session", first_value, second_value] do
:PUT ->
new_session = %{"first" => first_value, "second" => second_value}
ok("Session set", [{"tokumei-session", new_session}])
end
route ["session"], request do
:GET ->
session = :proplists.get_value("tokumei-session", request.headers)
value = Map.get(session, "first", "none-set")
ok("Session value: #{value}")
:DELETE ->
new_session = %{}
ok("Session set", [{"tokumei-session", new_session}])
end
end
Responses will have any session data that is set serialized into cookies. Requests will have their session (if any) rebuilt from the cookies they send.
An additional cookie (tokumei.session
) is set to validate the integrity of sessions.
N.B. Stored session data can be viewed by the client.
There are also limitations to the number and size of cookies that a browser will persist.
ALSO, should cookie attribute be captialized Path=/ or path=/
Summary
Functions
Expire the session
Set cookies for a sessions contents
Extract session from signed cookie values
Functions
Expire the session.
Expires a session by resetting the validation cookie with a past expiry date. A list of keys will also be expired, to remove any user preferences from the browser.
# Expire the signature cookie.
iex> Response.ok()
...> |> SignedCookies.delete([], [])
...> |> Map.get(:headers)
...> |> List.last()
{"set-cookie",
"tokumei.session=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; max-age=0; HttpOnly"}
# Expire the cookie for each key.
iex> Response.ok()
...> |> SignedCookies.delete(["foo"], [])
...> |> Map.get(:headers)
...> |> List.first()
{"set-cookie",
"foo=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; max-age=0; HttpOnly"}
Set cookies for a sessions contents
Examples
# Each key value from a session is set as a cookie
iex> Response.ok()
...> |> SignedCookies.embed(%{"foo" => "bar"}, secret: "secret")
...> |> Map.get(:headers)
...> |> List.first
{"set-cookie", "foo=bar; path=/; HttpOnly"}
# A signature is set as a final cookie
iex> Response.ok()
...> |> SignedCookies.embed(%{"foo" => "bar"}, secret: "secret")
...> |> Map.get(:headers)
...> |> List.last
{"set-cookie",
"tokumei.session=foo -- yjdW%2BB03KKAjvcimIsCQbzc7eV4%3D; path=/; HttpOnly"}
Extract session from signed cookie values.
Examples
# Sessions will be extracted from cookies
iex> Request.get("/", [
...> {"cookie", "foo=bar"},
...> {"cookie", "tokumei.session=foo -- yjdW%2BB03KKAjvcimIsCQbzc7eV4%3D"}])
...> |> SignedCookies.extract(secret: "secret")
%{"foo" => "bar"}
# Extra cookies are discarded
iex> Request.get("/", [
...> {"cookie", "foo=bar"},
...> {"cookie", "stealthy=value"},
...> {"cookie", "tokumei.session=foo -- yjdW%2BB03KKAjvcimIsCQbzc7eV4%3D"}])
...> |> SignedCookies.extract(secret: "secret")
%{"foo" => "bar"}
# Missing value will result in no session
iex> Request.get("/", [
...> {"cookie", "tokumei.session=foo -- yjdW%2BB03KKAjvcimIsCQbzc7eV4%3D"}])
...> |> SignedCookies.extract(secret: "secret")
%{}
# Extend {:error, {:missing_keys: ["foo"]}}
# Tampered values will return empty session
iex> Request.get("/", [
...> {"cookie", "foo=BAD"},
...> {"cookie", "tokumei.session=foo -- yjdW%2BB03KKAjvcimIsCQbzc7eV4%3D"}])
...> |> SignedCookies.extract(secret: "secret")
%{}
# Extend {:error, :signature_does_not_match}
# Invalid session cookie will return empty session
iex> Request.get("/", [
...> {"cookie", "foo=bar"},
...> {"cookie", "tokumei.session=garbage"}])
...> |> SignedCookies.extract(secret: "secret")
%{}
# Extend {:error, :invalid_session_hallmark}
# Sessions will be extracted from cookies
iex> Request.get("/", [
...> {"cookie", "foo=bar"}])
...> |> SignedCookies.extract(secret: "secret")
%{}
# Extend {:error, :no_session_hallmark}