localauth

package
v0.0.73 Latest Latest
Warning

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

Go to latest
Published: Apr 17, 2026 License: Apache-2.0 Imports: 11 Imported by: 1

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func LinkLocalCredentials

func LinkLocalCredentials(config EnsureAuthUserConfig, userID string, username, password, email string) error

LinkLocalCredentials adds local (password) authentication to an existing OAuth-only user. This enables "incremental auth" where users sign up via OAuth and later add a password.

Who Calls This

Your app calls this from a "Set Password" or "Complete Profile" page. Typically:

  1. User signed up via Google OAuth (has google channel, no local channel)
  2. User visits profile page, sees "Add password for email login"
  3. User submits password (and optionally username) form
  4. Your handler calls LinkLocalCredentials with the logged-in user's ID
  5. User can now login with email/password OR Google

Example Handler in Your App

func handleSetPassword(w http.ResponseWriter, r *http.Request) {
    userID := getLoggedInUserID(r) // from session/JWT
    user, _ := userStore.GetUserById(userID)
    email := user.Profile()["email"].(string)

    username := r.FormValue("username") // optional
    password := r.FormValue("password")

    err := oneauth.LinkLocalCredentials(config, userID, username, password, email)
    if err != nil {
        // handle error (e.g., username taken, password too weak)
    }
    // redirect to profile with success message
}

What It Does

  1. Verifies the email belongs to the given userID
  2. Checks that local channel doesn't already exist
  3. Creates local channel with hashed password
  4. Reserves username in UsernameStore (if configured and username provided)
  5. Updates user profile["channels"] to include "local"

func NewCreateUserFunc

func NewCreateUserFunc(userStore core.UserStore, identityStore core.IdentityStore, channelStore core.ChannelStore) core.CreateUserFunc

NewCreateUserFunc creates a CreateUserFunc from stores

func NewCredentialsValidator

func NewCredentialsValidator(identityStore core.IdentityStore, channelStore core.ChannelStore, userStore core.UserStore) core.CredentialsValidator

NewCredentialsValidator creates a CredentialsValidator from stores

func NewCredentialsValidatorWithUsername

func NewCredentialsValidatorWithUsername(identityStore core.IdentityStore, channelStore core.ChannelStore, userStore core.UserStore, usernameStore core.UsernameStore) core.CredentialsValidator

NewCredentialsValidatorWithUsername creates a CredentialsValidator that supports logging in with username (in addition to email/phone).

Who Calls This

Use this instead of NewCredentialsValidator when setting up LocalAuth if you want users to be able to login with their username:

localAuth := &oneauth.LocalAuth{
    ValidateCredentials: oneauth.NewCredentialsValidatorWithUsername(
        identityStore, channelStore, userStore, usernameStore,
    ),
    // ... other config
}

How Username Login Works

  1. User enters "johndoe" and password on login form
  2. DetectUsernameType returns "username" (not email, not phone)
  3. This validator looks up "johndoe" in UsernameStore → gets userID
  4. Gets user's email identity from IdentityStore
  5. Gets local channel for that email identity
  6. Verifies password against channel's password_hash
  7. Returns the user

Fallback Behavior

If user enters an email or phone number instead of username, it falls back to the standard email/phone lookup (same as NewCredentialsValidator).

func NewEnsureAuthUserFunc

func NewEnsureAuthUserFunc(config EnsureAuthUserConfig) func(authtype string, provider string, token any, userInfo map[string]any) (core.User, error)

NewEnsureAuthUserFunc creates a function that handles user creation/lookup for both OAuth and local authentication with channel linking support.

Who Calls This

This function is called by OneAuth.SaveUserAndRedirect after a successful OAuth callback or local login. The returned function implements the core logic for AuthUserStore.EnsureAuthUser.

Flow for OAuth (e.g., Google Login)

  1. User clicks "Login with Google" → redirects to Google
  2. Google redirects back to /auth/google/callback with auth code
  3. OAuth handler exchanges code for token, fetches userInfo (email, name, picture)
  4. OAuth handler calls OneAuth.SaveUserAndRedirect(authtype="oauth", provider="google", token, userInfo)
  5. SaveUserAndRedirect calls UserStore.EnsureAuthUser → this function
  6. This function checks if email identity exists: - EXISTS: Link Google channel to existing user, update profile["channels"] - NEW: Create User, Identity (verified=true), Google Channel
  7. SaveUserAndRedirect creates JWT, sets cookies, redirects to app

