Skip to content

Commit a6f8a9d

Browse files
authored
Merge pull request #3185 from coolwednesday/fix/oauth-health-check-3179-clean
2 parents 01a7e62 + 724bc99 commit a6f8a9d

File tree

2 files changed

+106
-1
lines changed

2 files changed

+106
-1
lines changed

pkg/gofr/auth.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package gofr
22

33
import (
44
"net/http"
5+
"net/url"
6+
"strings"
57
"time"
68

79
"github.com/golang-jwt/jwt/v5"
@@ -112,11 +114,35 @@ func (a *App) EnableOAuth(jwksEndpoint string,
112114
refreshInterval int,
113115
options ...jwt.ParserOption,
114116
) {
115-
a.AddHTTPService("gofr_oauth", jwksEndpoint)
117+
parsedURL, err := url.Parse(jwksEndpoint)
118+
if err != nil {
119+
a.container.Errorf("invalid JWKS endpoint URL: %v", err)
120+
return
121+
}
122+
123+
if parsedURL.Scheme == "" || parsedURL.Host == "" {
124+
a.container.Errorf("invalid JWKS endpoint URL: missing scheme or host in %q", jwksEndpoint)
125+
return
126+
}
127+
128+
if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" {
129+
a.container.Errorf("invalid JWKS endpoint URL: unsupported scheme %q", parsedURL.Scheme)
130+
return
131+
}
132+
133+
baseURL := parsedURL.Scheme + "://" + parsedURL.Host
134+
jwksPath := strings.TrimPrefix(parsedURL.Path, "/")
135+
136+
if parsedURL.RawQuery != "" {
137+
jwksPath += "?" + parsedURL.RawQuery
138+
}
139+
140+
a.AddHTTPService("gofr_oauth", baseURL)
116141

117142
oauthOption := middleware.OauthConfigs{
118143
Provider: a.container.GetHTTPService("gofr_oauth"),
119144
RefreshInterval: time.Second * time.Duration(refreshInterval),
145+
Path: jwksPath,
120146
}
121147

122148
publicKeyProvider := middleware.NewOAuth(oauthOption)

pkg/gofr/gofr_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,85 @@ func TestEnableBasicAuthWithFunc(t *testing.T) {
421421
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode, "TestEnableBasicAuthWithFunc Failed!")
422422
}
423423

424+
func TestEnableOAuth_HealthCheckEndpoint(t *testing.T) {
425+
port := testutil.GetFreePort(t)
426+
427+
// Mock server that serves both /.well-known/alive and /.well-known/jwks.json
428+
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
429+
switch r.URL.Path {
430+
case "/.well-known/alive":
431+
w.WriteHeader(http.StatusOK)
432+
case "/.well-known/jwks.json":
433+
w.Header().Set("Content-Type", "application/json")
434+
w.WriteHeader(http.StatusOK)
435+
_, _ = w.Write([]byte(`{"keys":[]}`))
436+
default:
437+
w.WriteHeader(http.StatusNotFound)
438+
}
439+
}))
440+
defer mockServer.Close()
441+
442+
c := container.NewContainer(config.NewMockConfig(nil))
443+
444+
a := &App{
445+
httpServer: &httpServer{
446+
router: gofrHTTP.NewRouter(),
447+
port: port,
448+
},
449+
container: c,
450+
}
451+
452+
// Pass full JWKS URL with path — the fix should extract the base URL
453+
a.EnableOAuth(mockServer.URL+"/.well-known/jwks.json", 600)
454+
455+
// Verify the service is registered
456+
oauthService := a.container.GetHTTPService("gofr_oauth")
457+
require.NotNil(t, oauthService, "gofr_oauth service should be registered")
458+
459+
// Health check should hit mockServer/.well-known/alive (not mockServer/.well-known/jwks.json/.well-known/alive)
460+
health := oauthService.HealthCheck(t.Context())
461+
assert.Equal(t, "UP", health.Status, "Health check should hit the host root, not the JWKS path")
462+
463+
// JWKS fetch should hit mockServer/.well-known/jwks.json (not mockServer//.well-known/jwks.json)
464+
resp, err := oauthService.GetWithHeaders(t.Context(), ".well-known/jwks.json", nil, nil)
465+
require.NoError(t, err)
466+
467+
defer resp.Body.Close()
468+
469+
assert.Equal(t, http.StatusOK, resp.StatusCode, "JWKS fetch should hit the correct path without double slash")
470+
}
471+
472+
func TestEnableOAuth_InvalidEndpoints(t *testing.T) {
473+
invalidEndpoints := []string{
474+
"",
475+
"not-a-url",
476+
"/.well-known/jwks.json",
477+
"http://",
478+
"ftp://host/.well-known/jwks.json",
479+
}
480+
481+
for _, endpoint := range invalidEndpoints {
482+
t.Run(endpoint, func(t *testing.T) {
483+
port := testutil.GetFreePort(t)
484+
c := container.NewContainer(config.NewMockConfig(nil))
485+
486+
a := &App{
487+
httpServer: &httpServer{
488+
router: gofrHTTP.NewRouter(),
489+
port: port,
490+
},
491+
container: c,
492+
}
493+
494+
a.EnableOAuth(endpoint, 600)
495+
496+
// Service should NOT be registered for invalid endpoints
497+
assert.Nil(t, a.container.GetHTTPService("gofr_oauth"),
498+
"gofr_oauth service should not be registered for invalid endpoint: %q", endpoint)
499+
})
500+
}
501+
}
502+
424503
func encodeBasicAuthorization(t *testing.T, arg string) string {
425504
t.Helper()
426505

0 commit comments

Comments
 (0)