browsersession

package
v1.97.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 4, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package browsersession provides browser-based OIDC authentication and cookie-managed sessions for the portal UI. It implements:

  • HMAC-SHA256 signed JWT session cookies (stateless)
  • OIDC authorization code flow with PKCE
  • Cookie-based authenticator for the HTTP auth chain

Index

Constants

View Source
const (
	DefaultCookieName = "mcp_session"
	DefaultCookiePath = "/"
	DefaultTTL        = 8 * time.Hour
)

Default configuration values.

View Source
const CSRFHeaderName = "X-CSRF-Token" //nolint:gosec // header name, not a credential

CSRFHeaderName is the request header that carries the CSRF token on state-changing requests authenticated by a session cookie. Because it is a custom header, a cross-origin page cannot set it on a form/navigation submit, and the browser blocks scripted cross-origin reads of the token, so only same-origin callers that legitimately fetched the token can send a valid value.

View Source
const DefaultPortalPath = "/portal/"

DefaultPortalPath is the default redirect path for the portal UI.

Variables

View Source
var ErrCSRFInvalid = errors.New("missing or invalid CSRF token")

ErrCSRFInvalid is returned when a cookie-authenticated, state-changing request is missing a valid X-CSRF-Token header.

Functions

func ClearCookie

func ClearCookie(w http.ResponseWriter, cfg *CookieConfig)

ClearCookie removes the session cookie.

func IsValidSameSite added in v1.96.0

func IsValidSameSite(s string) bool

IsValidSameSite reports whether s is a recognized same_site config value (case-insensitive): "" (defaults to Lax), "lax", "strict", or "none". Config validation uses this to reject a typo at startup rather than let ParseSameSite silently downgrade an unrecognized value to Lax.

func ParseSameSite added in v1.96.0

func ParseSameSite(s string) http.SameSite

ParseSameSite maps a configuration string ("lax", "strict", "none") to an http.SameSite mode. An empty or unrecognized value yields the zero value, which effectiveSameSite treats as Lax, the safe default. The match is case-insensitive.

func SetCookie

func SetCookie(w http.ResponseWriter, cfg *CookieConfig, tokenString string)

SetCookie writes the session JWT as an HTTP-only cookie.

func SignSession

func SignSession(claims SessionClaims, cfg *CookieConfig) (string, error)

SignSession creates a signed JWT string from session claims.

Types

type Authenticator

type Authenticator struct {
	// contains filtered or unexported fields
}

Authenticator checks for a valid session cookie and returns user info. It is designed to be used as the first step in an HTTP auth chain — if no cookie is present or the cookie is invalid, it returns nil (allowing fallback to token-based authentication).

func NewAuthenticator

func NewAuthenticator(cfg CookieConfig) *Authenticator

NewAuthenticator creates a cookie-based authenticator.

func (*Authenticator) AuthenticateHTTP

func (a *Authenticator) AuthenticateHTTP(r *http.Request) (*middleware.UserInfo, error)

AuthenticateHTTP checks the HTTP request for a valid session cookie. Returns nil, nil when no valid cookie is found (no error, just unauthenticated).

func (*Authenticator) ExtractIDToken added in v0.36.2

func (a *Authenticator) ExtractIDToken(r *http.Request) string

ExtractIDToken extracts the OIDC id_token from the session cookie. Returns empty string if no valid session exists or no id_token is stored.

func (*Authenticator) IssueCSRFToken added in v1.96.0

func (a *Authenticator) IssueCSRFToken(subject string) string

IssueCSRFToken derives a stateless CSRF token bound to the session subject.

The token is an HMAC-SHA256 of the subject under the same signing key that protects the session cookie, so it needs no server-side storage and is verified by recomputation. It is safe to hand to the browser (e.g. in the /me response) because an attacker on another origin can neither read the same-origin response body (blocked by the browser) nor forge the HMAC without the signing key.

func (*Authenticator) ValidateCSRFRequest added in v1.96.0

func (a *Authenticator) ValidateCSRFRequest(r *http.Request, subject string) error

ValidateCSRFRequest enforces CSRF protection for a cookie-authenticated request. Safe (read-only) methods always pass. State-changing methods must carry an X-CSRF-Token header whose value matches the token bound to subject; otherwise ErrCSRFInvalid is returned.

Token-authenticated (API-key / Bearer) requests do not reach this check — they are not vulnerable to CSRF because the credential is not attached automatically by the browser.

type CookieConfig

type CookieConfig struct {
	// Name is the cookie name (default: "mcp_session").
	Name string

	// Domain restricts the cookie to a specific domain.
	Domain string

	// Path restricts the cookie to a specific path (default: "/").
	Path string

	// Secure marks the cookie as HTTPS-only (default: true).
	Secure bool

	// SameSite controls cross-site cookie behavior (default: Lax).
	SameSite http.SameSite

	// TTL is the session lifetime (default: 8h).
	TTL time.Duration

	// Key is the HMAC-SHA256 signing key. Must be at least 32 bytes.
	Key []byte
}

