fix: prevent nil-panic on server and improve OAuth callback handling

Load config eagerly during server startup so sortByUrgency never
hits a nil config. Add nil-guard in BuildUrgencyCoefficients as
belt-and-suspenders defense. Fix OAuth callback to support both
GET and POST, and resolve issuer URLs properly with path.Dir.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-17 16:40:53 +01:00
parent c5a963bfd9
commit 80ea17227d
7 changed files with 526 additions and 59 deletions
+16 -1
View File
@@ -39,7 +39,22 @@ func GetLoginURL(w http.ResponseWriter, r *http.Request) {
// OAuthCallback handles the OAuth callback
func OAuthCallback(w http.ResponseWriter, r *http.Request) {
code := r.URL.Query().Get("code")
var code string
// Support both GET (direct OAuth redirect) and POST (frontend exchange)
if r.Method == http.MethodPost {
var req struct {
Code string `json:"code"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
errorResponse(w, http.StatusBadRequest, "invalid request body")
return
}
code = req.Code
} else {
code = r.URL.Query().Get("code")
}
if code == "" {
errorResponse(w, http.StatusBadRequest, "missing code parameter")
return
+1
View File
@@ -41,6 +41,7 @@ func (s *Server) setupRoutes() {
// OAuth endpoints (no auth required)
r.Get("/auth/login", handlers.GetLoginURL)
r.Get("/auth/callback", handlers.OAuthCallback)
r.Post("/auth/callback", handlers.OAuthCallback)
r.Post("/auth/refresh", handlers.RefreshToken)
r.Post("/auth/logout", handlers.Logout)
+16 -3
View File
@@ -6,10 +6,23 @@ import (
"fmt"
"io"
"net/http"
"net/url"
"path"
"golang.org/x/oauth2"
)
// issuerBase resolves ".." to get the base OAuth path from the issuer URL.
// e.g. "https://auth.example.com/application/o/app/" -> "https://auth.example.com/application/o/"
func issuerBase(issuer string) string {
u, err := url.Parse(issuer)
if err != nil {
return issuer
}
u.Path = path.Dir(path.Clean(u.Path)) + "/"
return u.String()
}
type OAuthClient struct {
config *oauth2.Config
cfg *Config
@@ -22,8 +35,8 @@ func NewOAuthClient(cfg *Config) *OAuthClient {
ClientSecret: cfg.OAuthClientSecret,
RedirectURL: cfg.OAuthRedirectURI,
Endpoint: oauth2.Endpoint{
AuthURL: cfg.OAuthIssuer + "../authorize/",
TokenURL: cfg.OAuthIssuer + "../token/",
AuthURL: issuerBase(cfg.OAuthIssuer) + "authorize/",
TokenURL: issuerBase(cfg.OAuthIssuer) + "token/",
},
Scopes: []string{"openid", "profile", "email"},
},
@@ -47,7 +60,7 @@ type UserInfo struct {
}
func (c *OAuthClient) GetUserInfo(ctx context.Context, accessToken string) (*UserInfo, error) {
req, err := http.NewRequestWithContext(ctx, "GET", c.cfg.OAuthIssuer+"../userinfo/", nil)
req, err := http.NewRequestWithContext(ctx, "GET", issuerBase(c.cfg.OAuthIssuer)+"userinfo/", nil)
if err != nil {
return nil, err
}
+5 -1
View File
@@ -170,8 +170,12 @@ func (t *Task) urgencyProject(coeff float64) float64 {
return 0.0
}
// BuildUrgencyCoefficients creates UrgencyCoefficients from config
// BuildUrgencyCoefficients creates UrgencyCoefficients from config.
// If cfg is nil, uses DefaultConfig() to prevent nil-pointer panics.
func BuildUrgencyCoefficients(cfg *Config) *UrgencyCoefficients {
if cfg == nil {
cfg = DefaultConfig()
}
return &UrgencyCoefficients{
Due: cfg.UrgencyDue,
PriorityH: cfg.UrgencyPriorityH,