Flow for Local Signup

  1. User submits signup form with email/password
  2. LocalAuth.HandleSignup validates and calls CreateUser (from NewCreateUserFunc)
  3. CreateUser creates User, Identity (verified=false), Local Channel
  4. HandleSignup calls HandleUser → SaveUserAndRedirect → this function
  5. User is logged in (or email verification required)

Channel Linking Logic

Multiple channels (local, google, github) can point to the same user via shared email:

User (id: abc123)
├── Identity: email → user@example.com
├── Channel: local → email:user@example.com (password_hash)
├── Channel: google → email:user@example.com (oauth profile)
└── Channel: github → email:user@example.com (oauth profile)

User profile tracks linked providers: profile["channels"] = ["local", "google", "github"]

Types

type EnsureAuthUserConfig

type EnsureAuthUserConfig struct {
	UserStore     core.UserStore
	IdentityStore core.IdentityStore
	ChannelStore  core.ChannelStore
	UsernameStore core.UsernameStore // Optional - for username uniqueness
}

EnsureAuthUserConfig holds configuration for NewEnsureAuthUserFunc.

Example setup in your app:

config := oneauth.EnsureAuthUserConfig{
    UserStore:     gaeStores.UserStore,
    IdentityStore: gaeStores.IdentityStore,
    ChannelStore:  gaeStores.ChannelStore,
    UsernameStore: gaeStores.UsernameStore, // optional
}
ensureUser := oneauth.NewEnsureAuthUserFunc(config)

// Then use with OneAuth:
authUserStore := &MyAuthUserStore{config: config, ensureUser: ensureUser}
oneAuth.UserStore = authUserStore

type GetLoggedInUserFunc

type GetLoggedInUserFunc func(r *http.Request) (userID string, err error)

GetLoggedInUserFunc returns the currently logged-in user ID from the request. Apps must implement this based on their session/JWT handling.

type LinkCredentialsConfig

type LinkCredentialsConfig struct {
	UserStore     core.UserStore
	IdentityStore core.IdentityStore
	ChannelStore  core.ChannelStore
	UsernameStore core.UsernameStore // Optional
}

LinkCredentialsConfig holds configuration for HandleLinkCredentials

type LocalAuth

type LocalAuth struct {
	// Validates credentials during login
	ValidateCredentials core.CredentialsValidator

	// Validates credentials during signup (deprecated: use SignupPolicy instead)
	ValidateSignup core.SignupValidator

	// Creates a new user (for signup)
	CreateUser core.CreateUserFunc

	// Optional email sender for verification emails
	EmailSender core.SendEmail

	// Optional token store for email verification and password reset
	TokenStore core.TokenStore

	// Base URL for generating verification/reset links
	BaseURL string

	// Whether email verification is required before login
	RequireEmailVerification bool

	// Provider name (defaults to "local")
	Provider string

	// Form field names
	UsernameField string
	PasswordField string
	EmailField    string
	PhoneField    string

	// Handler called after successful authentication
	HandleUser core.HandleUserFunc

	// Callback to verify email by token
	VerifyEmail VerifyEmailFunc

	// Callback to update password
	UpdatePassword UpdatePasswordFunc

	// SignupPolicy defines what is required for signup (overrides ValidateSignup if set)
	SignupPolicy *core.SignupPolicy

	// OnSignupError is called when signup fails. If nil, returns JSON error.
	OnSignupError core.AuthErrorHandler

	// OnLoginError is called when login fails. If nil, returns JSON error.
	OnLoginError core.AuthErrorHandler

	// SignupURL is used for redirects on error (if OnSignupError uses redirects)
	SignupURL string

	// LoginURL is used for redirects on error (if OnLoginError uses redirects)
	LoginURL string

	// Optional UsernameStore for enforcing username uniqueness
	UsernameStore core.UsernameStore

	// ForgotPasswordURL: if set, GET /forgot-password redirects here and
	// POST /forgot-password redirects here with ?sent=true on success.
	// If empty, GET renders a basic HTML form and POST returns JSON.
	ForgotPasswordURL string

	// ResetPasswordURL: if set, GET /reset-password redirects here (with ?token=...)
	// and POST redirects here with ?success=true on success or ?error=... on failure.
	// If empty, GET renders a basic HTML form and POST returns JSON.
	ResetPasswordURL string

	// RateLimiter limits login attempts per key (IP:username). Optional.
	// When set, rate-limited requests receive 429 Too Many Requests.
	RateLimiter core.RateLimiter

	// Lockout locks accounts after consecutive failed login attempts. Optional.
	// When set, locked accounts receive 429 Too Many Requests until the lockout expires.
	Lockout *core.AccountLockout
}

