Warning: Due to various recent migrations, viewing non-HEAD refs may be broken.
/main.go (raw)
package main
import (
"context"
"embed"
"flag"
"log"
"net"
"net/http"
"time"
"github.com/go-chi/chi/v5"
)
var (
//go:embed template
templateFS embed.FS
//go:embed static
staticFS embed.FS
)
func main() {
var configFilename string
flag.StringVar(&configFilename, "config", "/etc/lindenii/vireo/config", "Configuration filename")
flag.Parse()
cfg, err := loadConfig(configFilename)
if err != nil {
log.Fatalf("Failed to load config file: %v", err)
}
listenAddr := cfg.Listen
if cfg.Database == "" {
log.Fatalf("Missing database configuration")
}
db, err := openDB(cfg.Database)
if err != nil {
log.Fatalf("Failed to open DB: %v", err)
}
tplBaseData := &TemplateBaseData{
ServerName: cfg.ServerName,
}
if tplBaseData.ServerName == "" {
tplBaseData.ServerName = "vireo"
}
tpl, err := loadTemplate(templateFS, "template/*.html", tplBaseData)
if err != nil {
log.Fatalf("Failed to load template: %v", err)
}
oidcProvider, err := newOIDCProvider(context.Background(), db)
if err != nil {
log.Fatalf("Failed to initialize OpenID Connect provider: %v", err)
}
mux := chi.NewRouter()
mux.Handle("/static/*", http.FileServer(http.FS(staticFS)))
mux.Get("/", index)
mux.HandleFunc("/login", login)
mux.Post("/logout", logout)
mux.HandleFunc("/client/new", manageClient)
mux.HandleFunc("/client/{id}", manageClient)
mux.Post("/client/{id}/revoke", revokeClient)
mux.HandleFunc("/user/new", manageUser)
mux.HandleFunc("/user/{id}", manageUser)
mux.Get("/.well-known/oauth-authorization-server", getOAuthServerMetadata)
mux.Get("/.well-known/openid-configuration", getOpenIDConfiguration)
mux.Get("/.well-known/jwks.json", getOIDCJWKS)
mux.HandleFunc("/authorize", authorize)
mux.Post("/token", exchangeToken)
mux.Post("/introspect", introspectToken)
mux.Post("/revoke", revokeToken)
mux.HandleFunc("/userinfo", userInfo)
go maintainDBLoop(db)
server := http.Server{
Addr: listenAddr,
Handler: csrfMiddleware(loginTokenMiddleware(mux)),
BaseContext: func(net.Listener) context.Context {
return newBaseContext(db, tpl, oidcProvider)
},
}
log.Printf("OAuth server listening on %v", server.Addr)
if err := server.ListenAndServe(); err != nil {
log.Fatalf("Failed to listen and serve: %v", err)
}
}
func httpError(w http.ResponseWriter, err error) {
log.Print(err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
}
func maintainDBLoop(db *DB) {
ticker := time.NewTicker(15 * time.Minute)
defer ticker.Stop()
for range ticker.C {
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
if err := db.Maintain(ctx); err != nil {
log.Printf("Failed to perform database maintenance: %v", err)
}
cancel()
}
}