๐ŸŒฌ๏ธ Breeze docs

OAuth2 / social login

Zero-config OAuth2 / OpenID Connect login under middlewares/oauth2. Four providers work out of the box โ€” Google, GitHub, Microsoft, Discord โ€” with PKCE, CSRF-protected state, signed HttpOnly cookies (or JWT sessions), and transparent token refresh. Only ClientID and ClientSecret are required.

package main

import (
	"github.com/nelthaarion/breeze/v2"
	"github.com/nelthaarion/breeze/v2/middlewares/oauth2"
)

func main() {
	app := breeze.New()

	cfg := oauth2.Config{
		Provider:     oauth2.Google,
		ClientID:     "your-client-id",
		ClientSecret: "your-client-secret",
		BaseURL:      "https://app.example.com",
		CookieSecret: "a-long-random-secret",
	}

	app.Router.Handle(breeze.GET, "/auth/google", oauth2.Login(cfg))
	app.Router.Handle(breeze.GET, "/auth/google/callback", oauth2.Callback(cfg))
	app.Router.Handle(breeze.GET, "/auth/logout", oauth2.Logout(cfg))
	app.Router.Handle(breeze.GET, "/dashboard", dashboard, oauth2.Auth(cfg))

	app.Listen(":3000")
}

func dashboard(ctx *breeze.Context) error {
	user := oauth2.CurrentUser(ctx) // *oauth2.User
	return ctx.JSON(user)
}

Middlewares

FunctionPurpose
Login(cfg)begins the flow: random state + nonce + PKCE, redirect to the provider
Callback(cfg)validates state, exchanges the code, fetches the user, writes the session
Auth(cfg)requires a valid session; 401 / redirect otherwise
Optional(cfg)attaches the session if present, never blocks
Refresh(cfg)transparently refreshes an expiring access token, then rotates the cookie
Logout(cfg)clears the session and redirects

Refresh layers before Auth on protected routes that need fresh access tokens:

app.Router.Handle(breeze.GET, "/dashboard", dashboard,
	oauth2.Refresh(cfg),
	oauth2.Auth(cfg),
)

Reading the user

From any handler that ran after Auth, Optional, or Callback:

user := oauth2.CurrentUser(ctx)   // *User or nil
tok  := oauth2.CurrentToken(ctx)  // *Token or nil
if oauth2.IsAuthenticated(ctx) {
	// ...
}

UserFrom / TokenFrom are short aliases. The normalized User is provider-independent:

type User struct {
	ID       string   // stable provider id (Google sub, GitHub numeric id, ...)
	Email    string
	Name     string
	Username string
	Avatar   string   // URL
	Provider Provider
}

Sessions

Two modes, selected by Config.SessionMode:

  • SessionModeCookie (default) โ€” user + token serialized into a signed, HttpOnly cookie. Tamper-proof (HMAC), stateless server-side.
  • SessionModeJWT โ€” an HS256 JWT carrying the claims, stored in the cookie. The algorithm is pinned to prevent alg=none / confusion attacks.

Every write rotates the session (a new JWT jti or a freshly signed blob), so a pre-login cookie cannot be reused post-login.

Security

  • PKCE (S256) on by default for every provider.
  • CSRF โ€” state lives in a signed, short-lived, single-use cookie, compared in constant time; the flow cookie is cleared on callback.
  • Open-redirect guard โ€” ?redirect= overrides accept same-site paths only.
  • HttpOnly + Secure + SameSite=Lax cookies. Secure auto-enables for https:// BaseURLs and can be forced.
  • Bounded provider calls โ€” every outbound token/userinfo request runs under a timeout so a slow provider cannot pin a worker.

Configuration

FieldDefaultNotes
Providerโ€”Google, GitHub, Microsoft, Discord
ClientIDโ€” (required)
ClientSecretโ€” (required)
BaseURLhttp://localhost:3000origin used to build RedirectURL
RedirectURLBaseURL/auth/{provider}/callbackthe registered callback URL
Scopesprovider defaultsoverride to request more scopes
SessionModeSessionModeCookieor SessionModeJWT
CookieNamebreeze_oauth_session
SuccessRedirect/post-login destination
FailureRedirect"" โ†’ 401set to redirect on failure instead
CookieSecretrandom per processset in production (survives restarts)
JWTSecretfalls back to CookieSecretused in JWT mode
SessionTTL24h
StateTTL10mmax time to complete a login
ClockSkew1mexpiry tolerance
Securetrue (auto for https BaseURL)set false only for local http

Diagnostics

The oauth2 probe reports through GET /dashboard/api/diagnostics?subsystem=oauth2. See Diagnostics.

Generated documentation for the Breeze framework ยท built with SvelteKit, fully static.