diff --git a/lib/comfycamp/accounts.ex b/lib/comfycamp/accounts.ex index e4ec82a..f2f1b07 100644 --- a/lib/comfycamp/accounts.ex +++ b/lib/comfycamp/accounts.ex @@ -261,6 +261,11 @@ defmodule Comfycamp.Accounts do Repo.one(query) end + def get_user_by_bearer_token(token) do + {:ok, query} = UserToken.verify_bearer_token_query(token) + Repo.one(query) + end + @doc """ Deletes the signed token with the given context. """ diff --git a/lib/comfycamp/accounts/user_token.ex b/lib/comfycamp/accounts/user_token.ex index 6ff4abd..bd446cf 100644 --- a/lib/comfycamp/accounts/user_token.ex +++ b/lib/comfycamp/accounts/user_token.ex @@ -80,6 +80,19 @@ defmodule Comfycamp.Accounts.UserToken do {:ok, query} end + @doc """ + Checks if the token is valid and returns its underlying lookup query. + """ + def verify_bearer_token_query(token) do + query = + from token in by_token_and_context_query(token, "bearer"), + join: user in assoc(token, :user), + where: token.inserted_at > ago(1, "day"), + select: user + + {:ok, query} + end + @doc """ Builds a token and its hash to be delivered to the user's email. diff --git a/lib/comfycamp_web/controllers/oauth_controller.ex b/lib/comfycamp_web/controllers/oauth_controller.ex index 15c07a0..4444370 100644 --- a/lib/comfycamp_web/controllers/oauth_controller.ex +++ b/lib/comfycamp_web/controllers/oauth_controller.ex @@ -118,6 +118,10 @@ defmodule ComfycampWeb.OauthController do render(conn, :openid_discovery) end + def user_info(conn, _params) do + render(conn, :user_info, user: conn.assigns.oauth_user) + end + @doc """ Extract client id and client secret from request parameters or headers. Returns {:ok, "client_id", "client_secret"} on success. diff --git a/lib/comfycamp_web/controllers/oauth_json.ex b/lib/comfycamp_web/controllers/oauth_json.ex index 32a5508..722da83 100644 --- a/lib/comfycamp_web/controllers/oauth_json.ex +++ b/lib/comfycamp_web/controllers/oauth_json.ex @@ -22,6 +22,14 @@ defmodule ComfycampWeb.OauthJSON do } end + def user_info(%{user: user}) do + %{ + sub: Integer.to_string(user.id), + email: user.email, + preferred_username: user.username + } + end + def error(assigns) do %{ description: assigns["description"] diff --git a/lib/comfycamp_web/router.ex b/lib/comfycamp_web/router.ex index 545cc4a..b910c2b 100644 --- a/lib/comfycamp_web/router.ex +++ b/lib/comfycamp_web/router.ex @@ -15,6 +15,7 @@ defmodule ComfycampWeb.Router do pipeline :api do plug :accepts, ["json"] + plug :fetch_bearer_token end scope "/", ComfycampWeb do @@ -35,6 +36,13 @@ defmodule ComfycampWeb.Router do get "/.well-known/openid-configuration", OauthController, :openid_discovery end + scope "/", ComfycampWeb do + pipe_through [:api, :require_oauth] + + get "/oauth/userinfo", OauthController, :user_info + post "/oauth/userinfo", OauthController, :user_info + end + # Enable LiveDashboard and Swoosh mailbox preview in development if Application.compile_env(:comfycamp, :dev_routes) do # If you want to use the LiveDashboard in production, you should put diff --git a/lib/comfycamp_web/user_auth.ex b/lib/comfycamp_web/user_auth.ex index f0f7ce5..acb2b01 100644 --- a/lib/comfycamp_web/user_auth.ex +++ b/lib/comfycamp_web/user_auth.ex @@ -110,6 +110,36 @@ defmodule ComfycampWeb.UserAuth do end end + @doc """ + Authenticate the user by looking at Authorization header + and checking Bearer token. + """ + def fetch_bearer_token(conn, _opts) do + case Plug.Conn.get_req_header(conn, "authorization") do + ["Bearer " <> header] -> + user = Accounts.get_user_by_bearer_token(header) + assign(conn, :oauth_user, user) + + _ -> + conn + end + end + + @doc """ + Check that request contains a valid bearer token. + Call after fetch_bearer_token plug. + """ + def require_oauth(conn, _opts) do + if conn.assigns[:oauth_user] do + conn + else + conn + |> put_status(:unauthorized) + |> json(%{error: "You must use a bearer token to access this page."}) + |> halt() + end + end + @doc """ Handles mounting and authenticating the current_user in LiveViews.