diff --git a/internal/api/jwks.go b/internal/api/jwks.go index 0ca0bb4b6..15d880da3 100644 --- a/internal/api/jwks.go +++ b/internal/api/jwks.go @@ -66,6 +66,9 @@ type OpenIDConfigurationResponse struct { func (a *API) WellKnownOpenID(w http.ResponseWriter, r *http.Request) error { config := a.config issuer := config.JWT.Issuer + if issuer == "" { + issuer = config.API.ExternalURL + } // Ensure issuer doesn't end with a slash to avoid double slashes in URLs for len(issuer) > 0 && issuer[len(issuer)-1] == '/' { @@ -73,7 +76,7 @@ func (a *API) WellKnownOpenID(w http.ResponseWriter, r *http.Request) error { } response := OpenIDConfigurationResponse{ - Issuer: config.JWT.Issuer, + Issuer: issuer, AuthorizationEndpoint: issuer + "/oauth/authorize", TokenEndpoint: issuer + "/oauth/token", JWKSURL: issuer + "/.well-known/jwks.json", diff --git a/internal/api/jwks_test.go b/internal/api/jwks_test.go index 786d3438f..eb5edde00 100644 --- a/internal/api/jwks_test.go +++ b/internal/api/jwks_test.go @@ -6,6 +6,7 @@ import ( "encoding/json" "net/http" "net/http/httptest" + "strings" "testing" "github.com/lestrrat-go/jwx/v2/jwk" @@ -77,3 +78,47 @@ func TestJwks(t *testing.T) { }) } } + +func TestWellKnownOpenIDIssuerFallbackToExternalURL(t *testing.T) { + mockAPI, _, err := setupAPIForTest() + require.NoError(t, err) + + mockAPI.config.JWT.Issuer = "" + mockAPI.config.API.ExternalURL = "https://auth.example.com" + + req := httptest.NewRequest(http.MethodGet, "/.well-known/openid-configuration", nil) + w := httptest.NewRecorder() + mockAPI.handler.ServeHTTP(w, req) + require.Equal(t, http.StatusOK, w.Code) + + var resp OpenIDConfigurationResponse + require.NoError(t, json.NewDecoder(w.Body).Decode(&resp)) + + require.Equal(t, "https://auth.example.com", resp.Issuer) + require.True(t, strings.HasPrefix(resp.AuthorizationEndpoint, "https://auth.example.com/"), "authorization_endpoint should be absolute, got %q", resp.AuthorizationEndpoint) + require.True(t, strings.HasPrefix(resp.TokenEndpoint, "https://auth.example.com/"), "token_endpoint should be absolute, got %q", resp.TokenEndpoint) + require.True(t, strings.HasPrefix(resp.JWKSURL, "https://auth.example.com/"), "jwks_uri should be absolute, got %q", resp.JWKSURL) + require.True(t, strings.HasPrefix(resp.UserInfoEndpoint, "https://auth.example.com/"), "userinfo_endpoint should be absolute, got %q", resp.UserInfoEndpoint) +} + +func TestWellKnownOpenIDIssuerStripsTrailingSlash(t *testing.T) { + mockAPI, _, err := setupAPIForTest() + require.NoError(t, err) + + mockAPI.config.JWT.Issuer = "https://auth.example.com/" + mockAPI.config.API.ExternalURL = "https://something-else.example.com" + + req := httptest.NewRequest(http.MethodGet, "/.well-known/openid-configuration", nil) + w := httptest.NewRecorder() + mockAPI.handler.ServeHTTP(w, req) + require.Equal(t, http.StatusOK, w.Code) + + var resp OpenIDConfigurationResponse + require.NoError(t, json.NewDecoder(w.Body).Decode(&resp)) + + require.Equal(t, "https://auth.example.com", resp.Issuer) + require.Equal(t, "https://auth.example.com/oauth/authorize", resp.AuthorizationEndpoint) + require.Equal(t, "https://auth.example.com/oauth/token", resp.TokenEndpoint) + require.Equal(t, "https://auth.example.com/.well-known/jwks.json", resp.JWKSURL) + require.Equal(t, "https://auth.example.com/oauth/userinfo", resp.UserInfoEndpoint) +}