All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Pre-release note: All versions below are pre-release development milestones (
1.0.0-alpha.N). The first official public release will be1.0.0.
Future enhancements planned for the 1.0.0 official release.
- Custom login page branding per app (logo, colors)
- Configurable password policies per app (min length, complexity, history)
- Configurable token TTLs per application (currently global)
- SMS-based recovery (even if not primary 2FA method)
- Cross-application session groups — group multiple applications under a named session group so that a single authentication event is valid across all apps in the group (similar to Google's cross-product SSO)
- New models:
SessionGroupandSessionGroupApp(pkg/models/session_group.go); a session group belongs to a tenant, an application can belong to at most one group GlobalLogoutflag — whentrue, logging out of (or expiry of a session in) any app in the group immediately revokes the user's sessions in all other apps of the group- New package:
internal/sessiongroup/with two components:revoke.go— shared group-wide session revocation utility (used by both logout and expiry detection)expiry.go— real-time session expiry detection via Redis keyspace notifications (REDIS_NOTIFY_KEYSPACE_EVENTS=Ex) with a configurable periodic fallback scanner
- Admin API CRUD endpoints for session group management
- Admin GUI Session Groups page (create, edit, delete, manage member apps)
- New env vars for expiry detection (see Configuration section below)
- Docker Compose
redisservice updated with--notify-keyspace-events Exfor out-of-the-box real-time expiry support - New documentation:
docs/session-group-expiry.md
| Variable | Default | Description |
|---|---|---|
REDIS_NOTIFY_KEYSPACE_EVENTS |
(unset) | Set to Ex to enable Redis expired-key events for real-time expiry detection |
SESSION_GROUP_EXPIRY_REVOCATION_ENABLED |
true |
Enable/disable expiry-triggered group-wide session revocation |
SESSION_GROUP_EXPIRY_SCAN_INTERVAL |
5m |
Fallback periodic scan interval when keyspace notifications are not available |
SESSION_GROUP_KEYSYSPACE_NOTIF_ENABLED |
true |
Enable/disable the keyspace notification listener |
- Rich HTML editor in the Admin GUI email template editor, powered by CodeMirror 6
- Syntax highlighting, line numbers, and bracket matching for HTML email templates
- Template variable hinting — inline autocomplete for all available email template variables (e.g.
{{.VerifyURL}},{{.UserName}}) - Popup editor window — a dedicated full-screen editor window (
email_template_editor_window.tmpl) for distraction-free editing - Download helper scripts for CodeMirror assets:
scripts/download-codemirror-assets.sh(Linux/macOS)scripts/download-codemirror-assets.bat(Windows)
POST /oidc/:app_id/end_sessionnow triggers group-wide session revocation when the application belongs to a session group withGlobalLogout=true- Consistent behavior between standard logout (
POST /logout) and OIDC RP-initiated logout
- CORS allowed origins are now configurable through the Admin GUI Settings page, eliminating the need to restart the server for origin changes
- Settings are resolved with the existing 3-tier precedence: environment variable → database → built-in default
- Social-only users (registered exclusively via OAuth2) can now set a local password via
PUT /profile/passwordwithout providing a current password (since none exists) - Enables hybrid authentication for users who originally signed up through social login
- When a user attempts to re-register with an email already associated with a social account but not yet verified, the pending email verification is now re-triggered automatically
- Improves UX for users who signed up via social login and later try the email/password flow
- SMS 2FA configuration options and trusted device management are now correctly included in Admin GUI form submissions (previously missing from
formData)
- Trusted devices list — the
GET /2fa/trusted-devicesendpoint (and its admin counterpart) now filters out expired trusted devices; only active, non-expired devices are returned - Change password: current password optional —
PUT /profile/passwordno longer requires thecurrent_passwordfield when the user has no local password set (social-only accounts); fixes a 422 validation error that prevented social users from ever setting a password - RBAC member role: settings permissions — the system
memberrole was missingsettings:readandsettings:writepermissions, causing 403 errors on all 2FA self-service endpoints (TOTP setup, email 2FA, SMS 2FA, backup email, passkeys, trusted devices, phone management); SQL migration20260317_add_settings_permissions_to_member.sqlgrants these permissions to thememberrole in every existing application
-
CWE-269: API key empty scope privilege escalation (fix, closes #13) —
HasScope()ininternal/middleware/scope.gopreviously treated a DB-backed API key with an empty scope list as fully permissive, effectively granting admin-level access to any key with no scopes configured. The scope check now denies by default when the granted scope list is empty. To grant unrestricted access to a DB-backed key, the key must explicitly include the"*"scope. The staticADMIN_API_KEYenvironment variable remains unconditionally permissive and is unaffected by this change.Action required for existing installations: Any DB-backed API key that was intentionally created without scopes to obtain full access must be updated — add the
"*"scope or the specific scopes it needs via the Admin GUI or API.Credit: tinokyo (@Tinocio) — thank you for the detailed responsible disclosure including root cause analysis, proof of concept, and suggested fix.
- Dark mode table row styles improved for better visibility across all Admin GUI pages
- Full OIDC provider — the API can now act as a standards-compliant OIDC identity provider (opt-in via
OIDC_ENABLED=true) - Discovery document:
GET /.well-known/openid-configuration(global redirect) andGET /oidc/:app_id/.well-known/openid-configuration - JWKS endpoint:
GET /oidc/:app_id/.well-known/jwks.json - Authorization endpoint with login/consent UI:
GET /oidc/:app_id/authorize,POST /oidc/:app_id/authorize - Token endpoint (authorization_code, client_credentials, refresh_token grants):
POST /oidc/:app_id/token - UserInfo endpoint:
GET|POST /oidc/:app_id/userinfo - Token introspection:
POST /oidc/:app_id/introspect - Token revocation:
POST /oidc/:app_id/revoke - End session (RP-initiated logout):
GET|POST /oidc/:app_id/end_session - Admin API for OIDC client management:
POST|GET|PUT|DELETE /admin/oidc/apps/:id/clients, plus secret rotation - Admin GUI pages for managing OIDC clients (create, edit, delete, rotate secret)
- New models:
OIDCClient(pkg/models/oidc_client.go),OIDCAuthCode(pkg/models/oidc_auth_code.go) - New DTOs:
pkg/dto/oidc.go - New domain package:
internal/oidc/(handler, service, repository, id_token, keys) - New env vars:
OIDC_ENABLED,PUBLIC_URL,OIDC_ID_TOKEN_EXPIRATION_MINUTES,OIDC_AUTH_CODE_EXPIRATION_MINUTES - SQL migration:
20260306_add_oidc.sql - Supports scopes:
openid,profile,email,roles,offline_access; PKCE (S256); RS256 signed ID tokens
- Event-driven webhooks — applications can register HTTPS endpoints to receive real-time event notifications
- Events delivered:
user.registered,user.verified,user.login,user.password_changed,2fa.enabled,2fa.disabled,social.linked,social.unlinked - Delivery queue with async background dispatcher, automatic retries, and per-delivery status tracking
- Admin API:
GET|POST /admin/webhooks,GET|POST /admin/webhooks/apps/:app_id,PUT /admin/webhooks/:id/toggle,DELETE /admin/webhooks/:id, plus delivery history endpoints - App-scoped API:
GET|POST|PUT|DELETE /app/:id/webhooks,GET /app/:id/webhooks/deliveries - Admin GUI Webhooks page (create, toggle, delete, view delivery history)
- New models:
WebhookEndpoint(pkg/models/webhook_endpoint.go),WebhookDelivery(pkg/models/webhook_delivery.go) - New DTOs:
pkg/dto/webhook.go - New domain package:
internal/webhook/(handler, service, repository) - SQL migration:
20260305_add_webhooks.sql
- Account lockout — after configurable failed login attempts, accounts are temporarily locked with progressive backoff
- CAPTCHA trigger — after N failures, a CAPTCHA challenge is required before the next attempt
- Admin GUI: unlock button on User detail page (
PUT /gui/users/:id/unlock) - Admin API: configurable per-application settings via
20260305_add_app_bruteforce_settings.sqlmigration - New package:
internal/bruteforce/(service, captcha) - Login failure reasons tracked in Prometheus metrics (
auth_login_failure_totalwithreasonlabel)
- IP-based access control — allow or block login attempts by CIDR range or country code, per application
- Uses MaxMind GeoLite2 database for country/city resolution (gracefully disabled when
GEOIP_DB_PATHis not set) - Admin API:
GET|POST|PUT|DELETE /admin/apps/:id/ip-rules,GET /admin/apps/:id/ip-rules/:rule_id,POST /admin/apps/:id/ip-rules/check - Admin GUI IP Rules page (create, edit, delete, test access)
- Anomaly detection enhanced: new IP addresses now resolve to city/country and trigger notification emails
- New model:
IPRule(pkg/models/ip_rule.go) - New DTOs:
pkg/dto/ip_rule.go - New package:
internal/geoip/(service, ip_rules repository/evaluator) - New env var:
GEOIP_DB_PATH(optional — path to MaxMind.mmdbfile)
GET /health— checks PostgreSQL, Redis, and SMTP connectivity with latency; returns 200 when healthy, 503 when any component is downGET /metrics— Prometheus exposition format; requires Admin API Key- Metrics tracked:
http_requests_total,http_request_duration_seconds,auth_login_success_total,auth_login_failure_total,auth_register_total,auth_logout_total, DB connection pool gauges,active_sessions_total PrometheusMiddlewareinstruments every HTTP request automatically- Admin GUI Monitoring page with live health and metrics summary
- New package:
internal/health/(handler) - New DTOs:
pkg/dto/health.go
- SMS-based 2FA via Twilio — users can receive one-time codes by SMS as a second factor
- New endpoints:
POST /2fa/sms/enable(protected),POST /2fa/sms/resend(public, during login) - Phone number management:
POST /phone(add),POST /phone/verify,DELETE /phone,GET /phone/status - New package:
internal/sms/(sender interface, Twilio implementation) - New env vars:
SMS_PROVIDER("twilio"or empty to disable),SMS_TWILIO_ACCOUNT_SID,SMS_TWILIO_AUTH_TOKEN,SMS_TWILIO_FROM_NUMBER - Graceful degradation: SMS is silently disabled when
SMS_PROVIDERis not set
- Users can register a secondary email address as a 2FA fallback channel
- New endpoints:
POST /2fa/backup-email(add),DELETE /2fa/backup-email(remove),GET /2fa/backup-email/status,POST /2fa/backup-email/enable,POST /2fa/backup-email/disable,GET /2fa/backup-email/verify,POST /2fa/backup-email/resend - Admin GUI My Account: manage backup email for admin accounts
- SQL migration:
20260309_seed_backup_email_verification_type.sql
- "Remember this device" — users can mark a device as trusted to skip 2FA for a configurable period
- New endpoints:
GET /2fa/trusted-devices,DELETE /2fa/trusted-devices/:id,DELETE /2fa/trusted-devices(revoke all) - Admin API:
GET|DELETE /admin/users/:id/trusted-devices,DELETE /admin/users/:id/trusted-devices/:device_id - Admin GUI: trusted device list on User detail page, My Account page for admin self-management
- New model:
TrustedDevice(pkg/models/trusted_device.go) - New repository:
internal/twofa/trusted_device_repository.go
GET /activity-logs/export— export the authenticated user's logs (CSV)GET /admin/activity-logs/export— export all users' logs (CSV, admin-only)GET /gui/logs/export— export from admin GUI
GET /admin/users/export— export user list as CSVPOST /admin/users/import— bulk import users from CSV/JSON- Admin GUI: export button and import modal on Users page
- New DTO:
pkg/dto/user_import.go - New helper:
internal/user/import.go
- API keys now support granular permission scopes to limit which endpoints a key can access
- Usage tracking: every API key request is recorded in
api_key_usagestable; viewable on a per-key usage page in the Admin GUI - Expiry notification service: background service emails admins when API keys are nearing expiration
- New model:
ApiKeyUsage(pkg/models/api_key_usage.go) - New service:
internal/admin/apikey_notification.go - SQL migrations:
20260305_add_api_key_scopes_columns.sql,20260305_create_api_key_usages.sql,20260305_seed_api_key_expiring_soon_email_type.sql
GET /2fa/methods— public endpoint returning the 2FA methods enabled for the current application; used by login UI to show available options
GET /app-config/:app_id— returns the public login configuration for an application (enabled auth methods, feature flags); used by frontend login/register UI without authentication
- Per-application configuration for the path segments used in email action links (verify email, password reset, magic link)
- SQL migration:
20260314_add_app_link_paths.sql
- Applications can now enforce 2FA for all users via a feature flag
- SQL migration:
20260310_add_two_fa_previous_method.sql— tracks the previous 2FA method when switching methods
- New per-application customization fields (frontend URL, branding hints)
- SQL migration:
20260311_add_app_customization.sql
- Seeded new security-related email types: anomaly notifications (new device login, suspicious activity)
- SQL migrations:
20260305_seed_security_email_types.sql,20260305_seed_api_key_expiring_soon_email_type.sql
- When anomaly detection fires, the system now sends email notifications to the affected user
- Two email types:
new_device_loginandsuspicious_activity - Wired via callback from log service to email service in
main.go
- Successful OIDC logins are recorded in the activity log with
OIDC_LOGINevent type
- Previous 2FA method is preserved in
two_fa_previous_methodfield when a user switches methods, enabling smoother recovery flows
- Sessions now track a
statusfield for richer state management (active, revoked, expired) - User token blacklist is cleared when a new session starts (prevents stale blacklist entries from blocking valid new sessions)
middleware.Scope()added for API key scope validation on app-scoped routes- Rate limits added for OIDC endpoints (
OIDCAuthorizeRateLimit,OIDCTokenRateLimit,OIDCUserInfoRateLimit,OIDCIntrospectRateLimit,OIDCRevokeRateLimit) - Rate limits added for 2FA SMS and backup-email resend endpoints
- New pages: Monitoring (health check + Prometheus metrics summary), Webhooks, OIDC Clients, IP Rules, API Key Usage
- Users page: unlock button for brute-force-locked accounts, trusted device management per user
- My Account page: backup email management, trusted device management
- Dashboard: activity chart updated
- Session: clearing the token blacklist on new session creation prevents a race condition where a fresh token was incorrectly rejected after logout followed by immediate re-login
- Full FIDO2/WebAuthn passkey support — Registration, two-factor authentication, and passwordless login
- New endpoints:
POST /passkey/register/begin|finish,GET /passkeys,PUT /passkeys,DELETE /passkeys,POST /2fa/passkey/begin|finish,POST /passkey/login/begin|finish - New model:
WebauthnCredential(pkg/models/webauthn_credential.go) - New DTOs:
pkg/dto/webauthn.go(passkey list, rename, delete, registration/login options) - New configuration:
WEBAUTHN_RP_ID,WEBAUTHN_RP_NAME,WEBAUTHN_RP_ORIGINSenvironment variables - New domain package:
internal/webauthn/(handler, service, repository, config)
- Per-application roles, permissions, and user-role assignments via admin API
- Admin endpoints:
POST/GET /admin/rbac/roles,PUT/DELETE /admin/rbac/roles/:id,POST/GET /admin/rbac/permissions,PUT/DELETE /admin/rbac/permissions/:id,POST/GET/DELETE /admin/rbac/user-roles - Default system roles seeded on migration:
adminandmember - JWT tokens now include
rolesclaim (array of role names per application) - New models:
Role,Permission,UserRole(pkg/models/role.go) - New DTOs:
pkg/dto/rbac.go - New domain package:
internal/rbac/(handler, service, repository) - SQL migrations:
20260301_add_rbac.sql,20260301_seed_rbac_defaults.sql,20260302_backfill_member_role.sql
- List and revoke active sessions for authenticated users
- New endpoints:
GET /sessions(list active sessions),DELETE /sessions/:id(revoke one),DELETE /sessions(revoke all) - New DTOs:
pkg/dto/session.go - New domain package:
internal/session/(handler, service)
- Passwordless login via email magic link for both users and admin accounts
- New endpoints:
POST /magic-link/request,POST /magic-link/verify - Per-application setting:
magic_link_enabled(opt-in via Admin API) - Admin GUI magic link login support
- New environment variable:
ADMIN_URL(base URL for magic link emails) - SQL migrations:
20260303_add_admin_magic_link.sql,20260303_add_magic_link_settings.sql,20260303_seed_magic_link_email_type.sql
- Link and unlink social accounts to/from existing authenticated users
- New endpoints:
GET /profile/social-accounts,DELETE /profile/social-accounts,/auth/{provider}/link,/auth/{provider}/link/callback(for Google, Facebook, GitHub) - Admin GUI social account unlink support on My Account page
- New endpoint:
POST /resend-verification— Resend the email verification link for unverified users
- New admin pages: Roles, Permissions, User Roles, Sessions, My Account
- My Account page: Passkey management (register/rename/delete), magic link toggle, 2FA settings, social account unlinking
- Login page enhancements: Passkey login option, magic link login option (in addition to password)
- HTMX sidebar navigation: Improved admin GUI layout with HTMX-powered sidebar and page containers
- Admin accounts now support an
emailfield and login by email (in addition to username)
- Auth middleware now validates session existence in Redis on every authenticated request
- Revoked or expired sessions are immediately rejected even if the JWT token is still valid
- Improves security by ensuring session revocation takes effect instantly
- New feature flag fields on
applicationstable:passkey_2fa_enabled,passkey_login_enabled,magic_link_enabled,email_2fa_enabled,two_fa_methods - All fields managed by GORM AutoMigrate (no manual SQL migration needed)
- New fields:
email,two_fa_enabled,two_fa_method,two_fa_secret,two_fa_recovery_codes,magic_link_enabled
- JWT access tokens now include a
rolesarray claim containing the user's role names for the current application
- Passkey events:
PASSKEY_REGISTER,PASSKEY_DELETE,PASSKEY_LOGIN - Magic link events:
MAGIC_LINK_REQUESTED,MAGIC_LINK_LOGIN,MAGIC_LINK_FAILED - Email events:
EMAIL_VERIFY_RESEND - Social linking events:
SOCIAL_ACCOUNT_LINKED,SOCIAL_ACCOUNT_UNLINKED - All new events default to Informational severity with standard retention policies
- Admin GUI Dashboard — Full-featured web-based admin panel served at
/gui/*from the same binary - CLI Admin Setup —
cmd/setup/main.gointeractive wizard for creating the initial admin account with bcrypt-hashed password - Session-Based Authentication — Redis-backed sessions with secure cookies (SameSite=Strict, HttpOnly, Secure)
- CSRF Protection — Token-based CSRF middleware for all GUI mutation endpoints
- Dashboard Page — Overview with tenant/app/user/log counts and recent activity
- Tenant Management — Full CRUD with HTMX single-page interactions, paginated list
- Application Management — Full CRUD with tenant filter dropdown, paginated flat list
- OAuth Config Management — Full CRUD with provider dropdown, inline enable/disable toggle, secret masking
- User Management — Read-only user list with search, inline detail panel, active/inactive toggle with token revocation
- Activity Log Viewer — Read-only with multiple filters (event type, severity, app, date range, email search), inline detail panel
- API Key Management — Admin-level and per-application API keys with SHA-256 hashed storage, key shown once at creation, revoke/delete support
- Settings Management — Accordion-based settings page with lazy-loaded sections, per-setting inline save/reset, registry-based architecture
- Embedded Static Assets — Bootstrap 5 CSS/JS, HTMX, Bootstrap Icons all embedded via
go:embed - Go Template Engine — Custom
gin.HTMLRenderimplementation with layout/partial composition
- GUI Auth Middleware (
internal/middleware/gui_auth.go) — Session-based authentication for GUI routes - CSRF Middleware (
internal/middleware/csrf.go) — CSRF token validation for GUI mutations - Admin Auth Middleware (
internal/middleware/admin_auth.go) — Database-backed API key authentication for/admin/*routes - App API Key Middleware (
internal/middleware/app_api_key.go) — Per-application API key validation (available for future use) - Security Headers Middleware (
internal/middleware/security_headers.go) — X-Frame-Options, CSP, HSTS, Permissions-Policy, and more - Generic Rate Limit Middleware (
internal/middleware/rate_limit.go) — Configurable per-route rate limiting with Redis + in-memory fallback
- AdminAccount Model (
pkg/models/admin_account.go) — Admin user accounts with bcrypt password hash - SystemSetting Model (
pkg/models/system_setting.go) — Key-value settings with DB override support - ApiKey Model (
pkg/models/api_key.go) — Admin and per-app API keys with SHA-256 hash, prefix/suffix display - Database Migrations for admin accounts, system settings, and API keys tables
GET /gui/login— Login pagePOST /gui/login— Authenticate adminPOST /gui/logout— End admin sessionGET /gui/dashboard— Dashboard with statsGET/POST/PUT/DELETE /gui/tenants/*— Tenant CRUDGET/POST/PUT/DELETE /gui/apps/*— Application CRUDGET/POST/PUT/DELETE /gui/oauth/*— OAuth config CRUD with toggleGET /gui/users/*— User list, detail, search, toggle activeGET /gui/logs/*— Activity log viewer with filtersGET/POST/PUT/DELETE /gui/api-keys/*— API key managementGET/PUT/DELETE /gui/settings/*— Settings management
- JWT Secret Validation —
log.FatalfifJWT_SECRETis empty or less than 32 bytes; lazy initialization viasync.Once - JWT Token Type Claim — Added
typefield to JWT claims ("access"or"refresh"); auth middleware rejects refresh tokens used as access tokens; backward compatible with legacy tokens - Password Hashing — bcrypt cost increased from default (10) to 12 for all password operations
- CSRF Comparison — Changed from
==tocrypto/subtle.ConstantTimeCompareto prevent timing attacks - Cookie Security — Admin session cookies now use
SameSite=Strictviahttp.SetCookie - CORS Production Safety — Localhost origins removed from CORS allowlist in release mode; warning logged if
FRONTEND_URLis empty - Password Max Length — Added
max=128validation to all 7 password fields across 6 DTOs to prevent bcrypt DoS - Error Message Sanitization — Replaced 6 instances of
err.Error()leaking internal details in social handler with generic messages - Debug Print Removal — Removed 9
fmt.Print/fmt.Printlndebug statements from user, email, and 2FA services - SQL Injection Fix — Fixed
INTERVAL '? days'(non-parameterized) toINTERVAL '1 day' * ?in log cleanup - Rate Limiting — Applied rate limits to 6 public endpoints:
/register(3/min),/login(5/min + lockout),/refresh-token(10/min),/forgot-password(3/min),/reset-password(5/min),/2fa/login-verify(5/min + lockout) - Security Headers — Added global middleware: X-Frame-Options (DENY), X-Content-Type-Options (nosniff), Referrer-Policy, CSP (route-aware: strict for API, relaxed for GUI), HSTS (conditional on TLS)
- Shared Constants — Moved session/context keys, cookie helpers, and interfaces to
web/context_keys.goto resolve import cycles - SessionValidator Interface —
web.SessionValidatorimplemented byAccountServicefor middleware decoupling - ApiKeyValidator Interface —
web.ApiKeyValidatorimplemented by adminRepositoryfor middleware decoupling
- JWT Test Fix — Replaced
init()withsync.Oncelazy initialization to fix test ordering issues - New Test Suites — Rate limiter (17 tests), security headers (6 tests), DTO validation (17 tests), CSRF comparison (10 tests), error types (5 tests), API key utilities (6 tests)
- Swagger Updates — Added
@Failure 429annotations to all 6 rate-limited endpoints; regenerated swagger docs
- JWT init() ordering —
init()inpkg/jwt/jwt.gocalledlog.FatalfbeforeTestMaincould configure the secret, killing all test suites; replaced with lazysync.Onceinitialization
- 14 security findings addressed across all severity levels (Critical, High, Medium, Low)
- Generic rate limiting with in-memory fallback protects against brute-force even when Redis is unavailable
- Security headers protect against clickjacking, MIME sniffing, and XSS
This milestone introduced multi-tenancy architecture, enabling the API to serve multiple tenants and applications. These changes affect API clients upgrading from earlier pre-release builds.
All API requests now require the X-App-ID header:
# Before (alpha.1 / alpha.2)
curl -X POST /auth/register \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"secret"}'
# After (alpha.3+)
curl -X POST /auth/register \
-H "Content-Type: application/json" \
-H "X-App-ID: 00000000-0000-0000-0000-000000000001" \
-d '{"email":"user@example.com","password":"secret"}'Exceptions (no X-App-ID required):
- Swagger documentation endpoints (
/swagger/*) - Admin endpoints (
/admin/*) - OAuth callback endpoints (app_id in state parameter)
Database Schema:
- New tables:
tenants,applications,oauth_provider_configs - Modified tables:
users,social_accounts,activity_logsnow includeapp_idforeign key - Existing data automatically migrated to default tenant/application (
00000000-0000-0000-0000-000000000001) - Email uniqueness now scoped per application (was globally unique)
JWT Tokens:
- JWT tokens now include
app_idclaim - Existing tokens issued before upgrade will be invalid
- Users must re-authenticate after migration
OAuth Configuration:
- OAuth provider credentials (Google, Facebook, GitHub) now configured per-application
- Use migration tool
cmd/migrate_oauth/main.goto migrate existing credentials from environment variables to database - Legacy environment-based OAuth config still supported for default app
- Tenant Management: Create and manage multiple tenant organizations via admin API
- Application Management: Each tenant can have multiple applications with isolated user bases
- Per-Application OAuth: OAuth providers (Google, Facebook, GitHub) configured per-application in database
- Admin API Endpoints:
POST /admin/tenants- Create tenantGET /admin/tenants- List tenants (paginated)POST /admin/apps- Create applicationGET /admin/apps- List applications (paginated)POST /admin/oauth-providers- Configure OAuth provider for appGET /admin/oauth-providers/:app_id- List OAuth providers for appPUT /admin/oauth-providers/:id- Update OAuth provider configDELETE /admin/oauth-providers/:id- Delete OAuth provider config
- AppID Middleware: Validates and injects
X-App-IDheader into request context - Query parameter fallback:
?app_id=<uuid>supported when header not available
- Tenant Model (
pkg/models/tenant.go): Organization-level entity - Application Model (
pkg/models/application.go): Tenant's application entity - OAuthProviderConfig Model (
pkg/models/oauth_provider_config.go): Per-app OAuth credentials
- Migration Script:
migrations/20260105_add_multi_tenancy.sqlwith automatic data migration - Rollback Script:
migrations/20260105_add_multi_tenancy_rollback.sqlfor safe rollback - OAuth Migration Tool:
cmd/migrate_oauth/main.goto migrate OAuth credentials from env to database - Enhanced Backup Scripts:
scripts/backup_db.sh(Unix/Mac)scripts/backup_db.bat(Windows)
- Migration Helper Scripts:
scripts/apply_pending_migrations.sh- Apply pending migrationsscripts/rollback_last_migration.sh- Rollback last migration
- Copilot Instructions:
.github/copilot-instructions.mdfor AI-assisted development - Migration Documentation:
migrations/20260105_add_multi_tenancy.md - Admin API DTOs:
pkg/dto/admin.gowith request/response structures - Updated Swagger: Complete API documentation with new admin endpoints
- JWT generation now includes
app_idclaim (seepkg/jwt/jwt.go) - Auth middleware validates
app_idfrom token against request header - User lookup queries now scoped by
app_id
- OAuth state now includes
app_idfor callback routing - Social account linkage scoped per application
- OAuth credentials loaded from database per-application (fallback to env vars for default app)
- TOTP secrets and recovery codes now scoped per application
- 2FA state isolated between applications
- User registration scoped by
app_id - Email uniqueness constraint now
(email, app_id)instead of global - Profile endpoints return data scoped to request's
app_id
- All activity logs include
app_idfor audit trail segmentation - Log queries filtered by application context
- All API tests updated to include
X-App-IDheader - Test scripts (
test_api.sh,test_logout.sh) updated with multi-tenancy support
- Redis key prefixes now include
app_idfor session isolation - CORS middleware allows
X-App-IDheader in requests
- Docker network creation documentation (
README.md) - Added step to create shared network before starting containers
- Data Isolation: Complete tenant/application data isolation at database level
- OAuth Security: OAuth credentials stored per-application with encrypted secrets
- JWT Claims: App ID validation prevents cross-application token reuse
- Index Updates: Optimized indexes for multi-tenant queries
-
Backup Database:
make migrate-backup # or manually: pg_dump -U postgres -d auth_db > backup_$(date +%Y%m%d_%H%M%S).sql
-
Apply Migration:
make migrate-up # This automatically: # - Creates tenants, applications, oauth_provider_configs tables # - Adds app_id columns to users, social_accounts, activity_logs # - Migrates all existing data to default tenant/app # - Updates indexes (email uniqueness now per-app) # - Records migration in schema_migrations table
-
Migrate OAuth Credentials (if using social login):
go run cmd/migrate_oauth/main.go # Reads from .env and creates oauth_provider_configs entries # For providers: Google, Facebook, GitHub
-
Update API Clients:
- Add
X-App-ID: 00000000-0000-0000-0000-000000000001header to all requests - Default app ID is created automatically during migration
- Update documentation/SDKs with new header requirement
- Notify users to re-authenticate (existing JWTs are invalid)
- Add
-
Test Endpoints:
# Test registration curl -X POST http://localhost:8080/auth/register \ -H "X-App-ID: 00000000-0000-0000-0000-000000000001" \ -H "Content-Type: application/json" \ -d '{"email":"test@example.com","password":"Test123!@#"}' # Test login curl -X POST http://localhost:8080/auth/login \ -H "X-App-ID: 00000000-0000-0000-0000-000000000001" \ -H "Content-Type: application/json" \ -d '{"email":"test@example.com","password":"Test123!@#"}'
-
Rollback (if needed):
# If migration fails or issues arise: make migrate-down # or manually: psql -U postgres -d auth_db -f migrations/20260105_add_multi_tenancy_rollback.sql # Restore from backup if necessary: psql -U postgres -d auth_db < backup_20260119_143022.sql
Estimated Migration Time:
- Small databases (<10k users): 1-2 minutes
- Medium databases (10k-100k users): 3-5 minutes
- Large databases (>100k users): 5-15 minutes
Expected Downtime: 5-15 minutes (application must be stopped during migration)
- Multi-tenancy enabled by default
- Default tenant and application created automatically
- Use admin API to create additional tenants/applications:
# Create tenant POST /admin/tenants {"name": "Acme Corp"} # Create application POST /admin/apps {"tenant_id": "<tenant_id>", "name": "Mobile App", "description": "iOS/Android app"} # Configure OAuth for app POST /admin/oauth-providers { "app_id": "<app_id>", "provider": "google", "client_id": "xxx", "client_secret": "yyy", "redirect_url": "https://app.example.com/auth/google/callback", "is_enabled": true }
Issue: "X-App-ID header is required" error
- Solution: Add header to all API requests (except /swagger/* and /admin/*)
- Default app ID:
00000000-0000-0000-0000-000000000001
Issue: JWT tokens not working after migration
- Solution: This is expected. Users must re-login to get new JWTs with
app_idclaim
Issue: Social login not working
- Solution: Run OAuth migration tool:
go run cmd/migrate_oauth/main.go - Or configure via Admin API:
POST /admin/oauth-providers
Issue: Email already exists error for different apps
- Solution: This is correct behavior - email uniqueness is now per-app, not global
Issue: Migration fails with foreign key error
- Solution: Check database constraints. Rollback and restore from backup.
Need Help?
- See: Pre-Release Migration Reference for detailed migration guide
- See:
migrations/20260105_add_multi_tenancy.mdfor technical details - Open GitHub issue with "migration-help" label
- GitHub Actions Workflow: Complete CI/CD pipeline with test, build, and security-scan jobs
- Local Testing Support: Full compatibility with
actfor running GitHub Actions locally - Improved Port Configuration: Services use non-conflicting ports (PostgreSQL: 5435, Redis: 6381) for CI
- Smart Artifact Handling: Conditional artifact upload/download based on environment (skips for local act runs)
- Gosec Security Scanner: Automated security scanning integrated into CI/CD
- Nancy Vulnerability Scanner: Optional dependency vulnerability scanning (requires authentication)
- Security Exception Documentation: Proper
#noseccomments with justification for legitimate cases
- Environment Variable Support: Tests now properly read from CI environment variables via
viper.AutomaticEnv() - Redis Connection Handling: Improved test reliability with proper Redis configuration
- Test Coverage: Maintained high test coverage across all components
- CI/CD Commands Section: Added commands for running CI locally with act
- Updated README: Added CI/CD features to Developer Experience section
- Installation Guide: Added note about installing act for local CI testing
- Port conflicts in CI/CD workflow when running with
act(changed from default 5432/6379 to 5435/6381) - Test configuration to respect environment variables via
viper.AutomaticEnv()andviper.SetDefault() - Artifact operations now skip when running locally with
actusingif: ${{ !env.ACT }}condition - Security scanner false positive for non-cryptographic random number usage in log sampling
- Nancy vulnerability scanner now uses
continue-on-errorto prevent CI failures when OSS Index authentication is not configured
- CI workflow now uses different ports to avoid conflicts with local development environments
- Test setup uses
SetDefaultinstead ofSetto allow environment variable override
- Event Severity Classification: Events categorized as Critical, Important, or Informational
- Intelligent Logging: High-frequency events (TOKEN_REFRESH, PROFILE_ACCESS) disabled by default
- Anomaly Detection: Automatically logs unusual patterns (new IP, new device) even for disabled events
- Automatic Cleanup: Background service removes expired logs based on retention policies
- Configurable Retention:
- Critical events: 365 days (LOGIN, PASSWORD_CHANGE, 2FA changes)
- Important events: 180 days (EMAIL_VERIFY, SOCIAL_LOGIN, PROFILE_UPDATE)
- Informational events: 90 days (TOKEN_REFRESH, PROFILE_ACCESS when enabled)
- Created
internal/config/logging.gofor centralized logging configuration - All settings configurable via environment variables
- Event enable/disable controls per event type
- Sampling rates for high-frequency events
- Anomaly detection configuration
- Retention policies per severity level
- Created
internal/log/anomaly.gofor behavioral analysis - Detects new IP addresses from user's historical patterns
- Detects new devices/browsers (user agent changes)
- Configurable pattern analysis window (default: 30 days)
- Optional unusual time access detection
- Privacy-preserving pattern storage (hashed IPs/user agents)
- Added
severityfield to activity_logs (CRITICAL, IMPORTANT, INFORMATIONAL) - Added
expires_atfield for automatic expiration timestamps - Added
is_anomalyflag to identify anomaly-triggered logs - Created composite indexes for efficient queries and cleanup
- Migration script with rollback capability
- MIGRATIONS.md: User-friendly migration guide with step-by-step instructions
- BREAKING_CHANGES.md: Breaking changes tracker with version history
- UPGRADE_GUIDE.md: Detailed version upgrade instructions with rollback procedures
- migrations/README.md: Developer-focused migration guide with best practices
- migrations/TEMPLATE.md: Standardized template for creating new migrations
- migrations/MIGRATIONS_LOG.md: Historical log of all applied migrations
- scripts/migrate.sh: Interactive Unix/Mac migration tool with:
- Migration status checking
- Automatic backups before migrations
- Apply/rollback functionality
- Database connection testing
- scripts/migrate.bat: Windows-compatible migration tool
- Makefile targets:
make migrate- Interactive migration toolmake migrate-up- Apply migrationsmake migrate-down- Rollback migrationsmake migrate-status- Check migration statusmake migrate-backup- Create database backup
- Updated CONTRIBUTING.md with detailed migration guidelines
- Clear process for creating migrations with checklists
- Breaking change documentation requirements
- Testing and verification procedures
- Semver guidelines for version bumping
- Created
internal/log/cleanup.gofor background log deletion - Runs on configurable schedule (default: daily)
- Batch processing to avoid database locks (default: 1000 per batch)
- Graceful shutdown handling
- Statistics tracking and manual trigger capability
- GDPR compliance support (delete user logs on request)
- Created
docs/ACTIVITY_LOGGING_GUIDE.md- Complete configuration guide - Created
docs/ENV_VARIABLES.md- All environment variables reference - Created
docs/SMART_LOGGING_IMPLEMENTATION.md- Implementation summary - Created
migrations/README_SMART_LOGGING.md- Migration instructions - Updated
docs/API.mdwith new event categorization - Updated
README.mdwith smart logging features
# Default behavior (zero configuration needed)
# - Critical/Important events: Always logged
# - TOKEN_REFRESH/PROFILE_ACCESS: Disabled (logged only on anomaly)
# - Automatic cleanup: Enabled (runs daily)
# - Anomaly detection: Enabled
# Optional customization via environment variables:
LOG_DISABLED_EVENTS=TOKEN_REFRESH,PROFILE_ACCESS
LOG_ANOMALY_DETECTION_ENABLED=true
LOG_RETENTION_CRITICAL=365
LOG_RETENTION_IMPORTANT=180
LOG_RETENTION_INFORMATIONAL=90
LOG_CLEANUP_ENABLED=true
LOG_CLEANUP_INTERVAL=24h- Database Size: 80-95% reduction in log volume
- Performance: Maintained (async logging) with enhanced indexes
- Security: Improved focus on actionable events with anomaly detection
- Compliance: Maintained for all critical audit requirements
- Flexibility: Fully configurable per deployment environment
- None - backward compatible with existing activity logs
- Existing logs automatically assigned severity and expiration on migration
- All existing API endpoints continue to work unchanged
- Run migration:
migrations/20240103_add_activity_log_smart_fields.sql - Rollback available:
migrations/20240103_add_activity_log_smart_fields_rollback.sql - See
migrations/README_SMART_LOGGING.mdfor detailed instructions
- Profile data now automatically syncs from social providers on every login
- System updates both
social_accountsanduserstables with latest provider data - Smart update strategy: Only updates fields that have changed
- Non-blocking: Authentication succeeds even if profile update fails
- Supports all providers: Google, Facebook, GitHub
- Profile picture (avatar/photo URL)
- Full name, first name, last name
- Email from provider
- Locale/language preference
- Username (GitHub login, etc.)
- Complete raw provider response (JSONB)
- OAuth access token
- Users see updated profile pictures immediately after changing them on social platforms
- Name changes on social accounts automatically reflected in app
- No manual sync or refresh needed
- Data stays current with social provider
- Added
UpdateSocialAccount()method to social repository - Enables full social account record updates via GORM
- Updated
UserResponseDTO to include all new profile fields - Added
SocialAccountResponseDTO for social account data - Modified
GetUserByIDrepository to preload social accounts - Enhanced
GetProfilehandler to return complete user profile with social accounts - Profile endpoint now returns: name, first_name, last_name, profile_picture, locale, social_accounts
- Regenerated Swagger documentation to reflect new profile structure
- Added
Namefield to store full name from social login or user input - Added
FirstNamefield for first name from social login - Added
LastNamefield for last name from social login - Added
ProfilePicturefield to store profile picture URL from social providers - Added
Localefield for user's language/locale preference
- Added
Emailfield to store email from social provider - Added
Namefield to store name from social provider - Added
FirstNamefield for first name from social provider - Added
LastNamefield for last name from social provider - Added
ProfilePicturefield for profile picture URL from social provider - Added
Usernamefield for username/login from providers (e.g., GitHub login) - Added
Localefield for locale from social provider - Added
RawDataJSONB field to store complete raw JSON response from provider
- Added
UpdateUser()method to user repository for updating user profile data - Enhanced Google login handler to capture: email, verified_email, name, given_name, family_name, picture, locale
- Enhanced Facebook login handler to capture: email, name, first_name, last_name, picture (large), locale
- Enhanced GitHub login handler to capture: email, name, login, avatar_url, bio, location, company
- Implemented smart profile update logic: only update user fields if currently empty when linking social accounts
- Store complete provider response in
RawDatafield for all providers
- Profile endpoint (
GET /profile) now returns additional fields: name, first_name, last_name, profile_picture, locale - Social account objects now include all new fields in responses
- No breaking changes - all new fields are optional and nullable
- Modified social login data extraction to request extended fields from providers
- Updated Facebook Graph API call to request:
id,name,email,first_name,last_name,picture.type(large),locale - Enhanced social account linking to preserve and enrich existing user profile data
- Migration Method: GORM AutoMigrate (automatic on application startup)
- Database Impact: Adds 5 columns to
userstable, 8 columns tosocial_accountstable - Backward Compatibility: Fully backward compatible - all new fields are nullable
- Files Modified:
pkg/models/user.go- User model with new profile fieldspkg/models/social_account.go- Social account model with extended data fieldsinternal/social/service.go- Enhanced provider handlers for Google, Facebook, GitHubinternal/user/repository.go- Added UpdateUser methoddocs/migrations/MIGRATION_SOCIAL_LOGIN_DATA.md- Migration documentation
- Added comprehensive migration documentation in
docs/migrations/MIGRATION_SOCIAL_LOGIN_DATA.md - Documents data flow changes, database schema updates, and testing recommendations
- Includes security considerations and rollback plan
- User registration and authentication
- Email verification
- Password reset functionality
- Two-factor authentication (TOTP)
- Social login integration (Google, Facebook, GitHub)
- JWT-based authentication (access & refresh tokens)
- Activity logging
- Redis-based session management
- Comprehensive API documentation with Swagger