Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions internal/resources/common/url.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
)

// NormalizeExternalURL returns an absolute URL with a stable, slash-free path
// representation. It is used for GoTrue's issuer and for derived public URLs.
// representation for derived public URLs.
func NormalizeExternalURL(raw string) string {
u, err := url.Parse(strings.TrimSpace(raw))
if err != nil || u.Scheme == "" || u.Host == "" {
Expand All @@ -23,12 +23,18 @@ func NormalizeExternalURL(raw string) string {
return strings.TrimRight(u.String(), "/")
}

// AuthJWKSURL derives the public Auth JWKS endpoint from the configured Auth
// AuthIssuerURL derives the canonical Supabase Auth issuer from the configured
// external URL. If the URL already includes /auth/v1, it is preserved.
func AuthJWKSURL(externalURL string) string {
func AuthIssuerURL(externalURL string) string {
base := NormalizeExternalURL(externalURL)
if strings.HasSuffix(base, "/auth/v1") {
return base + "/.well-known/jwks.json"
return base
}
return fmt.Sprintf("%s/auth/v1/.well-known/jwks.json", base)
return fmt.Sprintf("%s/auth/v1", base)
}

// AuthJWKSURL derives the public Auth JWKS endpoint from the configured Auth
// external URL. If the URL already includes /auth/v1, it is preserved.
func AuthJWKSURL(externalURL string) string {
return AuthIssuerURL(externalURL) + "/.well-known/jwks.json"
}
2 changes: 1 addition & 1 deletion internal/resources/deployments/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func buildAuthEnv(project *supabasev1alpha1.SupabaseProject, secretNames *supaba
corev1.EnvVar{Name: "GOTRUE_JWT_ADMIN_ROLES", Value: "service_role"},
corev1.EnvVar{Name: "GOTRUE_JWT_AUD", Value: "authenticated"},
corev1.EnvVar{Name: "GOTRUE_JWT_EXP", Value: fmt.Sprintf("%d", common.GetAccessTokenExpiration(project))},
corev1.EnvVar{Name: "GOTRUE_JWT_ISSUER", Value: common.NormalizeExternalURL(spec.ExternalURL)},
corev1.EnvVar{Name: "GOTRUE_JWT_ISSUER", Value: common.AuthIssuerURL(spec.ExternalURL)},
corev1.EnvVar{Name: "GOTRUE_JWT_VALID_METHODS", Value: "ES256"},
corev1.EnvVar{
Name: "GOTRUE_JWT_KEYS",
Expand Down
17 changes: 17 additions & 0 deletions internal/resources/deployments/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,23 @@ func TestAuthDeploymentRendersAnonymousSignInsSetting(t *testing.T) {
}
}

func TestAuthDeploymentRendersCanonicalJWTIssuer(t *testing.T) {
project := newTestProject(testNamespace)
project.Spec.Auth.ExternalURL = "https://auth.example.com"

deployment := BuildAuthDeployment(project, newTestSecretNames())
for _, variable := range deployment.Spec.Template.Spec.Containers[0].Env {
if variable.Name != "GOTRUE_JWT_ISSUER" {
continue
}
if variable.Value != "https://auth.example.com/auth/v1" {
t.Fatalf("GOTRUE_JWT_ISSUER = %q, want %q", variable.Value, "https://auth.example.com/auth/v1")
}
return
}
t.Fatal("GOTRUE_JWT_ISSUER was not rendered")
}

func TestAuthEmailHookUsesGeneratedSigningSecret(t *testing.T) {
project := newTestProject(testNamespace)
project.Spec.Auth.EmailHook = &supabasev1alpha1.EmailHookSpec{
Expand Down
Loading