// Copyright 2026 Arctel.net // SPDX-License-Identifier: Apache-2.0 // Package auth provides the authentication, OAuth, session management, and access token domain plugin for Cordis. package auth import ( "context" "embed" "reflect" "Wavelet/core" "Wavelet/core/contracts" "Wavelet/core/extpoints" ) //go:embed migrations/*/*.sql var authMigrations embed.FS // Option configures the auth plugin. type Option func(*Plugin) // WithAuthService sets a custom AuthService implementation. func WithAuthService(svc contracts.AuthService) Option { return func(p *Plugin) { p.authSvc = svc } } // WithAuthRegistry sets a custom AuthRegistry implementation. func WithAuthRegistry(reg contracts.AuthRegistry) Option { return func(p *Plugin) { p.authRegistry = reg } } // Plugin implements core.Plugin to provide authentication and OAuth domain services. type Plugin struct { authSvc contracts.AuthService authRegistry contracts.AuthRegistry } // New creates a new auth domain plugin. func New(opts ...Option) *Plugin { p := &Plugin{} for _, opt := range opts { if opt != nil { opt(p) } } return p } // Name returns the unique identifier for the auth domain plugin. func (p *Plugin) Name() string { return "auth" } // Inject declares required dependencies for the auth domain plugin. func (p *Plugin) Inject() []reflect.Type { return []reflect.Type{ reflect.TypeFor[contracts.DBService](), reflect.TypeFor[contracts.CacheService](), } } // Manifest returns the plugin metadata. func (p *Plugin) Manifest() core.Manifest { return core.Manifest{ Name: "auth", Version: "1.0.0", Description: "Authentication, OAuth, Session and Passkey domain plugin", Author: "Wavelet Team", } } // Apply registers the auth migrations, services, routes, and settings into the Context. func (p *Plugin) Apply(ctx *core.Context) error { // 0. Bind DBService & CacheService from Context if db, err := core.Inject[contracts.DBService](ctx); err == nil && db != nil { setDBService(db) } else { core.When[contracts.DBService](ctx, func(db contracts.DBService) { setDBService(db) }) } if cache, err := core.Inject[contracts.CacheService](ctx); err == nil && cache != nil { setCacheService(cache) } else { core.When[contracts.CacheService](ctx, func(cache contracts.CacheService) { setCacheService(cache) }) } ctx.OnDispose(func() error { setDBService(nil) setCacheService(nil) return nil }) // 1. Register migrations ctx.Migrations().Register("auth", authMigrations) // 2. Initialize and provide AuthService & AuthRegistry if p.authSvc == nil { p.authSvc = newAuthService() } if p.authRegistry == nil { p.authRegistry = newAuthRegistry() } core.Provide[contracts.AuthService](ctx, p.authSvc) core.Provide[contracts.AuthRegistry](ctx, p.authRegistry) // 3. Register HTTP Routes oauthGroup := ctx.Router().Group("/api/v1/oauth") { oauthGroup.GET("/sources", GetLoginSources) oauthGroup.GET("/login", GetLoginURL) oauthGroup.GET("/:source/authorize", Authorize) oauthGroup.GET("/logout", Logout) oauthGroup.POST("/callback", Callback) oauthGroup.GET("/user-info", LoginRequired(), UserInfo) oauthGroup.GET("/external-accounts", LoginRequired(), ListExternalAccounts) oauthGroup.POST("/external-accounts/:id/delete", LoginRequired(), DeleteExternalAccount) } ctx.Router().GET("/api/v1/user-info", LoginRequired(), UserInfo) // 4. Register Settings Schemas ctx.Settings().Register(extpoints.SettingSchema{ Key: "auth.session_age", Default: 86400 * 7, Description: "Default session lifetime in seconds", Type: "integer", Category: "security", }) ctx.Settings().Register(extpoints.SettingSchema{ Key: "auth.login_rate_limit_max_attempts", Default: 5, Description: "Max login failure attempts before temporary IP lock", Type: "integer", Category: "security", }) // 5. Register Event Listeners for domain events ctx.Events().On(contracts.EventTopicUserStatusChanged, func(c context.Context, e contracts.UserStatusChangedEvent) error { InvalidateCachedUser(c, e.UserID) return nil }) ctx.Events().On(contracts.EventTopicUserDeleted, func(c context.Context, e contracts.UserDeletedEvent) error { InvalidateCachedUser(c, e.TargetUserID) return nil }) return nil }