Authentication

Server API

Configure the AuthInGo Go engine, mount email-password auth routes, and protect handlers with sessions.

Server API

The Go package is the source of truth for authentication. It owns user creation, password checks, session creation, cookies, and route protection.

Create an Auth instance

authingo.New requires a Store. The built-in PostgreSQL adapter is the quickest path.

db, err := sql.Open("pgx", os.Getenv("DATABASE_URL"))
if err != nil {
	log.Fatal(err)
}

auth := authingo.New(authingo.Options{
	Store: postgres.NewAdapter(db),
})

AuthInGo defaults to secure, host-only cookies: SameSite=Lax for the access session cookie and SameSite=Strict for the refresh cookie. Embedded demos or other truly cross-site deployments can override this explicitly:

secure := true

auth := authingo.New(authingo.Options{
	Store: postgres.NewAdapter(db),
	Cookies: authingo.CookieOptions{
		Secure:          &secure,
		SessionSameSite: http.SameSiteNoneMode,
		RefreshSameSite: http.SameSiteNoneMode,
	},
})

Mount built-in routes

Mount the handler anywhere in your app. Most examples use /api/auth.

mux := http.NewServeMux()
mux.Handle("/api/auth/", http.StripPrefix("/api/auth", auth.Handler()))

This exposes:

MethodPathBodyResponse
POST/sign-up{ "email": string, "password": string, "name": string }{ "user": User }
POST/sign-in{ "email": string, "password": string }{ "user": User }
GET/sessionnone{ "user": User, "session": Session }
POST/refreshnone{ "user": User }
POST/sign-outnone{ "success": true }

All built-in POST routes require X-Authingo-Client: true. The React SDK adds this header automatically.

Protect routes

Wrap private handlers with RequireAuth.

private.go
mux.Handle("/api/me", auth.RequireAuth(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	user, ok := r.Context().Value(authingo.UserContextKey).(*authingo.User)
	if !ok {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]any{
		"user": user,
	})
})))

Protected requests must include:

X-Authingo-Client: true

Why the extra header?

Cookies are sent automatically by browsers. The X-Authingo-Client header gives your server a simple additional signal that the request came from code intentionally using AuthInGo.

CORS for a separate frontend

If your frontend and backend run on different origins, allow credentials and the AuthInGo header.

cors.go
func cors(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000")
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type, X-Authingo-Client")
		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")

		if r.Method == http.MethodOptions {
			w.WriteHeader(http.StatusOK)
			return
		}

		next.ServeHTTP(w, r)
	})
}

Full example

Connect storage

db, err := sql.Open("pgx", os.Getenv("DATABASE_URL"))
if err != nil {
	log.Fatal(err)
}

Initialize AuthInGo

auth := authingo.New(authingo.Options{
	Store: postgres.NewAdapter(db),
})

Mount public and private routes

mux := http.NewServeMux()
mux.Handle("/api/auth/", http.StripPrefix("/api/auth", auth.Handler()))
mux.Handle("/api/me", auth.RequireAuth(http.HandlerFunc(meHandler)))

Session cleanup

authingo.New starts a daily cleanup loop and calls Store.CleanupExpiredSessions with a short timeout. The PostgreSQL adapter deletes expired sessions in bounded batches and session lookups only authenticate rows where both expires_at and refresh_expires_at are still in the future.

On this page