Add OIDC login and callback against Authentik
internal/auth discovers the provider at MEALQUEST_OIDC_ISSUER with coreos/go-oidc/v3 and runs a confidential-client authorization code flow with golang.org/x/oauth2 (client_secret_basic). GET /auth/login stores state, nonce and a PKCE S256 verifier in a 10-minute HMAC-signed HttpOnly __Host- cookie and redirects with scopes "openid profile email groups offline_access". An optional "return" path must be same-origin; anything else fails the login with 400. GET /auth/callback clears the single-use cookie, checks state, exchanges the code with the verifier, verifies the ID token (RS256 only, issuer, audience, expiry) and nonce, then hands the claims to a SessionStarter. cmd/server wires a placeholder that refuses every login until the session layer lands. Config gains required MEALQUEST_OIDC_ISSUER, MEALQUEST_OIDC_CLIENT_ID, MEALQUEST_OIDC_CLIENT_SECRET_FILE and MEALQUEST_OIDC_ALLOWED_GROUPS. Pins go-oidc v3.21.0 and oauth2 v0.36.0, the newest releases that support Go 1.25 (oauth2 v0.37.0 requires Go 1.26). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+21
-1
@@ -13,6 +13,7 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/auth"
|
||||
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/config"
|
||||
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/frontend"
|
||||
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/server"
|
||||
@@ -51,7 +52,18 @@ func run(cfg config.Config, logger *slog.Logger) error {
|
||||
}
|
||||
}()
|
||||
|
||||
router, err := server.NewRouter(logger, version, frontend.Assets())
|
||||
authFlow, err := auth.New(ctx, auth.Config{
|
||||
Issuer: cfg.OIDCIssuer.String(),
|
||||
ClientID: cfg.OIDCClientID,
|
||||
ClientSecret: cfg.OIDCClientSecret.Reveal(),
|
||||
PublicURL: cfg.PublicURL,
|
||||
SecretKey: []byte(cfg.SecretKey.Reveal()),
|
||||
}, sessionsPending{}, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
router, err := server.NewRouter(logger, version, authFlow, frontend.Assets())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -103,6 +115,14 @@ func openStore(ctx context.Context, dataDir string) (*store.Store, error) {
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// sessionsPending refuses every login until the session layer (Feature 03
|
||||
// task 2) replaces it.
|
||||
type sessionsPending struct{}
|
||||
|
||||
func (sessionsPending) StartSession(http.ResponseWriter, *http.Request, auth.Claims) error {
|
||||
return errors.New("session layer not implemented")
|
||||
}
|
||||
|
||||
// logListening logs the startup line. Only the public URL's origin is logged
|
||||
// so no credential-bearing URL component can reach the log.
|
||||
func logListening(logger *slog.Logger, cfg config.Config) {
|
||||
|
||||
Reference in New Issue
Block a user