CookieConfig controls session cookie behavior.

func (*CookieConfig) IsCrossSiteCookieMode added in v1.96.0

func (c *CookieConfig) IsCrossSiteCookieMode() bool

IsCrossSiteCookieMode reports whether the effective SameSite setting permits cross-site transmission of the session cookie (SameSite=None). In that mode the browser's built-in CSRF defense is disabled and the X-CSRF-Token layer becomes the sole protection, so callers should log a startup warning.

type Flow

type Flow struct {
	// contains filtered or unexported fields
}

Flow implements the OIDC authorization code flow with PKCE.

func NewFlow

func NewFlow(ctx context.Context, cfg FlowConfig) (*Flow, error)

NewFlow creates a new OIDC flow by performing provider discovery.

func (*Flow) CallbackHandler

func (f *Flow) CallbackHandler(w http.ResponseWriter, r *http.Request)

CallbackHandler processes the OIDC provider's callback after authentication. It validates the state, exchanges the authorization code for tokens, creates a session cookie, and redirects to the portal.

func (*Flow) LoginHandler

func (f *Flow) LoginHandler(w http.ResponseWriter, r *http.Request)

LoginHandler initiates the OIDC authorization code flow. It generates state + PKCE verifier, stores them in a temporary cookie, and redirects the user to the OIDC provider's authorization endpoint.

An optional `return_to` query parameter is captured into the state cookie and used as the post-login redirect on callback. This lets the admin SPA send a 401-recovery flow back to the page the operator was on (e.g., a connection settings page where they clicked Connect) rather than dropping them at the portal root. The value must be a site-relative path; anything else is silently dropped so an attacker can't dangle an open redirect off the login endpoint.

func (*Flow) LogoutHandler

func (f *Flow) LogoutHandler(w http.ResponseWriter, r *http.Request)

LogoutHandler clears the session cookie and redirects to the OIDC end_session endpoint.

type FlowConfig

type FlowConfig struct {
	// Issuer is the OIDC provider's issuer URL.
	Issuer string

	// ClientID is the OIDC client identifier.
	ClientID string

	// ClientSecret is the OIDC client secret for confidential clients.
	ClientSecret string // #nosec G117 -- OIDC client secret from admin config

	// RedirectURI is the callback URL (e.g., "https://example.com/portal/auth/callback").
	RedirectURI string

	// Scopes to request (default: [openid, profile, email]).
	Scopes []string

	// RoleClaim is the JSON path to roles in the id_token claims.
	RoleClaim string

	// RolePrefix filters roles to those with this prefix.
	RolePrefix string

	// Cookie configures the session cookie.
	Cookie CookieConfig

	// PostLoginRedirect is where to redirect after successful login (default: DefaultPortalPath).
	PostLoginRedirect string

	// PostLogoutRedirect is the absolute URL sent as post_logout_redirect_uri
	// to the OIDC provider. Must be an absolute URL (Keycloak rejects relative paths).
	// Falls back to PostLoginRedirect if empty.
	PostLogoutRedirect string

	// HTTPClient is used for OIDC discovery and token exchange.
	// If nil, http.DefaultClient is used.
	HTTPClient *http.Client

	// OnLogin, if set, is called after a successful login with the person's
	// email and name derived from the id_token. It records browser-session
	// (portal/admin SPA) users in the known-users directory (#614). It must be
	// non-blocking and best-effort — login must never depend on it.
	OnLogin func(email, firstName, lastName string)
}

FlowConfig configures the OIDC authorization code flow.

type SessionClaims

type SessionClaims struct {
	UserID  string   `json:"sub"`
	Email   string   `json:"email,omitempty"`
	Roles   []string `json:"roles"`
	IDToken string   `json:"idt,omitempty"` // raw id_token for logout id_token_hint

	// FirstName and LastName are derived from the id_token at login and used to
	// record the person in the known-users directory (#614). They are NOT
	// persisted in the session cookie (json:"-"); they exist only in-memory for
	// the duration of the callback, so the cookie stays small and these are
	// absent on subsequent cookie-authenticated requests.
	FirstName string `json:"-"`
	LastName  string `json:"-"`
}

SessionClaims holds the claims stored in the session JWT cookie.

func ParseFromRequest

func ParseFromRequest(r *http.Request, cfg *CookieConfig) (*SessionClaims, error)

ParseFromRequest reads the session cookie from the request and verifies it.

func VerifySession

func VerifySession(tokenString string, key []byte) (*SessionClaims, error)

VerifySession validates a signed JWT and returns the session claims.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL