Auth
Authenticators, login state, and permission checks
auth manages multiple authenticators and writes authentication results into request context. It can combine Session, JWT, API Key, and RBAC permission checks.
Install
go get github.com/duxweb/runa/auth
HTTP middleware lives in a subpackage:
go get github.com/duxweb/runa/auth github.com/duxweb/runa/session
Connect to an application
package main
import (
"context"
"github.com/duxweb/runa"
"github.com/duxweb/runa/auth"
authmw "github.com/duxweb/runa/auth/middleware"
"github.com/duxweb/runa/provider"
"github.com/duxweb/runa/route"
"github.com/duxweb/runa/session"
sessionmw "github.com/duxweb/runa/session/middleware"
)
type appModule struct {
provider.ModuleBase
}
func (appModule) Name() string { return "app" }
func (appModule) Register(ctx context.Context, app provider.Context) error {
auths, err := provider.Invoke[*auth.Registry](app)
if err != nil {
return err
}
auths.Auth("web", auth.SessionAuth("web"))
return nil
}
func main() {
app := runa.New()
app.Install(
route.Provider(route.Addr(":8080")),
session.Provider(session.RegisterSession("web")),
auth.Provider(),
)
app.Module(appModule{})
route.Default().Use(sessionmw.Use("web"))
route.Default().Get("/me", func(ctx *route.Context) error {
info, _ := ctx.Locals("runa.auth").(*auth.Info)
return ctx.JSON(info)
}).Use(authmw.Use("web"))
if err := app.Run(context.Background()); err != nil {
panic(err)
}
}
auth.Provider() only registers *auth.Registry. Concrete authenticators are registered by code. Auth middleware executes authentication and puts *auth.Info into route service data.
A common business pattern is to register authenticators and routes in the same Module:
func (UserModule) Register(ctx context.Context, app provider.Context) error {
auths := provider.MustInvoke[*auth.Registry](app)
auths.Auth("web", auth.SessionAuth("web"))
route.Default().Use(sessionmw.Use("web"))
route.Default().Get("/me", meHandler).Use(authmw.Use("web"))
return nil
}
Standalone New usage
registry := auth.New()
registry.Auth("api", auth.APIKeyAuth(func(token string) (runa.Map, bool, error) {
if token == "secret" {
return runa.Map{"id": "1"}, true, nil
}
return nil, false, nil
}, auth.Header("Authorization")))
Config
auth currently has no file config structure. Authenticators usually involve secrets, user queries, and business permissions. It is recommended to read config in a business Provider and register authenticators in code.
[auth]
jwt_secret = "change-me"
secret := []byte(runa.Config("auth").GetString("jwt_secret", "change-me"))
auths.Auth("jwt", auth.JWTAuth(secret, auth.Header("Authorization")))
Authenticators
Built-in authenticators:
auth.SessionAuth("web")
auth.JWTAuth([]byte("secret"), auth.Header("Authorization"), auth.Cookie("token"))
auth.APIKeyAuth(verifyToken, auth.Header("X-API-Key"), auth.Query("api_key"))
auth.AuthMux(auth.SessionAuth("web"), auth.JWTAuth(secret))
Token sources:
auth.Header(name)reads from Header.auth.Cookie(name)reads from Cookie.auth.Query(name)reads from Query.
Permission checks
route.Default().Get("/admin", adminHandler).
Name("admin.dashboard").
Use(authmw.Permission(rbac.Checker(store)))
authmw.Permission(...) reads the route name as the permission ID. It can also be customized through route metadata.
RBAC
RBAC lives in the standalone toolkit github.com/duxweb/runa/rbac:
store := rbac.NewMemoryStore()
store.AssignRole("1", "admin")
store.Grant("admin", "admin.*")
checker := rbac.Checker(store)
route.Default().Use(authmw.Permission(checker))
Common mistakes
Installing auth.Provider without registering authenticators
auth.Provider() creates the registry. You still need auth.Default().Auth(...) or Provider-time registration so routes have an authenticator to use.
Forgetting auth middleware
The auth capability does not protect routes by itself. Mount auth/middleware on the routes or groups that require authentication.
Using SessionAuth without session
auth.SessionAuth("web") expects session middleware to load the web session first.
API quick reference
auth.New()creates a standalone auth registry.auth.Provider()connects to the framework lifecycle.auth.Default()reads*auth.Registryfrom default DI.registry.Auth(name, authenticator)registers an authenticator.authmw.Use(names...)requires authentication.authmw.Optional(names...)makes authentication optional.authmw.Permission(checkers...)runs permission checks.