Documentation
¶
Index ¶
- func CSRFTemplateField(r *http.Request) template.HTML
- func CSRFToken(r *http.Request) string
- type AuthUserStore
- type CSRFMiddleware
- type LinkOAuthConfig
- type Middleware
- type OneAuth
- func (a *OneAuth) AddAuth(prefix string, handler http.Handler) *OneAuth
- func (a *OneAuth) EnsureDefaults() *OneAuth
- func (a *OneAuth) GetLinkingUserID(r *http.Request) string
- func (a *OneAuth) HandleLinkOAuthCallback(config LinkOAuthConfig, linkingUserID, provider string, ...)
- func (a *OneAuth) Handler() http.Handler
- func (a *OneAuth) SaveUserAndRedirect(authtype, provider string, token *oauth2.Token, userInfo map[string]any, ...)
- func (a *OneAuth) StartLinkOAuth(r *http.Request, userID string)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CSRFTemplateField ¶
CSRFTemplateField returns an HTML hidden input field containing the CSRF token. Use this in templates: {{.CSRFField}}
Types ¶
type AuthUserStore ¶
type AuthUserStore interface {
core.UserStore
core.IdentityStore
core.ChannelStore
// EnsureAuthUser orchestrates user creation/lookup across stores
// This is the main entry point for OAuth and local authentication
EnsureAuthUser(authtype string, provider string, token *oauth2.Token, userInfo map[string]any) (core.User, error)
}
AuthUserStore combines the store interfaces needed for authentication
type CSRFMiddleware ¶
type CSRFMiddleware struct {
// CookieName is the name of the CSRF cookie. Default: "csrf_token".
CookieName string
// FieldName is the form field name to check. Default: "csrf_token".
FieldName string
// HeaderName is the HTTP header to check. Default: "X-CSRF-Token".
HeaderName string
// MaxAge is the cookie lifetime in seconds. Default: 3600 (1 hour).
MaxAge int
// Secure sets the Secure flag on the cookie (for HTTPS).
Secure bool
// SameSite sets the SameSite attribute. Default: SameSiteStrictMode.
SameSite http.SameSite
// Path sets the cookie path. Default: "/".
Path string
// ErrorHandler is called when CSRF validation fails. Default: 403 JSON response.
ErrorHandler http.HandlerFunc
// ExemptFunc returns true if the request should skip CSRF validation.
// Default: exempt requests with an Authorization: Bearer header.
ExemptFunc func(*http.Request) bool
}
CSRFMiddleware implements the double-submit cookie pattern for CSRF protection. It generates a random token stored in a cookie and validates that state-changing requests include a matching token in a form field or header.
Bearer-token requests are exempt by default since they are not vulnerable to CSRF. The cookie is NOT HttpOnly so JavaScript can read it for AJAX header submission.
func (*CSRFMiddleware) Protect ¶
func (m *CSRFMiddleware) Protect(next http.Handler) http.Handler
Protect returns middleware that enforces CSRF protection. Safe methods (GET, HEAD, OPTIONS) receive a CSRF cookie and have the token injected into the request context. Unsafe methods must include a matching token in either the form field or header.
type LinkOAuthConfig ¶
type LinkOAuthConfig struct {
UserStore core.UserStore
IdentityStore core.IdentityStore
ChannelStore core.ChannelStore
}
LinkOAuthConfig holds configuration for OAuth account linking
type Middleware ¶
type Middleware struct {
AuthTokenHeaderName string
AuthTokenCookieName string
UserParamName string
CallbackURLParam string
SessionGetter func(r *http.Request, param string) any
GetRedirURL func(r *http.Request) string
DefaultRedirectURL string
VerifyToken func(tokenString string) (loggedInUserId string, token any, err error)
}
func (*Middleware) EnsureReasonableDefaults ¶
func (a *Middleware) EnsureReasonableDefaults()
*
- Ensures that config values have reasonable defaults.
func (*Middleware) EnsureUser ¶
func (a *Middleware) EnsureUser(next http.Handler) http.Handler
func (*Middleware) ExtractUser ¶
func (a *Middleware) ExtractUser(next http.Handler) http.Handler
*
- Fetches the user from the request and loads the UserId and User variables
- available for other handlers. *
- Note this does not perform any redirects if a valid user does not exist.
- To also enforce a user exists, use the EnsureUser handler which both
- calls ExgractUser and ensures that user is logged in.
func (*Middleware) GetLoggedInUserId ¶
func (a *Middleware) GetLoggedInUserId(r *http.Request) string
Get the ID of the logged in user from the current request
type OneAuth ¶
type OneAuth struct {
Session *scs.SessionManager
Middleware Middleware
// Optional name that can be used as a prefix for all required vars
AppName string
// Name of the session variable where the auth token is stored
AuthTokenSessionVar string
// Must be passed in
UserStore AuthUserStore
// All the domains where the auth token cookies will be set on a login success or logout
CookieDomains []string
// JWT related fields
JwtIssuer string
JWTSecretKey string
// How long is a session cookie valid for. Defaults to 1 day
SessionTimeoutInSeconds int
// contains filtered or unexported fields
}
func (*OneAuth) EnsureDefaults ¶
func (*OneAuth) GetLinkingUserID ¶
GetLinkingUserID retrieves and clears the linking user ID from session. Call this in your OAuth callback to detect linking mode.
Example Usage ¶
func googleCallback(w http.ResponseWriter, r *http.Request) {
linkingUserID := oneAuth.GetLinkingUserID(r)
if linkingUserID != "" {
// Linking flow
oneAuth.HandleLinkOAuthCallback(config, linkingUserID, "google", userInfo, w, r)
return
}
// Normal login flow
oneAuth.SaveUserAndRedirect(...)
}
func (*OneAuth) HandleLinkOAuthCallback ¶
func (a *OneAuth) HandleLinkOAuthCallback(config LinkOAuthConfig, linkingUserID, provider string, userInfo map[string]any, w http.ResponseWriter, r *http.Request)
HandleLinkOAuthCallback returns an HTTP handler for linking an OAuth provider to an existing local-only user.
Who Calls This ¶
This is called by OAuth providers after the user authorizes linking. The flow is:
- Local-only user visits profile, clicks "Link Google Account"
- App stores user ID in session as "linkingUserID"
- App redirects to Google OAuth with special state
- Google redirects back to /auth/google/callback
- OAuth callback sees "linkingUserID" in session
- Instead of normal login, calls this handler to link the account
How to Set Up ¶
Modify your OAuth callback to detect linking mode:
func googleCallback(w http.ResponseWriter, r *http.Request) {
// ... exchange code for token, get userInfo ...
// Check if this is a linking flow
linkingUserID := session.Get("linkingUserID")
if linkingUserID != "" {
session.Delete("linkingUserID")
linkConfig := oneauth.LinkOAuthConfig{
UserStore: stores.UserStore,
IdentityStore: stores.IdentityStore,
ChannelStore: stores.ChannelStore,
}
oneAuth.HandleLinkOAuthCallback(linkConfig, linkingUserID, "google", userInfo, w, r)
return
}
// Normal login flow
oneAuth.SaveUserAndRedirect("oauth", "google", token, userInfo, w, r)
}
What It Does ¶
- Verifies the OAuth email matches the user's existing email identity
- Creates OAuth channel for the provider
- Updates user profile["channels"] to include the new provider
- Redirects to callback URL (or returns JSON success)
Security ¶
The OAuth email MUST match the user's existing email to prevent account hijacking. Users cannot link to a different email address.
func (*OneAuth) SaveUserAndRedirect ¶
func (a *OneAuth) SaveUserAndRedirect(authtype, provider string, token *oauth2.Token, userInfo map[string]any, w http.ResponseWriter, r *http.Request)
*
- Called by the oauth callback handler with auth token and user info after
- a successful auth flow and redirect. *
- Here is our opportunity to:
- 1. Create a userId that is unique to our system based on userInfo
- 2. Set the right session cookies from this.
func (*OneAuth) StartLinkOAuth ¶
StartLinkOAuth initiates OAuth account linking by storing the user ID in session. Call this from your "Link [Provider] Account" button handler.
Example Usage ¶
func handleLinkGoogle(w http.ResponseWriter, r *http.Request) {
userID := getLoggedInUserID(r)
oneAuth.StartLinkOAuth(r, userID)
// Redirect to Google OAuth
http.Redirect(w, r, "/auth/google/", http.StatusFound)
}