Atex.OAuth.Plug
(atex v0.9.1)
View Source
Plug router for handling AT Protocol's OAuth flow.
This module provides four endpoints:
GET /login?handle=<handle>- Initiates the OAuth authorization flow for a given handleGET /callback- Handles the OAuth callback after user authorizationGET /client-metadata.json- Serves the OAuth client metadataGET /logout- Logs out the current session and revokes tokens
Usage
This module requires Plug.Session to be in your pipeline, as well as
secret_key_base to have been set on your connections. Ideally it should be
routed to via Plug.Router.forward/2, under a route like "/oauth".
The plug requires a :callback option that must be an MFA tuple (Module,
Function, Args). This callback is invoked after successful OAuth
authentication, receiving the connection with the authenticated session data.
An optional :logout_callback option can be provided for handling logout
redirects. If not provided, the user is redirected to "/".
Error Handling
Atex.OAuth.Error exceptions are raised when errors occur during the OAuth
flow (e.g. an invalid handle is provided, or validation failed). You should
implement a Plug.ErrorHandler to catch and handle these exceptions
gracefully.
Example
Example implementation showing how to set up the OAuth plug with proper session handling, error handling, and callbacks.
defmodule ExampleOAuthPlug do
use Plug.Router
use Plug.ErrorHandler
plug :put_secret_key_base
plug Plug.Session,
store: :cookie,
key: "atex-oauth",
signing_salt: "signing-salt"
plug :match
plug :dispatch
forward "/oauth", to: Atex.OAuth.Plug,
init_opts: [
callback: {__MODULE__, :oauth_callback, []},
logout_callback: {__MODULE__, :logout_callback, []}
]
def oauth_callback(conn) do
# Handle successful OAuth authentication
conn
|> put_resp_header("Location", "/dashboard")
|> resp(307, "")
|> send_resp()
end
def logout_callback(conn) do
# Handle logout redirect
conn
|> put_resp_header("Location", "/login")
|> resp(307, "")
|> send_resp()
end
def put_secret_key_base(conn, _) do
put_in(
conn.secret_key_base,
"very long key base with at least 64 bytes"
)
end
# Error handler for OAuth exceptions
@impl Plug.ErrorHandler
def handle_errors(conn, %{kind: :error, reason: %Atex.OAuth.Error{} = error, stack: _stack}) do
status = case error.reason do
reason when reason in [:missing_handle, :invalid_handle, :invalid_callback_request, :issuer_mismatch] -> 400
_ -> 500
end
conn
|> put_resp_content_type("text/plain")
|> send_resp(status, error.message)
end
# Fallback for other errors
def handle_errors(conn, %{kind: _kind, reason: _reason, stack: _stack}) do
send_resp(conn, conn.status, "Something went wrong")
end
endSession Storage
After successful authentication, the plug stores the following in
conn.session:
:atex_sessions- A list of composite session keys ("<did>:<nonce>") for all accounts logged in on this device.:atex_active_session- The composite session key of the currently active account. UseAtex.OAuth.current_session_key/1to read this, andAtex.OAuth.switch_session/2to change it.
The full session credentials (tokens, DPoP key, etc.) are stored in
Atex.OAuth.SessionStore and looked up by the composite key.
Summary
Functions
Callback implementation for Plug.call/2.
Callback implementation for Plug.init/1.
Revokes a session, removing it from the store and cleaning up the Plug session.
Functions
Callback implementation for Plug.call/2.
Callback implementation for Plug.init/1.
@spec revoke_session(Plug.Conn.t(), String.t()) :: {:ok, Plug.Conn.t()} | {:error, :not_found}
Revokes a session, removing it from the store and cleaning up the Plug session.
This function:
- Deletes the session from
Atex.OAuth.SessionStore - Revokes tokens with the authorization server
- Removes the session key from the Plug session's active session
- If the deleted session was the active one, switches to another or clears it
Parameters
conn- The Plug connectionsession_key- The composite session key to revoke
Returns
{:ok, conn}- Session revoked; the returned conn has updated session data{:error, :not_found}- Session key not found