{"id":43159408,"url":"https://github.com/josuebrunel/ezauth","last_synced_at":"2026-02-21T02:00:24.381Z","repository":{"id":333082982,"uuid":"1107981840","full_name":"josuebrunel/ezauth","owner":"josuebrunel","description":"Simple and easy to use authentication library for Golang ","archived":false,"fork":false,"pushed_at":"2026-02-19T02:07:08.000Z","size":7587,"stargazers_count":37,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-02-19T07:40:39.018Z","etag":null,"topics":["authentication","betterauth","easy","go","golang","jwt","jwt-authentication","library","oauth2","simple"],"latest_commit_sha":null,"homepage":"https://josuebrunel.github.io/ezauth/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/josuebrunel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-01T21:39:27.000Z","updated_at":"2026-02-19T02:07:12.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/josuebrunel/ezauth","commit_stats":null,"previous_names":["josuebrunel/ezauth"],"tags_count":28,"template":false,"template_full_name":null,"purl":"pkg:github/josuebrunel/ezauth","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josuebrunel%2Fezauth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josuebrunel%2Fezauth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josuebrunel%2Fezauth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josuebrunel%2Fezauth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/josuebrunel","download_url":"https://codeload.github.com/josuebrunel/ezauth/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josuebrunel%2Fezauth/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29671513,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T00:11:43.526Z","status":"online","status_checked_at":"2026-02-21T02:00:07.432Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","betterauth","easy","go","golang","jwt","jwt-authentication","library","oauth2","simple"],"created_at":"2026-02-01T01:14:24.839Z","updated_at":"2026-02-21T02:00:24.375Z","avatar_url":"https://github.com/josuebrunel.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ezauth\n\n[![Tests](https://github.com/josuebrunel/ezauth/actions/workflows/ci.yml/badge.svg)](https://github.com/josuebrunel/ezauth/actions/workflows/ci.yml)\n[![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://josuebrunel.github.io/ezauth/)\n[![Go Reference](https://pkg.go.dev/badge/github.com/josuebrunel/ezauth.svg)](https://pkg.go.dev/github.com/josuebrunel/ezauth)\n\nSimple and easy to use authentication library for Golang.\n\n`ezauth` can be used as a standalone authentication service or embedded directly into your Go application as a library.\n\n## Features\n\n- Email/Password Authentication (Register, Login)\n- JWT based sessions (Access \u0026 Refresh Tokens, Refresh Token Rotation)\n- OAuth2 Support (Google, GitHub, Facebook)\n- Password Reset and Passwordless (Magic Link) authentication\n- Extended User Profiles (First Name, Last Name, Locale, Timezone, Roles, etc.)\n- SQLite, PostgreSQL, and MySQL support\n- API Key Protection for endpoints\n- Built-in Middleware for route protection\n- Swagger API Documentation\n\n## Usage\n\n### As a Standalone Service\n\nYou can run `ezauth` as a separate service that handles authentication for your microservices.\n\n1. **Configuration**: Set environment variables.\n   ```bash\n   export EZAUTH_ADDR=\":8080\"\n   export EZAUTH_API_KEY=\"your-master-api-key\"\n   export EZAUTH_BASE_URL=\"http://localhost:8080\"\n   export EZAUTH_DB_DIALECT=\"sqlite3\"  # or \"postgres\" or \"mysql\"\n   export EZAUTH_DB_DSN=\"auth.db\"      # for mysql: \"user:pass@tcp(localhost:3306)/dbname?parseTime=true\"\n   export EZAUTH_DB_SCHEMA=\"public\"    # Optional: Database schema (PostgreSQL only)\n   export EZAUTH_JWT_SECRET=\"super-secret-key\"\n\n   # SMTP (Optional - for Email features)\n   export EZAUTH_SMTP_HOST=\"smtp.example.com\"\n   export EZAUTH_SMTP_PORT=\"587\"\n   export EZAUTH_SMTP_USER=\"user@example.com\"\n   export EZAUTH_SMTP_PASSWORD=\"password\"\n   export EZAUTH_SMTP_FROM=\"noreply@example.com\"\n\n   # Email Templates (Optional - customize email content)\n   # Uses Go text/template syntax: {{.Link}}, {{.Token}}, {{.Email}}\n   export EZAUTH_EMAIL_PASSWORDLESS_SUBJECT=\"Magic Link Login\"\n   export EZAUTH_EMAIL_PASSWORDLESS_BODY=\"Click the following link to login: {{.Link}}\"\n   export EZAUTH_EMAIL_PASSWORD_RESET_SUBJECT=\"Password Reset Request\"\n   export EZAUTH_EMAIL_PASSWORD_RESET_BODY=\"Click the following link to reset your password: {{.Link}}\"\n\n   # Pages \u0026 Redirects (For Form-based auth)\n   export EZAUTH_REDIRECT_AFTER_LOGIN=\"/\"\n   export EZAUTH_REDIRECT_AFTER_REGISTER=\"/\"\n   export EZAUTH_LOGIN_PAGE_URL=\"/login\"\n   export EZAUTH_REGISTER_PAGE_URL=\"/register\"\n\n   # OAuth2 (Optional)\n   export EZAUTH_OAUTH2_CALLBACK_URL=\"http://localhost:3000/callback\"\n\n   # Google\n   export EZAUTH_OAUTH2_GOOGLE_CLIENT_ID=\"your-google-client-id\"\n   export EZAUTH_OAUTH2_GOOGLE_CLIENT_SECRET=\"your-google-client-secret\"\n   export EZAUTH_OAUTH2_GOOGLE_REDIRECT_URL=\"http://localhost:8080/auth/oauth2/google/callback\"\n   export EZAUTH_OAUTH2_GOOGLE_SCOPES=\"email,profile\"\n\n   # GitHub\n   export EZAUTH_OAUTH2_GITHUB_CLIENT_ID=\"your-github-client-id\"\n   export EZAUTH_OAUTH2_GITHUB_CLIENT_SECRET=\"your-github-client-secret\"\n   export EZAUTH_OAUTH2_GITHUB_REDIRECT_URL=\"http://localhost:8080/auth/oauth2/github/callback\"\n   export EZAUTH_OAUTH2_GITHUB_SCOPES=\"user:email\"\n\n   # Facebook\n   export EZAUTH_OAUTH2_FACEBOOK_CLIENT_ID=\"your-facebook-client-id\"\n   export EZAUTH_OAUTH2_FACEBOOK_CLIENT_SECRET=\"your-facebook-client-secret\"\n   export EZAUTH_OAUTH2_FACEBOOK_REDIRECT_URL=\"http://localhost:8080/auth/oauth2/facebook/callback\"\n   export EZAUTH_OAUTH2_FACEBOOK_SCOPES=\"email\"\n   ```\n\n2. **Build and Run**:\n   Build the binary from `cmd/ezauthapi/main.go`.\n   ```bash\n   go build -o ezauthapi ./cmd/ezauthapi\n   ```\n   Then, run the compiled binary:\n   ```bash\n   ./ezauthapi\n   ```\n\n### As a Library\n\nEmbed `ezauth` directly into your existing Go application.\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"log\"\n    \"net/http\"\n    \"os\"\n\n    \"github.com/go-chi/chi/v5\"\n    \"github.com/go-chi/chi/v5/middleware\"\n    \"github.com/josuebrunel/ezauth\"\n    \"github.com/josuebrunel/ezauth/pkg/config\"\n)\n\nfunc main() {\n    // 1. Setup Config\n    os.Setenv(\"EZAUTH_API_KEY\", \"my-api-key\")\n    os.Setenv(\"EZAUTH_JWT_SECRET\", \"my-jwt-key\")\n    \n    cfg, err := config.LoadConfig()\n    if err != nil {\n        log.Fatalf(\"Failed to load config: %v\", err)\n    }\n\n    // 2. Initialize EzAuth\n    auth, err := ezauth.New(\u0026cfg, \"\")\n    if err != nil {\n        log.Fatalf(\"Failed to initialize auth: %v\", err)\n    }\n\n    // 3. Run migrations\n    if err := auth.Migrate(); err != nil {\n        log.Fatalf(\"Failed to migrate: %v\", err)\n    }\n\n    r := chi.NewRouter()\n    r.Use(middleware.Logger)\n    r.Use(middleware.Recoverer)\n\n    // 4. Add session middleware (handles sessions and user loading)\n    r.Use(auth.SessionMiddleware)\n\n    // 5. Mount Auth Routes\n    r.Mount(\"/auth\", auth.Handler)\n\n    // Protected Route Example\n    r.Get(\"/dashboard\", func(w http.ResponseWriter, r *http.Request) {\n        // Retrieve the authenticated user\n        user, err := auth.GetSessionUser(r.Context())\n\n        if err != nil {\n            http.Redirect(w, r, \"/auth/login\", http.StatusSeeOther)\n            return\n        }\n\n        w.Write([]byte(fmt.Sprintf(\"Welcome, %s!\", user.Email)))\n    })\n\n    http.ListenAndServe(\":3000\", r)\n}\n```\n\n## Session Management (Cookies)\n\nWhen using the Form-based handlers, `ezauth` manages sessions using HTTP-only cookies via the `scs` session manager. The cookie name is `ezauthsess`.\n\nInside the session, the Access Token and Refresh Token are stored under the key `tokens`.\n\nYou can retrieve them in your application using the helper method:\n\n```go\ntokens, err := auth.GetSessionTokens(ctx)\nif err == nil {\n    accessToken := tokens[\"access_token\"]\n    refreshToken := tokens[\"refresh_token\"]\n    // ...\n}\n```\n\n### Retrieving the Authenticated User\n\nYou can retrieve the full user object from the session using `auth.GetSessionUser(ctx)`.\n\n\u003e [!IMPORTANT]\n\u003e You **MUST** mount the session middleware on your router for this to work.\n\n```go\n// 1. Mount session middleware\nr.Use(auth.SessionMiddleware)\n\n// 2. In your handler\nr.Get(\"/\", func(w http.ResponseWriter, r *http.Request) {\n    if !auth.IsAuthenticated(r.Context()) {\n        http.Redirect(w, r, \"/login\", http.StatusSeeOther)\n        return\n    }\n\n    user, _ := auth.GetSessionUser(r.Context())\n    fmt.Println(\"User:\", user.Email)\n})\n```\n\n### Handling Errors and Success Messages\n\nWhen 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.\n\n```go\nr.Get(\"/login\", func(w http.ResponseWriter, r *http.Request) {\n    // Get flash messages (auto-cleared after read)\n    errorMsg := auth.GetErrorMessage(r.Context())\n    successMsg := auth.GetSuccessMessage(r.Context())\n\n    // Pass to template for display\n    data := map[string]string{\n        \"Error\":   errorMsg,\n        \"Success\": successMsg,\n    }\n    tmpl.Execute(w, data)\n})\n```\n\n### CSRF Protection\n\nWhen 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`. \n\n**Note on Tokens vs Headers:** \nThis 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.\n\nBecause 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:\n\n```go\nimport \"github.com/josuebrunel/ezauth\"\n\n// In your custom handler (ensure it's wrapped with the same CSRF middleware as ezauth)\nr.Get(\"/my-custom-login\", func(w http.ResponseWriter, r *http.Request) {\n    data := map[string]interface{}{\n        // Generate a pre-built \u003cinput type=\"hidden\"\u003e field\n        \"csrfField\": ezauth.CSRFTemplateField(r),\n        \n        // Or get the raw string if you need it for AJAX headers (X-CSRF-Token)\n        \"csrfToken\": ezauth.CSRFToken(r), \n    }\n    tmpl.Execute(w, data)\n})\n```\n\n\u003e [!NOTE]\n\u003e 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.\n\n## Helper Functions\n\n`ezauth` provides package-level helper functions for convenient access to authentication context, useful in handlers or templates.\n\n\u003e [!IMPORTANT]\n\u003e These functions require the appropriate middleware (`SessionMiddleware`, `LoadUserMiddleware`, or `AuthMiddleware`) to be mounted on the router path.\n\n```go\nimport \"github.com/josuebrunel/ezauth\"\n\nfunc MyHandler(w http.ResponseWriter, r *http.Request) {\n    // Check if authenticated\n    if ezauth.IsAuthenticated(r.Context()) {\n        // ...\n    }\n\n    // Get User ID (works with both Session and JWT auth)\n    userID, err := ezauth.GetUserID(r.Context())\n\n    // Get User Object (requires LoadUserMiddleware or SessionMiddleware)\n    user, err := ezauth.GetUser(r.Context())\n    if err == nil {\n        // Check for role\n        if user.HasRole(\"admin\") {\n            // ...\n        }\n\n        // Get Metadata with type safety\n        if theme, ok := models.GetMeta[string](user, \"theme\"); ok {\n            // use theme\n        }\n    }\n}\n```\n\n### User Model Helpers\n\nThe `User` struct includes helper methods for common operations:\n\n- `HasRole(role string) bool`: Checks if the user has a specific role.\n- `GetMeta[T any](user, key) (T, bool)`: Retrieves a value from `UserMetadata` with type casting.\n- `SetMeta(key, value)`: Sets a value in `UserMetadata`.\n- `GetAppMeta[T any](user, key) (T, bool)`: Retrieves a value from `AppMetadata`.\n- `SetAppMeta(key, value)`: Sets a value in `AppMetadata`.\n\n## API Endpoints\n\n### Form-based Handlers (Cookies \u0026 Redirects)\n\nThese endpoints accept `application/x-www-form-urlencoded`, set secure cookies, and redirect.\n\n| Method | Endpoint                           | Description                                                                 |\n| ------ | ---------------------------------- | --------------------------------------------------------------------------- |\n| POST   | `/auth/register`                   | Register a new user                                                         |\n| POST   | `/auth/login`                      | Login and set cookies                                                       |\n| POST   | `/auth/logout`                     | Clear cookies and logout                                                    |\n| POST   | `/auth/password-reset/request`     | Request password reset link                                                 |\n| POST   | `/auth/password-reset/confirm`     | Confirm password reset                                                      |\n| POST   | `/auth/passwordless/request`       | Request magic link                                                          |\n| GET    | `/auth/passwordless/login`         | Login via magic link                                                        |\n| GET    | `/auth/oauth2/{provider}/login`    | Login via OAuth2 provider                                                   |\n| GET    | `/auth/oauth2/{provider}/callback` | OAuth2 provider callback. URL: `{base_url}/auth/oauth2/{provider}/callback` |\n\n#### Form Field Reference\n\n| Endpoint                       | Required Fields                         | Optional Fields                                                    |\n| :----------------------------- | :-------------------------------------- | :----------------------------------------------------------------- |\n| `/auth/register`               | `email`, `password`, `password_confirm` | `first_name`, `last_name`, `locale`, `timezone`, `roles`, `meta_*` |\n| `/auth/login`                  | `email`, `password`                     |                                                                    |\n| `/auth/password-reset/request` | `email`                                 |                                                                    |\n| `/auth/password-reset/confirm` | `token`, `password`                     |                                                                    |\n| `/auth/passwordless/request`   | `email`                                 |                                                                    |\n| `/auth/passwordless/login`     | `token` (query param)                   |                                                                    |\n\n\u003e [!NOTE]\n\u003e Passwords must be at least 8 characters long.\n\n### API Handlers (JSON)\n\nThese endpoints accept `application/json` and return JSON responses.\n\n| Method | Endpoint                           | Description                       |\n| ------ | ---------------------------------- | --------------------------------- |\n| POST   | `/auth/api/register`               | Register a new user               |\n| POST   | `/auth/api/login`                  | Login and receive tokens          |\n| POST   | `/auth/api/token/refresh`          | Refresh access token              |\n| POST   | `/auth/api/password-reset/request` | Request password reset link       |\n| POST   | `/auth/api/password-reset/confirm` | Confirm password reset            |\n| POST   | `/auth/api/passwordless/request`   | Request magic link                |\n| GET    | `/auth/api/passwordless/login`     | Login via magic link              |\n| GET    | `/auth/api/userinfo`               | Get current user info (Protected) |\n| POST   | `/auth/api/logout`                 | Revoke refresh token (Protected)  |\n| DELETE | `/auth/api/user`                   | Delete account (Protected)        |\n\n## Middlewares\n \n `ezauth` provides several \"plug and play\" middlewares to protect your routes and manage user sessions. These are available directly on the `EzAuth` instance.\n \n ### `auth.SessionMiddleware`\n \n **Usage**: `r.Use(auth.SessionMiddleware)`\n \n This is the recommended middleware for most applications. It combines session management and user loading.\n - Loads and saves session data (cookies).\n - Populates `GetSessionUser(ctx)` for downstream handlers.\n \n ### `auth.LoginRequiredMiddleware`\n \n **Usage**: `r.Use(auth.LoginRequiredMiddleware)`\n \n Protects routes by requiring authentication.\n - **Browser requests**: Redirects to the configured `EZAUTH_LOGIN_PAGE_URL`.\n - **API requests**: Returns `401 Unauthorized`.\n \n ### `auth.LoadUserMiddleware`\n \n **Usage**: `r.Use(auth.LoadUserMiddleware)`\n \n 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.\n \n ### `auth.AuthMiddleware`\n \n **Usage**: `r.Use(auth.AuthMiddleware)`\n \n Protects API routes using JWT Bearer tokens in the `Authorization` header.\n - Validates the token signature.\n - Sets the user ID in the context.\n \n ## Swagger Documentation\n\nTo generate the Swagger documentation, run:\n\n```bash\nmake swagger\n```\n\nThe Swagger UI is available at `/swagger/index.html`.\n\n## Examples\n\nCheck out the `_example` directory for ready-to-use examples:\n\n*   [`go-server`](_example/go-server): A complete, plug-and-play example showing how to integrate `ezauth` with a Go web server.\n*   [`javascript-client`](_example/javascript-client): An example JavaScript client interacting with the `ezauth` API.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosuebrunel%2Fezauth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjosuebrunel%2Fezauth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosuebrunel%2Fezauth/lists"}