add OAuth authentication with JWT tokens and API key support

This commit is contained in:
2026-01-18 21:00:03 +01:00
parent 2eb6292dc2
commit 8601bb5ba3
8 changed files with 1004 additions and 44 deletions

View File

@@ -2,24 +2,36 @@
package api
import (
"database/sql"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
chimw "github.com/go-chi/chi/v5/middleware"
"github.com/omixlab/mosis-portal/internal/api/handlers"
"github.com/omixlab/mosis-portal/internal/api/middleware"
"github.com/omixlab/mosis-portal/internal/auth"
"github.com/omixlab/mosis-portal/internal/config"
"github.com/omixlab/mosis-portal/internal/database"
)
// NewRouter creates and configures the HTTP router
func NewRouter(cfg *config.Config, db *sql.DB) http.Handler {
func NewRouter(cfg *config.Config, db *database.DB) http.Handler {
r := chi.NewRouter()
// Middleware
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.RealIP)
r.Use(middleware.RequestID)
r.Use(chimw.Logger)
r.Use(chimw.Recoverer)
r.Use(chimw.RealIP)
r.Use(chimw.RequestID)
// Initialize auth components
jwtManager := auth.NewJWTManager(cfg.JWTSecret)
oauthManager := auth.NewOAuthManager(
cfg.BaseURL,
cfg.GitHubClientID, cfg.GitHubClientSecret,
cfg.GoogleClientID, cfg.GoogleClientSecret,
)
authMiddleware := middleware.NewAuthMiddleware(jwtManager, db)
authHandler := handlers.NewAuthHandler(oauthManager, jwtManager, db)
// Health check
r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
@@ -29,47 +41,57 @@ func NewRouter(cfg *config.Config, db *sql.DB) http.Handler {
// API v1
r.Route("/v1", func(r chi.Router) {
// Auth routes
// Auth routes (public)
r.Route("/auth", func(r chi.Router) {
r.Post("/oauth/github", handlers.NotImplemented)
r.Get("/oauth/github/callback", handlers.NotImplemented)
r.Post("/oauth/google", handlers.NotImplemented)
r.Get("/oauth/google/callback", handlers.NotImplemented)
r.Post("/refresh", handlers.NotImplemented)
r.Post("/logout", handlers.NotImplemented)
r.Get("/me", handlers.NotImplemented)
// OAuth - use GET for initiating (redirect based)
r.Get("/oauth/github", authHandler.OAuthStart(auth.ProviderGitHub))
r.Get("/oauth/github/callback", authHandler.OAuthCallback(auth.ProviderGitHub))
r.Get("/oauth/google", authHandler.OAuthStart(auth.ProviderGoogle))
r.Get("/oauth/google/callback", authHandler.OAuthCallback(auth.ProviderGoogle))
// Token management
r.Post("/refresh", authHandler.Refresh)
r.Post("/logout", authHandler.Logout)
// Current user (requires auth)
r.With(authMiddleware.RequireAuth).Get("/me", authHandler.Me)
})
// Developer apps
r.Route("/apps", func(r chi.Router) {
r.Get("/", handlers.NotImplemented)
r.Post("/", handlers.NotImplemented)
r.Get("/{appID}", handlers.NotImplemented)
r.Patch("/{appID}", handlers.NotImplemented)
r.Delete("/{appID}", handlers.NotImplemented)
// Protected developer routes
r.Group(func(r chi.Router) {
r.Use(authMiddleware.RequireAuth)
// Versions
r.Route("/{appID}/versions", func(r chi.Router) {
// Developer apps
r.Route("/apps", func(r chi.Router) {
r.Get("/", handlers.NotImplemented)
r.Post("/", handlers.NotImplemented)
r.Get("/{versionID}", handlers.NotImplemented)
r.Post("/{versionID}/submit", handlers.NotImplemented)
r.Post("/{versionID}/publish", handlers.NotImplemented)
r.Get("/{appID}", handlers.NotImplemented)
r.Patch("/{appID}", handlers.NotImplemented)
r.Delete("/{appID}", handlers.NotImplemented)
// Versions
r.Route("/{appID}/versions", func(r chi.Router) {
r.Get("/", handlers.NotImplemented)
r.Post("/", handlers.NotImplemented)
r.Get("/{versionID}", handlers.NotImplemented)
r.Post("/{versionID}/submit", handlers.NotImplemented)
r.Post("/{versionID}/publish", handlers.NotImplemented)
})
})
})
// API Keys
r.Route("/api-keys", func(r chi.Router) {
r.Get("/", handlers.NotImplemented)
r.Post("/", handlers.NotImplemented)
r.Delete("/{keyID}", handlers.NotImplemented)
})
// API Keys
r.Route("/api-keys", func(r chi.Router) {
r.Get("/", handlers.NotImplemented)
r.Post("/", handlers.NotImplemented)
r.Delete("/{keyID}", handlers.NotImplemented)
})
// Signing Keys
r.Route("/signing-keys", func(r chi.Router) {
r.Get("/", handlers.NotImplemented)
r.Post("/", handlers.NotImplemented)
r.Delete("/{keyID}", handlers.NotImplemented)
// Signing Keys
r.Route("/signing-keys", func(r chi.Router) {
r.Get("/", handlers.NotImplemented)
r.Post("/", handlers.NotImplemented)
r.Delete("/{keyID}", handlers.NotImplemented)
})
})
// Public store endpoints
@@ -80,15 +102,16 @@ func NewRouter(cfg *config.Config, db *sql.DB) http.Handler {
r.Get("/apps/updates", handlers.NotImplemented)
})
// Telemetry
// Telemetry (API key auth preferred, but can work without for initial setup)
r.Route("/telemetry", func(r chi.Router) {
r.Post("/events", handlers.NotImplemented)
r.Post("/crash", handlers.NotImplemented)
})
})
// Admin routes (htmx UI)
// Admin routes (htmx UI) - requires auth
r.Route("/admin", func(r chi.Router) {
r.Use(authMiddleware.RequireAuth)
r.Get("/", handlers.NotImplemented)
r.Get("/review-queue", handlers.NotImplemented)
r.Get("/review/{versionID}", handlers.NotImplemented)