Allows local username/password based authentication

func (*LocalAuth) HandleForgotPassword

func (a *LocalAuth) HandleForgotPassword(w http.ResponseWriter, r *http.Request)

HandleForgotPassword handles forgot password requests (POST)

func (*LocalAuth) HandleForgotPasswordForm

func (a *LocalAuth) HandleForgotPasswordForm(w http.ResponseWriter, r *http.Request)

HandleForgotPasswordForm shows the forgot password form (GET)

func (*LocalAuth) HandleLinkCredentials

func (a *LocalAuth) HandleLinkCredentials(config LinkCredentialsConfig, getUser GetLoggedInUserFunc) http.HandlerFunc

HandleLinkCredentials returns an HTTP handler that adds local (password) auth to an existing OAuth-only user.

Who Calls This

Mount this handler at a protected route (requires login) like POST /auth/link-credentials:

localAuth := &oneauth.LocalAuth{...}
linkConfig := oneauth.LinkCredentialsConfig{
    UserStore:     stores.UserStore,
    IdentityStore: stores.IdentityStore,
    ChannelStore:  stores.ChannelStore,
    UsernameStore: stores.UsernameStore, // optional
}
getUser := func(r *http.Request) (string, error) {
    return getLoggedInUserIDFromSession(r), nil
}
mux.Handle("POST /auth/link-credentials", localAuth.HandleLinkCredentials(linkConfig, getUser))

Flow

  1. OAuth-only user visits profile page, sees "Add password" form
  2. User submits form with password (and optionally username)
  3. Handler validates input, creates local channel, reserves username
  4. User can now login with email/password OR their OAuth provider

Form Fields

  • password (required): The new password
  • username (optional): Username for login (if UsernameStore configured)

Responses

  • 200 OK: {"success": true, "message": "..."}
  • 400 Bad Request: {"error": "...", "code": "...", "field": "..."}
  • 401 Unauthorized: User not logged in
  • 409 Conflict: Local credentials already exist

func (*LocalAuth) HandleResetPassword

func (a *LocalAuth) HandleResetPassword(w http.ResponseWriter, r *http.Request)

HandleResetPassword handles password reset submissions (POST)

func (*LocalAuth) HandleResetPasswordForm

func (a *LocalAuth) HandleResetPasswordForm(w http.ResponseWriter, r *http.Request)

HandleResetPasswordForm shows the reset password form (GET)

func (*LocalAuth) HandleSignup

func (a *LocalAuth) HandleSignup(w http.ResponseWriter, r *http.Request)

HandleSignup processes user registration

func (*LocalAuth) HandleVerifyEmail

func (a *LocalAuth) HandleVerifyEmail(w http.ResponseWriter, r *http.Request)

HandleVerifyEmail handles email verification via token

func (*LocalAuth) ServeHTTP

func (a *LocalAuth) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP handles login requests

type UpdatePasswordFunc

type UpdatePasswordFunc func(email, newPassword string) error

func NewUpdatePasswordFunc

func NewUpdatePasswordFunc(identityStore core.IdentityStore, channelStore core.ChannelStore) UpdatePasswordFunc

NewUpdatePasswordFunc creates an UpdatePasswordFunc from stores. If the user has no local channel (e.g. OAuth-only user), one is created automatically.

type VerifyEmailFunc

type VerifyEmailFunc func(token string) error

func NewVerifyEmailFunc

func NewVerifyEmailFunc(identityStore core.IdentityStore, tokenStore core.TokenStore) VerifyEmailFunc

NewVerifyEmailFunc creates a VerifyEmailFunc from stores

Jump to

Keyboard shortcuts

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