Created
October 18, 2019 15:24
-
-
Save topherhunt/ec1b64599e5aa15d82aa090bcc6f4ef7 to your computer and use it in GitHub Desktop.
Auth plug load_current_user using cond vs. with
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# These aren't 100% parallel (they're from different apps with slightly different auth rules) | |
# but they're close enough to demonstrate what `with` can do, I think. | |
# Version 1: using `cond` | |
# Pro: very linear. just go down the list and pick the first truthy result. | |
# Con: Using `cond` maybe doesn't sufficiently emphasize how important the ordering is | |
# here. If you naively swapped the order of two conditions, it could wreck the security. | |
# Basically, in this version, I'm relying on the dev's caution and willingness to read | |
# through my extensive comments. | |
def load_current_user(conn, _opts) do | |
cond do | |
# If a user is already loaded, nothing to do | |
current_user_assigned?(conn) -> conn | |
# If no user is logged in, explicitly set current_user to nil | |
no_login_session?(conn) -> assign(conn, :current_user, nil) | |
# If the session is expired, log me out (must be before load_user_from_session!) | |
session_expired?(conn) -> logout!(conn) | |
# If we can find the user with this id, assign them | |
user = load_user_from_session(conn) -> set_assigned_user(conn, user) | |
# If no user was found by that id, the session is invalid. Log me out. | |
true -> logout!(conn) | |
end | |
end | |
# Version 2: using `with` | |
# Pro: I love that this pattern so clearly documents the ordering/dependency of different | |
# conditions, and what each failure case means. | |
# Con: The get_token / parse_token / get_user helpers have to be custom-made to return | |
# :ok/:error tuples, as opposed to my more common pattern of "return a value on success, | |
# or nil on failure". | |
def load_user_from_token(conn, _opts) do | |
with :ok <- ensure_no_user_assigned(conn), | |
{:ok, token} <- get_token(conn), | |
{:ok, user_id} <- parse_token(token), | |
{:ok, user} <- get_user(user_id) do | |
assign(conn, :current_user, user) | |
else | |
{:error, :user_already_assigned} -> conn | |
{:error, :no_auth_token} -> assign(conn, :current_user, nil) | |
{:error, :token_invalid} -> halt_with_invalid_token_error(conn) | |
{:error, :user_not_found} -> halt_with_invalid_token_error(conn) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment