A separate session cookie for OAuth authentication flow.
This plug creates a dedicated short-lived session cookie for storing OAuth state (nonce, PKCE verifier, redirect URL) during the authentication flow. This provides several security benefits:
Why Dual Cookies?
OAuth providers like Microsoft Entra can use form_post response mode (POST callback)
which is more secure than query mode (tokens don't appear in URL/browser history).
However, SameSite=Lax cookies are NOT sent on cross-site POST requests.
The solution is two cookies:
- Auth cookie:
SameSite=None; Secure- sent on cross-site POSTs, short-lived (5 min) - Main cookie:
SameSite=LaxorStrict- not sent cross-site, long-lived
Security Benefits
- Session fixation prevention: New session ID after authentication
- Minimal exposure window: Auth cookie expires in 5 minutes
- Token security: Allows
form_postmode (tokens not in URL) - Cookie isolation: Auth state separate from user session
Usage
Add to your router's authentication pipeline:
pipeline :authentication do
plug KeenAuth.Plug, otp_app: :my_app
plug KeenAuth.Plug.AuthSession
endConfiguration
Configure in your endpoint or via plug options:
plug KeenAuth.Plug.AuthSession,
key: "_my_app_auth",
signing_salt: "auth_signing_salt",
encryption_salt: "auth_encryption_salt",
same_site: "None", # Required for cross-site POST
secure: true, # Required with SameSite=None
max_age: 300 # 5 minutesHow It Works
- OAuth
/new→ State stored in auth cookie (SameSite=None) - Provider redirects with POST → Auth cookie sent (cross-site OK)
- OAuth
/callback→ State validated and cleared - Success → User stored in main session, auth cookie deleted
Summary
Functions
Clear the entire auth session.
Clear auth session and regenerate the main session ID.
Delete a value from the auth session.
Get a value from the auth session.
Get a value from auth session and delete it atomically.
Put a value in the auth session.
Regenerate main session ID without clearing auth data.
Functions
@spec clear(Plug.Conn.t()) :: Plug.Conn.t()
Clear the entire auth session.
@spec clear_and_regenerate(Plug.Conn.t()) :: Plug.Conn.t()
Clear auth session and regenerate the main session ID.
Call this after successful authentication to:
- Clear OAuth flow data from auth cookie
- Regenerate main session ID (prevents session fixation)
@spec delete(Plug.Conn.t(), atom()) :: Plug.Conn.t()
Delete a value from the auth session.
@spec get(Plug.Conn.t(), atom()) :: any()
Get a value from the auth session.
@spec get_and_delete(Plug.Conn.t(), atom()) :: {Plug.Conn.t(), any()}
Get a value from auth session and delete it atomically.
@spec put(Plug.Conn.t(), atom(), any()) :: Plug.Conn.t()
Put a value in the auth session.
@spec regenerate(Plug.Conn.t()) :: Plug.Conn.t()
Regenerate main session ID without clearing auth data.