https://github.com/josuebrunel/ezauth
Simple and easy to use authentication library for Golang
https://github.com/josuebrunel/ezauth
authentication betterauth easy go golang jwt jwt-authentication library oauth2 simple
Last synced: about 2 months ago
JSON representation
Simple and easy to use authentication library for Golang
- Host: GitHub
- URL: https://github.com/josuebrunel/ezauth
- Owner: josuebrunel
- License: mit
- Created: 2025-12-01T21:39:27.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-02-19T02:07:08.000Z (about 2 months ago)
- Last Synced: 2026-02-19T07:40:39.018Z (about 2 months ago)
- Topics: authentication, betterauth, easy, go, golang, jwt, jwt-authentication, library, oauth2, simple
- Language: Go
- Homepage: https://josuebrunel.github.io/ezauth/
- Size: 7.24 MB
- Stars: 37
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# ezauth
[](https://github.com/josuebrunel/ezauth/actions/workflows/ci.yml)
[](https://josuebrunel.github.io/ezauth/)
[](https://pkg.go.dev/github.com/josuebrunel/ezauth)
Simple and easy to use authentication library for Golang.
`ezauth` can be used as a standalone authentication service or embedded directly into your Go application as a library.
## Features
- Email/Password Authentication (Register, Login)
- JWT based sessions (Access & Refresh Tokens, Refresh Token Rotation)
- OAuth2 Support (Google, GitHub, Facebook)
- Password Reset and Passwordless (Magic Link) authentication
- Extended User Profiles (First Name, Last Name, Locale, Timezone, Roles, etc.)
- SQLite, PostgreSQL, and MySQL support
- API Key Protection for endpoints
- Built-in Middleware for route protection
- Swagger API Documentation
## Usage
### As a Standalone Service
You can run `ezauth` as a separate service that handles authentication for your microservices.
1. **Configuration**: Set environment variables.
```bash
export EZAUTH_ADDR=":8080"
export EZAUTH_API_KEY="your-master-api-key"
export EZAUTH_BASE_URL="http://localhost:8080"
export EZAUTH_DB_DIALECT="sqlite3" # or "postgres" or "mysql"
export EZAUTH_DB_DSN="auth.db" # for mysql: "user:pass@tcp(localhost:3306)/dbname?parseTime=true"
export EZAUTH_DB_SCHEMA="public" # Optional: Database schema (PostgreSQL only)
export EZAUTH_JWT_SECRET="super-secret-key"
# SMTP (Optional - for Email features)
export EZAUTH_SMTP_HOST="smtp.example.com"
export EZAUTH_SMTP_PORT="587"
export EZAUTH_SMTP_USER="user@example.com"
export EZAUTH_SMTP_PASSWORD="password"
export EZAUTH_SMTP_FROM="noreply@example.com"
# Email Templates (Optional - customize email content)
# Uses Go text/template syntax: {{.Link}}, {{.Token}}, {{.Email}}
export EZAUTH_EMAIL_PASSWORDLESS_SUBJECT="Magic Link Login"
export EZAUTH_EMAIL_PASSWORDLESS_BODY="Click the following link to login: {{.Link}}"
export EZAUTH_EMAIL_PASSWORD_RESET_SUBJECT="Password Reset Request"
export EZAUTH_EMAIL_PASSWORD_RESET_BODY="Click the following link to reset your password: {{.Link}}"
# Pages & Redirects (For Form-based auth)
export EZAUTH_REDIRECT_AFTER_LOGIN="/"
export EZAUTH_REDIRECT_AFTER_REGISTER="/"
export EZAUTH_LOGIN_PAGE_URL="/login"
export EZAUTH_REGISTER_PAGE_URL="/register"
# OAuth2 (Optional)
export EZAUTH_OAUTH2_CALLBACK_URL="http://localhost:3000/callback"
# Google
export EZAUTH_OAUTH2_GOOGLE_CLIENT_ID="your-google-client-id"
export EZAUTH_OAUTH2_GOOGLE_CLIENT_SECRET="your-google-client-secret"
export EZAUTH_OAUTH2_GOOGLE_REDIRECT_URL="http://localhost:8080/auth/oauth2/google/callback"
export EZAUTH_OAUTH2_GOOGLE_SCOPES="email,profile"
# GitHub
export EZAUTH_OAUTH2_GITHUB_CLIENT_ID="your-github-client-id"
export EZAUTH_OAUTH2_GITHUB_CLIENT_SECRET="your-github-client-secret"
export EZAUTH_OAUTH2_GITHUB_REDIRECT_URL="http://localhost:8080/auth/oauth2/github/callback"
export EZAUTH_OAUTH2_GITHUB_SCOPES="user:email"
# Facebook
export EZAUTH_OAUTH2_FACEBOOK_CLIENT_ID="your-facebook-client-id"
export EZAUTH_OAUTH2_FACEBOOK_CLIENT_SECRET="your-facebook-client-secret"
export EZAUTH_OAUTH2_FACEBOOK_REDIRECT_URL="http://localhost:8080/auth/oauth2/facebook/callback"
export EZAUTH_OAUTH2_FACEBOOK_SCOPES="email"
```
2. **Build and Run**:
Build the binary from `cmd/ezauthapi/main.go`.
```bash
go build -o ezauthapi ./cmd/ezauthapi
```
Then, run the compiled binary:
```bash
./ezauthapi
```
### As a Library
Embed `ezauth` directly into your existing Go application.
```go
package main
import (
"fmt"
"log"
"net/http"
"os"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/josuebrunel/ezauth"
"github.com/josuebrunel/ezauth/pkg/config"
)
func main() {
// 1. Setup Config
os.Setenv("EZAUTH_API_KEY", "my-api-key")
os.Setenv("EZAUTH_JWT_SECRET", "my-jwt-key")
cfg, err := config.LoadConfig()
if err != nil {
log.Fatalf("Failed to load config: %v", err)
}
// 2. Initialize EzAuth
auth, err := ezauth.New(&cfg, "")
if err != nil {
log.Fatalf("Failed to initialize auth: %v", err)
}
// 3. Run migrations
if err := auth.Migrate(); err != nil {
log.Fatalf("Failed to migrate: %v", err)
}
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
// 4. Add session middleware (handles sessions and user loading)
r.Use(auth.SessionMiddleware)
// 5. Mount Auth Routes
r.Mount("/auth", auth.Handler)
// Protected Route Example
r.Get("/dashboard", func(w http.ResponseWriter, r *http.Request) {
// Retrieve the authenticated user
user, err := auth.GetSessionUser(r.Context())
if err != nil {
http.Redirect(w, r, "/auth/login", http.StatusSeeOther)
return
}
w.Write([]byte(fmt.Sprintf("Welcome, %s!", user.Email)))
})
http.ListenAndServe(":3000", r)
}
```
## Session Management (Cookies)
When using the Form-based handlers, `ezauth` manages sessions using HTTP-only cookies via the `scs` session manager. The cookie name is `ezauthsess`.
Inside the session, the Access Token and Refresh Token are stored under the key `tokens`.
You can retrieve them in your application using the helper method:
```go
tokens, err := auth.GetSessionTokens(ctx)
if err == nil {
accessToken := tokens["access_token"]
refreshToken := tokens["refresh_token"]
// ...
}
```
### Retrieving the Authenticated User
You can retrieve the full user object from the session using `auth.GetSessionUser(ctx)`.
> [!IMPORTANT]
> You **MUST** mount the session middleware on your router for this to work.
```go
// 1. Mount session middleware
r.Use(auth.SessionMiddleware)
// 2. In your handler
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
if !auth.IsAuthenticated(r.Context()) {
http.Redirect(w, r, "/login", http.StatusSeeOther)
return
}
user, _ := auth.GetSessionUser(r.Context())
fmt.Println("User:", user.Email)
})
```
### Handling Errors and Success Messages
When using form-based handlers, errors and success messages are stored as flash messages in the session. Flash messages are one-time messages that are automatically cleared after being read.
```go
r.Get("/login", func(w http.ResponseWriter, r *http.Request) {
// Get flash messages (auto-cleared after read)
errorMsg := auth.GetErrorMessage(r.Context())
successMsg := auth.GetSuccessMessage(r.Context())
// Pass to template for display
data := map[string]string{
"Error": errorMsg,
"Success": successMsg,
}
tmpl.Execute(w, data)
})
```
### CSRF Protection
When using the form-based handlers (e.g., `POST /auth/login`), `ezauth` automatically enforces CSRF protection using `filippo.io/csrf/gorilla` and your `EZAUTH_JWT_SECRET`.
**Note on Tokens vs Headers:**
This library relies entirely on modern browser **Fetch Metadata headers** (e.g. `Sec-Fetch-Site`, `Origin`) to enforce same-origin requests dynamically, mirroring the upcoming Go 1.25 standard library CSRF protections.
Because of this, **hidden CSRF tokens in your HTML forms are completely optional and ignored during validation.** However, if you are integrating with frontend frameworks or legacy systems that *expect* a token to be present, `ezauth` provides helpers to seamlessly generate dummy tokens to satisfy those requirements:
```go
import "github.com/josuebrunel/ezauth"
// In your custom handler (ensure it's wrapped with the same CSRF middleware as ezauth)
r.Get("/my-custom-login", func(w http.ResponseWriter, r *http.Request) {
data := map[string]interface{}{
// Generate a pre-built field
"csrfField": ezauth.CSRFTemplateField(r),
// Or get the raw string if you need it for AJAX headers (X-CSRF-Token)
"csrfToken": ezauth.CSRFToken(r),
}
tmpl.Execute(w, data)
})
```
> [!NOTE]
> If you are using the JSON API endpoints (`/auth/api/*`) instead of the web forms, CSRF is disabled automatically since they use standard JWT Bearer Auth without cookies.
## Helper Functions
`ezauth` provides package-level helper functions for convenient access to authentication context, useful in handlers or templates.
> [!IMPORTANT]
> These functions require the appropriate middleware (`SessionMiddleware`, `LoadUserMiddleware`, or `AuthMiddleware`) to be mounted on the router path.
```go
import "github.com/josuebrunel/ezauth"
func MyHandler(w http.ResponseWriter, r *http.Request) {
// Check if authenticated
if ezauth.IsAuthenticated(r.Context()) {
// ...
}
// Get User ID (works with both Session and JWT auth)
userID, err := ezauth.GetUserID(r.Context())
// Get User Object (requires LoadUserMiddleware or SessionMiddleware)
user, err := ezauth.GetUser(r.Context())
if err == nil {
// Check for role
if user.HasRole("admin") {
// ...
}
// Get Metadata with type safety
if theme, ok := models.GetMeta[string](user, "theme"); ok {
// use theme
}
}
}
```
### User Model Helpers
The `User` struct includes helper methods for common operations:
- `HasRole(role string) bool`: Checks if the user has a specific role.
- `GetMeta[T any](user, key) (T, bool)`: Retrieves a value from `UserMetadata` with type casting.
- `SetMeta(key, value)`: Sets a value in `UserMetadata`.
- `GetAppMeta[T any](user, key) (T, bool)`: Retrieves a value from `AppMetadata`.
- `SetAppMeta(key, value)`: Sets a value in `AppMetadata`.
## API Endpoints
### Form-based Handlers (Cookies & Redirects)
These endpoints accept `application/x-www-form-urlencoded`, set secure cookies, and redirect.
| Method | Endpoint | Description |
| ------ | ---------------------------------- | --------------------------------------------------------------------------- |
| POST | `/auth/register` | Register a new user |
| POST | `/auth/login` | Login and set cookies |
| POST | `/auth/logout` | Clear cookies and logout |
| POST | `/auth/password-reset/request` | Request password reset link |
| POST | `/auth/password-reset/confirm` | Confirm password reset |
| POST | `/auth/passwordless/request` | Request magic link |
| GET | `/auth/passwordless/login` | Login via magic link |
| GET | `/auth/oauth2/{provider}/login` | Login via OAuth2 provider |
| GET | `/auth/oauth2/{provider}/callback` | OAuth2 provider callback. URL: `{base_url}/auth/oauth2/{provider}/callback` |
#### Form Field Reference
| Endpoint | Required Fields | Optional Fields |
| :----------------------------- | :-------------------------------------- | :----------------------------------------------------------------- |
| `/auth/register` | `email`, `password`, `password_confirm` | `first_name`, `last_name`, `locale`, `timezone`, `roles`, `meta_*` |
| `/auth/login` | `email`, `password` | |
| `/auth/password-reset/request` | `email` | |
| `/auth/password-reset/confirm` | `token`, `password` | |
| `/auth/passwordless/request` | `email` | |
| `/auth/passwordless/login` | `token` (query param) | |
> [!NOTE]
> Passwords must be at least 8 characters long.
### API Handlers (JSON)
These endpoints accept `application/json` and return JSON responses.
| Method | Endpoint | Description |
| ------ | ---------------------------------- | --------------------------------- |
| POST | `/auth/api/register` | Register a new user |
| POST | `/auth/api/login` | Login and receive tokens |
| POST | `/auth/api/token/refresh` | Refresh access token |
| POST | `/auth/api/password-reset/request` | Request password reset link |
| POST | `/auth/api/password-reset/confirm` | Confirm password reset |
| POST | `/auth/api/passwordless/request` | Request magic link |
| GET | `/auth/api/passwordless/login` | Login via magic link |
| GET | `/auth/api/userinfo` | Get current user info (Protected) |
| POST | `/auth/api/logout` | Revoke refresh token (Protected) |
| DELETE | `/auth/api/user` | Delete account (Protected) |
## Middlewares
`ezauth` provides several "plug and play" middlewares to protect your routes and manage user sessions. These are available directly on the `EzAuth` instance.
### `auth.SessionMiddleware`
**Usage**: `r.Use(auth.SessionMiddleware)`
This is the recommended middleware for most applications. It combines session management and user loading.
- Loads and saves session data (cookies).
- Populates `GetSessionUser(ctx)` for downstream handlers.
### `auth.LoginRequiredMiddleware`
**Usage**: `r.Use(auth.LoginRequiredMiddleware)`
Protects routes by requiring authentication.
- **Browser requests**: Redirects to the configured `EZAUTH_LOGIN_PAGE_URL`.
- **API requests**: Returns `401 Unauthorized`.
### `auth.LoadUserMiddleware`
**Usage**: `r.Use(auth.LoadUserMiddleware)`
Loads the user into the context *without* managing the session itself. Use this if you are using `auth.Handler.Session.LoadAndSave` manually or have a custom session setup.
### `auth.AuthMiddleware`
**Usage**: `r.Use(auth.AuthMiddleware)`
Protects API routes using JWT Bearer tokens in the `Authorization` header.
- Validates the token signature.
- Sets the user ID in the context.
## Swagger Documentation
To generate the Swagger documentation, run:
```bash
make swagger
```
The Swagger UI is available at `/swagger/index.html`.
## Examples
Check out the `_example` directory for ready-to-use examples:
* [`go-server`](_example/go-server): A complete, plug-and-play example showing how to integrate `ezauth` with a Go web server.
* [`javascript-client`](_example/javascript-client): An example JavaScript client interacting with the `ezauth` API.