Skip to content

Commit 2a7d1a3

Browse files
authored
Merge branch 'master' into iat/remove-create-extension-pg-trgm
2 parents ce68054 + 61632f8 commit 2a7d1a3

20 files changed

+1213
-103
lines changed

hack/test_asymmetric.env

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
GOTRUE_JWT_SECRET=X4dNv2oLvR7BnqmYk07Fi2NG2frvL2sxNQ9hxBJ65eDkFQ4He6qHrLJB9UrjYIZ1Rk0/yeh4vmihlCtxpFTeVQ==
2+
GOTRUE_JWT_KEYS=[{"kty":"oct","alg":"HS256","k":"WDRkTnYyb0x2UjdCbnFtWWswN0ZpMk5HMmZydkwyc3hOUTloeEJKNjVlRGtGUTRIZTZxSHJMSkI5VXJqWUlaMVJrMC95ZWg0dm1paGxDdHhwRlRlVlE9PQ","key_ops":["verify"],"kid":"jJmZzsVatG6yZMkn"},{"alg":"ES256","kid":"21a9eafe-24ab-4d10-960b-f949490f65e1","key_ops":["sign","verify"],"ext":true,"kty":"EC","x":"4vFbTWzvWn3qQv-9VUUlCK4tIMm_42raimXz2hywstU","y":"iggwYm-VfYTgBYKM4b0cyzmuHreNuR7x-986NUAvbAE","crv":"P-256","d":"DhhUbBJeGUwLlMO0SV-8U6idnsfsx5TOkZaJu-tsoF4"}]
3+
GOTRUE_JWT_EXP=3600
4+
GOTRUE_JWT_AUD="authenticated"
5+
GOTRUE_JWT_ADMIN_ROLES="supabase_admin,service_role"
6+
GOTRUE_JWT_DEFAULT_GROUP_NAME="authenticated"
7+
GOTRUE_DB_DRIVER=postgres
8+
DB_NAMESPACE="auth"
9+
GOTRUE_DB_AUTOMIGRATE=true
10+
DATABASE_URL="postgres://supabase_auth_admin:root@localhost:5432/postgres"
11+
GOTRUE_API_HOST=localhost
12+
PORT=9999
13+
API_EXTERNAL_URL="http://localhost:9999"
14+
GOTRUE_LOG_SQL=none
15+
GOTRUE_LOG_LEVEL=warn
16+
GOTRUE_SITE_URL=https://example.netlify.com
17+
GOTRUE_URI_ALLOW_LIST="http://localhost:3000,https://supabase.com/,http://localhost:5173/"
18+
GOTRUE_OPERATOR_TOKEN=foobar
19+
GOTRUE_EXTERNAL_APPLE_ENABLED=true
20+
GOTRUE_EXTERNAL_APPLE_CLIENT_ID=testclientid
21+
GOTRUE_EXTERNAL_APPLE_SECRET=testsecret
22+
GOTRUE_EXTERNAL_APPLE_REDIRECT_URI=https://identity.services.netlify.com/callback
23+
GOTRUE_EXTERNAL_AZURE_ENABLED=true
24+
GOTRUE_EXTERNAL_AZURE_CLIENT_ID=testclientid
25+
GOTRUE_EXTERNAL_AZURE_SECRET=testsecret
26+
GOTRUE_EXTERNAL_AZURE_REDIRECT_URI=https://identity.services.netlify.com/callback
27+
GOTRUE_EXTERNAL_BITBUCKET_ENABLED=true
28+
GOTRUE_EXTERNAL_BITBUCKET_CLIENT_ID=testclientid
29+
GOTRUE_EXTERNAL_BITBUCKET_SECRET=testsecret
30+
GOTRUE_EXTERNAL_BITBUCKET_REDIRECT_URI=https://identity.services.netlify.com/callback
31+
GOTRUE_EXTERNAL_DISCORD_ENABLED=true
32+
GOTRUE_EXTERNAL_DISCORD_CLIENT_ID=testclientid
33+
GOTRUE_EXTERNAL_DISCORD_SECRET=testsecret
34+
GOTRUE_EXTERNAL_DISCORD_REDIRECT_URI=https://identity.services.netlify.com/callback
35+
GOTRUE_EXTERNAL_FACEBOOK_ENABLED=true
36+
GOTRUE_EXTERNAL_FACEBOOK_CLIENT_ID=testclientid
37+
GOTRUE_EXTERNAL_FACEBOOK_SECRET=testsecret
38+
GOTRUE_EXTERNAL_FACEBOOK_REDIRECT_URI=https://identity.services.netlify.com/callback
39+
GOTRUE_EXTERNAL_FLY_ENABLED=true
40+
GOTRUE_EXTERNAL_FLY_CLIENT_ID=testclientid
41+
GOTRUE_EXTERNAL_FLY_SECRET=testsecret
42+
GOTRUE_EXTERNAL_FLY_REDIRECT_URI=https://identity.services.netlify.com/callback
43+
GOTRUE_EXTERNAL_FIGMA_ENABLED=true
44+
GOTRUE_EXTERNAL_FIGMA_CLIENT_ID=testclientid
45+
GOTRUE_EXTERNAL_FIGMA_SECRET=testsecret
46+
GOTRUE_EXTERNAL_FIGMA_REDIRECT_URI=https://identity.services.netlify.com/callback
47+
GOTRUE_EXTERNAL_GITHUB_ENABLED=true
48+
GOTRUE_EXTERNAL_GITHUB_CLIENT_ID=testclientid
49+
GOTRUE_EXTERNAL_GITHUB_SECRET=testsecret
50+
GOTRUE_EXTERNAL_GITHUB_REDIRECT_URI=https://identity.services.netlify.com/callback
51+
GOTRUE_EXTERNAL_KAKAO_ENABLED=true
52+
GOTRUE_EXTERNAL_KAKAO_CLIENT_ID=testclientid
53+
GOTRUE_EXTERNAL_KAKAO_SECRET=testsecret
54+
GOTRUE_EXTERNAL_KAKAO_REDIRECT_URI=https://identity.services.netlify.com/callback
55+
GOTRUE_EXTERNAL_KEYCLOAK_ENABLED=true
56+
GOTRUE_EXTERNAL_KEYCLOAK_CLIENT_ID=testclientid
57+
GOTRUE_EXTERNAL_KEYCLOAK_SECRET=testsecret
58+
GOTRUE_EXTERNAL_KEYCLOAK_REDIRECT_URI=https://identity.services.netlify.com/callback
59+
GOTRUE_EXTERNAL_KEYCLOAK_URL=https://keycloak.example.com/auth/realms/myrealm
60+
GOTRUE_EXTERNAL_LINKEDIN_ENABLED=true
61+
GOTRUE_EXTERNAL_LINKEDIN_CLIENT_ID=testclientid
62+
GOTRUE_EXTERNAL_LINKEDIN_SECRET=testsecret
63+
GOTRUE_EXTERNAL_LINKEDIN_REDIRECT_URI=https://identity.services.netlify.com/callback
64+
GOTRUE_EXTERNAL_LINKEDIN_OIDC_ENABLED=true
65+
GOTRUE_EXTERNAL_LINKEDIN_OIDC_CLIENT_ID=testclientid
66+
GOTRUE_EXTERNAL_LINKEDIN_OIDC_SECRET=testsecret
67+
GOTRUE_EXTERNAL_LINKEDIN_OIDC_REDIRECT_URI=https://identity.services.netlify.com/callback
68+
GOTRUE_EXTERNAL_GITLAB_ENABLED=true
69+
GOTRUE_EXTERNAL_GITLAB_CLIENT_ID=testclientid
70+
GOTRUE_EXTERNAL_GITLAB_SECRET=testsecret
71+
GOTRUE_EXTERNAL_GITLAB_REDIRECT_URI=https://identity.services.netlify.com/callback
72+
GOTRUE_EXTERNAL_GOOGLE_ENABLED=true
73+
GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID=testclientid
74+
GOTRUE_EXTERNAL_GOOGLE_SECRET=testsecret
75+
GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI=https://identity.services.netlify.com/callback
76+
GOTRUE_EXTERNAL_NOTION_ENABLED=true
77+
GOTRUE_EXTERNAL_NOTION_CLIENT_ID=testclientid
78+
GOTRUE_EXTERNAL_NOTION_SECRET=testsecret
79+
GOTRUE_EXTERNAL_NOTION_REDIRECT_URI=https://identity.services.netlify.com/callback
80+
GOTRUE_EXTERNAL_SNAPCHAT_ENABLED=true
81+
GOTRUE_EXTERNAL_SNAPCHAT_CLIENT_ID=testclientid
82+
GOTRUE_EXTERNAL_SNAPCHAT_SECRET=testsecret
83+
GOTRUE_EXTERNAL_SNAPCHAT_REDIRECT_URI=https://identity.services.netlify.com/callback
84+
GOTRUE_EXTERNAL_SNAPCHAT_EMAIL_OPTIONAL=true
85+
GOTRUE_EXTERNAL_SPOTIFY_ENABLED=true
86+
GOTRUE_EXTERNAL_SPOTIFY_CLIENT_ID=testclientid
87+
GOTRUE_EXTERNAL_SPOTIFY_SECRET=testsecret
88+
GOTRUE_EXTERNAL_SPOTIFY_REDIRECT_URI=https://identity.services.netlify.com/callback
89+
GOTRUE_EXTERNAL_SLACK_ENABLED=true
90+
GOTRUE_EXTERNAL_SLACK_CLIENT_ID=testclientid
91+
GOTRUE_EXTERNAL_SLACK_SECRET=testsecret
92+
GOTRUE_EXTERNAL_SLACK_REDIRECT_URI=https://identity.services.netlify.com/callback
93+
GOTRUE_EXTERNAL_SLACK_OIDC_ENABLED=true
94+
GOTRUE_EXTERNAL_SLACK_OIDC_CLIENT_ID=testclientid
95+
GOTRUE_EXTERNAL_SLACK_OIDC_SECRET=testsecret
96+
GOTRUE_EXTERNAL_SLACK_OIDC_REDIRECT_URI=https://identity.services.netlify.com/callback
97+
GOTRUE_EXTERNAL_WORKOS_ENABLED=true
98+
GOTRUE_EXTERNAL_WORKOS_CLIENT_ID=testclientid
99+
GOTRUE_EXTERNAL_WORKOS_SECRET=testsecret
100+
GOTRUE_EXTERNAL_WORKOS_REDIRECT_URI=https://identity.services.netlify.com/callback
101+
GOTRUE_EXTERNAL_TWITCH_ENABLED=true
102+
GOTRUE_EXTERNAL_TWITCH_CLIENT_ID=testclientid
103+
GOTRUE_EXTERNAL_TWITCH_SECRET=testsecret
104+
GOTRUE_EXTERNAL_TWITCH_REDIRECT_URI=https://identity.services.netlify.com/callback
105+
GOTRUE_EXTERNAL_TWITTER_ENABLED=true
106+
GOTRUE_EXTERNAL_TWITTER_CLIENT_ID=testclientid
107+
GOTRUE_EXTERNAL_TWITTER_SECRET=testsecret
108+
GOTRUE_EXTERNAL_TWITTER_REDIRECT_URI=https://identity.services.netlify.com/callback
109+
GOTRUE_EXTERNAL_ZOOM_ENABLED=true
110+
GOTRUE_EXTERNAL_ZOOM_CLIENT_ID=testclientid
111+
GOTRUE_EXTERNAL_ZOOM_SECRET=testsecret
112+
GOTRUE_EXTERNAL_ZOOM_REDIRECT_URI=https://identity.services.netlify.com/callback
113+
GOTRUE_EXTERNAL_FLOW_STATE_EXPIRY_DURATION="300s"
114+
GOTRUE_EXTERNAL_WEB3_SOLANA_ENABLED="true"
115+
GOTRUE_RATE_LIMIT_VERIFY="100000"
116+
GOTRUE_RATE_LIMIT_TOKEN_REFRESH="30"
117+
GOTRUE_RATE_LIMIT_ANONYMOUS_USERS="5"
118+
GOTRUE_RATE_LIMIT_HEADER="My-Custom-Header"
119+
GOTRUE_TRACING_ENABLED=true
120+
GOTRUE_TRACING_EXPORTER=default
121+
GOTRUE_TRACING_HOST=127.0.0.1
122+
GOTRUE_TRACING_PORT=8126
123+
GOTRUE_TRACING_TAGS="env:test"
124+
GOTRUE_SECURITY_CAPTCHA_ENABLED="false"
125+
GOTRUE_SECURITY_CAPTCHA_PROVIDER="hcaptcha"
126+
GOTRUE_SECURITY_CAPTCHA_SECRET="0x0000000000000000000000000000000000000000"
127+
GOTRUE_SECURITY_CAPTCHA_TIMEOUT="10s"
128+
GOTRUE_SAML_ENABLED="true"
129+
GOTRUE_SAML_PRIVATE_KEY="MIIEowIBAAKCAQEAszrVveMQcSsa0Y+zN1ZFb19cRS0jn4UgIHTprW2tVBmO2PABzjY3XFCfx6vPirMAPWBYpsKmXrvm1tr0A6DZYmA8YmJd937VUQ67fa6DMyppBYTjNgGEkEhmKuszvF3MARsIKCGtZqUrmS7UG4404wYxVppnr2EYm3RGtHlkYsXu20MBqSDXP47bQP+PkJqC3BuNGk3xt5UHl2FSFpTHelkI6lBynw16B+lUT1F96SERNDaMqi/TRsZdGe5mB/29ngC/QBMpEbRBLNRir5iUevKS7Pn4aph9Qjaxx/97siktK210FJT23KjHpgcUfjoQ6BgPBTLtEeQdRyDuc/CgfwIDAQABAoIBAGYDWOEpupQPSsZ4mjMnAYJwrp4ZISuMpEqVAORbhspVeb70bLKonT4IDcmiexCg7cQBcLQKGpPVM4CbQ0RFazXZPMVq470ZDeWDEyhoCfk3bGtdxc1Zc9CDxNMs6FeQs6r1beEZug6weG5J/yRn/qYxQife3qEuDMl+lzfl2EN3HYVOSnBmdt50dxRuX26iW3nqqbMRqYn9OHuJ1LvRRfYeyVKqgC5vgt/6Tf7DAJwGe0dD7q08byHV8DBZ0pnMVU0bYpf1GTgMibgjnLjK//EVWafFHtN+RXcjzGmyJrk3+7ZyPUpzpDjO21kpzUQLrpEkkBRnmg6bwHnSrBr8avECgYEA3pq1PTCAOuLQoIm1CWR9/dhkbJQiKTJevlWV8slXQLR50P0WvI2RdFuSxlWmA4xZej8s4e7iD3MYye6SBsQHygOVGc4efvvEZV8/XTlDdyj7iLVGhnEmu2r7AFKzy8cOvXx0QcLg+zNd7vxZv/8D3Qj9Jje2LjLHKM5n/dZ3RzUCgYEAzh5Lo2anc4WN8faLGt7rPkGQF+7/18ImQE11joHWa3LzAEy7FbeOGpE/vhOv5umq5M/KlWFIRahMEQv4RusieHWI19ZLIP+JwQFxWxS+cPp3xOiGcquSAZnlyVSxZ//dlVgaZq2o2MfrxECcovRlaknl2csyf+HjFFwKlNxHm2MCgYAr//R3BdEy0oZeVRndo2lr9YvUEmu2LOihQpWDCd0fQw0ZDA2kc28eysL2RROte95r1XTvq6IvX5a0w11FzRWlDpQ4J4/LlcQ6LVt+98SoFwew+/PWuyLmxLycUbyMOOpm9eSc4wJJZNvaUzMCSkvfMtmm5jgyZYMMQ9A2Ul/9SQKBgB9mfh9mhBwVPIqgBJETZMMXOdxrjI5SBYHGSyJqpT+5Q0vIZLfqPrvNZOiQFzwWXPJ+tV4Mc/YorW3rZOdo6tdvEGnRO6DLTTEaByrY/io3/gcBZXoSqSuVRmxleqFdWWRnB56c1hwwWLqNHU+1671FhL6pNghFYVK4suP6qu4BAoGBAMk+VipXcIlD67mfGrET/xDqiWWBZtgTzTMjTpODhDY1GZck1eb4CQMP5j5V3gFJ4cSgWDJvnWg8rcz0unz/q4aeMGl1rah5WNDWj1QKWMS6vJhMHM/rqN1WHWR0ZnV83svYgtg0zDnQKlLujqW4JmGXLMU7ur6a+e6lpa1fvLsP"
130+
GOTRUE_MAX_VERIFIED_FACTORS=10
131+
GOTRUE_SMS_TEST_OTP_VALID_UNTIL=""
132+
GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPT=true
133+
GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPTION_KEY_ID=abc
134+
GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPTION_KEY=pwFoiPyybQMqNmYVN0gUnpbfpGQV2sDv9vp0ZAxi_Y4
135+
GOTRUE_SECURITY_DB_ENCRYPTION_DECRYPTION_KEYS=abc:pwFoiPyybQMqNmYVN0gUnpbfpGQV2sDv9vp0ZAxi_Y4

internal/api/api.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,12 @@ func NewAPIWithVersion(globalConfig *conf.GlobalConfiguration, db *storage.Conne
175175

176176
r.Get("/health", api.HealthCheck)
177177
r.Get("/.well-known/jwks.json", api.WellKnownJwks)
178-
r.Get("/.well-known/openid-configuration", api.WellKnownOpenID)
179178

179+
// Both OIDC Discovery and OAuth Authorization Server Metadata use the same unified handler
180+
// OIDC Discovery is an extension of RFC 8414, so one response satisfies both specs
181+
r.Get("/.well-known/openid-configuration", api.WellKnownOpenID)
180182
if globalConfig.OAuthServer.Enabled {
181-
r.Get("/.well-known/oauth-authorization-server", api.oauthServer.OAuthServerMetadata)
183+
r.Get("/.well-known/oauth-authorization-server", api.WellKnownOpenID)
182184
}
183185

184186
r.Route("/callback", func(r *router) {
@@ -374,6 +376,9 @@ func NewAPIWithVersion(globalConfig *conf.GlobalConfiguration, db *storage.Conne
374376
// OAuth Token endpoint (public, with client authentication)
375377
r.With(api.requireOAuthClientAuth).Post("/token", api.oauthServer.OAuthToken)
376378

379+
// OIDC UserInfo endpoint (requires user authentication via Bearer token)
380+
r.With(api.requireAuthentication).Get("/userinfo", api.oauthServer.OAuthUserInfo)
381+
377382
// OAuth 2.1 Authorization endpoints
378383
// `/authorize` to initiate OAuth2 authorization code flow where Supabase Auth is the OAuth2 provider
379384
r.Get("/authorize", api.oauthServer.OAuthServerAuthorize)

internal/api/auth.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/gofrs/uuid"
1111
jwt "github.com/golang-jwt/jwt/v5"
1212
"github.com/supabase/auth/internal/api/apierrors"
13+
"github.com/supabase/auth/internal/api/shared"
1314
"github.com/supabase/auth/internal/conf"
1415
"github.com/supabase/auth/internal/models"
1516
"github.com/supabase/auth/internal/storage"
@@ -147,6 +148,8 @@ func (a *API) maybeLoadUserOrSession(ctx context.Context) (context.Context, erro
147148
return ctx, err
148149
}
149150
ctx = withSession(ctx, session)
151+
// Also store in shared context for cross-package access (e.g., oauthserver package)
152+
ctx = shared.WithSession(ctx, session)
150153
}
151154
return ctx, nil
152155
}

internal/api/jwks.go

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/lestrrat-go/jwx/v2/jwa"
77
jwk "github.com/lestrrat-go/jwx/v2/jwk"
8+
"github.com/supabase/auth/internal/models"
89
)
910

1011
type JwksResponse struct {
@@ -29,18 +30,91 @@ func (a *API) WellKnownJwks(w http.ResponseWriter, r *http.Request) error {
2930
return sendJSON(w, http.StatusOK, resp)
3031
}
3132

33+
// OpenIDConfigurationResponse represents both OIDC Discovery and OAuth 2.0 Authorization Server Metadata
34+
// This unified response serves both:
35+
// - /.well-known/openid-configuration (OIDC Discovery per OpenID Connect Discovery 1.0)
36+
// - /.well-known/oauth-authorization-server (OAuth Authorization Server Metadata per RFC 8414)
37+
//
38+
// Since OIDC Discovery extends RFC 8414, a single response structure satisfies both specifications.
3239
type OpenIDConfigurationResponse struct {
33-
Issuer string `json:"issuer"`
34-
JWKSURL string `json:"jwks_uri"`
40+
// Core Discovery Fields (Required by both OIDC and OAuth 2.0)
41+
Issuer string `json:"issuer"`
42+
AuthorizationEndpoint string `json:"authorization_endpoint"`
43+
TokenEndpoint string `json:"token_endpoint"`
44+
JWKSURL string `json:"jwks_uri"`
45+
UserInfoEndpoint string `json:"userinfo_endpoint,omitempty"` // OIDC-specific
46+
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
47+
48+
// Supported Parameters
49+
ScopesSupported []string `json:"scopes_supported,omitempty"`
50+
ResponseTypesSupported []string `json:"response_types_supported"`
51+
ResponseModesSupported []string `json:"response_modes_supported,omitempty"`
52+
GrantTypesSupported []string `json:"grant_types_supported"`
53+
SubjectTypesSupported []string `json:"subject_types_supported"` // OIDC-specific
54+
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"` // OIDC-specific
55+
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported"`
56+
ClaimsSupported []string `json:"claims_supported,omitempty"` // OIDC-specific
57+
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"` // OAuth 2.1/PKCE
3558
}
3659

60+
// WellKnownOpenID handles both OIDC Discovery and OAuth 2.0 Authorization Server Metadata endpoints
61+
// This unified handler serves:
62+
// - GET /.well-known/openid-configuration (OIDC Discovery)
63+
// - GET /.well-known/oauth-authorization-server (RFC 8414)
64+
//
65+
// Both endpoints return the same comprehensive metadata since OIDC Discovery is a superset of OAuth 2.0 metadata
3766
func (a *API) WellKnownOpenID(w http.ResponseWriter, r *http.Request) error {
3867
config := a.config
68+
issuer := config.JWT.Issuer
69+
70+
// Ensure issuer doesn't end with a slash to avoid double slashes in URLs
71+
for len(issuer) > 0 && issuer[len(issuer)-1] == '/' {
72+
issuer = issuer[:len(issuer)-1]
73+
}
74+
75+
response := OpenIDConfigurationResponse{
76+
Issuer: config.JWT.Issuer,
77+
AuthorizationEndpoint: issuer + "/oauth/authorize",
78+
TokenEndpoint: issuer + "/oauth/token",
79+
JWKSURL: issuer + "/.well-known/jwks.json",
80+
UserInfoEndpoint: issuer + "/oauth/userinfo",
81+
82+
// OAuth 2.1 / OIDC Supported Features
83+
ResponseTypesSupported: []string{"code"},
84+
ResponseModesSupported: []string{"query"},
85+
GrantTypesSupported: []string{"authorization_code", "refresh_token"},
86+
SubjectTypesSupported: []string{"public"},
87+
IDTokenSigningAlgValuesSupported: []string{"RS256", "HS256", "ES256"}, // TODO :: should create this based on signing key config?
88+
TokenEndpointAuthMethodsSupported: []string{"client_secret_basic", "client_secret_post", "none"},
89+
CodeChallengeMethodsSupported: []string{"S256", "plain"},
90+
ScopesSupported: models.SupportedOAuthScopes,
91+
92+
// OIDC Standard Claims
93+
ClaimsSupported: []string{
94+
"sub",
95+
"aud",
96+
"iss",
97+
"exp",
98+
"iat",
99+
"auth_time",
100+
"nonce",
101+
"email",
102+
"email_verified",
103+
"phone_number",
104+
"phone_number_verified",
105+
"name",
106+
"picture",
107+
"preferred_username",
108+
"updated_at",
109+
},
110+
}
111+
112+
// Include registration endpoint if dynamic registration is enabled
113+
if config.OAuthServer.Enabled && config.OAuthServer.AllowDynamicRegistration {
114+
response.RegistrationEndpoint = issuer + "/oauth/clients/register"
115+
}
39116

40117
w.Header().Set("Cache-Control", "public, max-age=600")
41118

42-
return sendJSON(w, http.StatusOK, OpenIDConfigurationResponse{
43-
Issuer: config.JWT.Issuer,
44-
JWKSURL: config.JWT.Issuer + "/.well-known/jwks.json",
45-
})
119+
return sendJSON(w, http.StatusOK, response)
46120
}

internal/api/oauthserver/authorize.go

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type AuthorizeParams struct {
3030
Resource string `json:"resource"`
3131
CodeChallenge string `json:"code_challenge"`
3232
CodeChallengeMethod string `json:"code_challenge_method"`
33+
Nonce string `json:"nonce"` // OIDC nonce parameter
3334
}
3435

3536
// AuthorizationDetailsResponse represents the response for getting authorization details
@@ -96,6 +97,7 @@ func (s *Server) OAuthServerAuthorize(w http.ResponseWriter, r *http.Request) er
9697
Resource: query.Get("resource"),
9798
CodeChallenge: query.Get("code_challenge"),
9899
CodeChallengeMethod: query.Get("code_challenge_method"),
100+
Nonce: query.Get("nonce"),
99101
}
100102

101103
// validate basic required parameters (client_id, redirect_uri)
@@ -134,15 +136,17 @@ func (s *Server) OAuthServerAuthorize(w http.ResponseWriter, r *http.Request) er
134136
}
135137

136138
// Store authorization request in database (without user initially)
137-
authorization := models.NewOAuthServerAuthorization(
138-
client.ID, // Use the client's UUID instead of the public client_id string
139-
params.RedirectURI,
140-
params.Scope,
141-
params.State,
142-
params.Resource,
143-
params.CodeChallenge,
144-
params.CodeChallengeMethod,
145-
)
139+
authorization := models.NewOAuthServerAuthorization(models.NewOAuthServerAuthorizationParams{
140+
ClientID: client.ID,
141+
RedirectURI: params.RedirectURI,
142+
Scope: params.Scope,
143+
State: params.State,
144+
Resource: params.Resource,
145+
CodeChallenge: params.CodeChallenge,
146+
CodeChallengeMethod: params.CodeChallengeMethod,
147+
TTL: config.OAuthServer.AuthorizationTTL,
148+
Nonce: params.Nonce,
149+
})
146150

147151
if err := models.CreateOAuthServerAuthorization(db, authorization); err != nil {
148152
// Error creating authorization - redirect with server_error
@@ -457,6 +461,11 @@ func (s *Server) validateRemainingAuthorizeParams(params *AuthorizeParams) error
457461
return errors.New("only response_type=code is supported")
458462
}
459463

464+
// Validate scopes
465+
if err := s.validateScopes(params.Scope); err != nil {
466+
return err
467+
}
468+
460469
// Resource parameter validation (per RFC 8707)
461470
if err := s.validateResourceParam(params.Resource); err != nil {
462471
return err
@@ -521,6 +530,27 @@ func (s *Server) validateResourceParam(resource string) error {
521530
return nil
522531
}
523532

533+
// validateScopes validates the requested scopes
534+
func (s *Server) validateScopes(scopeString string) error {
535+
if scopeString == "" {
536+
return errors.New("scope parameter is required")
537+
}
538+
539+
scopes := models.ParseScopeString(scopeString)
540+
if len(scopes) == 0 {
541+
return errors.New("scope parameter cannot be empty")
542+
}
543+
544+
// Validate each scope against the centrally defined supported scopes
545+
for _, scope := range scopes {
546+
if !models.IsSupportedScope(scope) {
547+
return fmt.Errorf("unsupported scope: %s", scope)
548+
}
549+
}
550+
551+
return nil
552+
}
553+
524554
func (s *Server) isValidRedirectURI(client *models.OAuthServerClient, redirectURI string) bool {
525555
registeredURIs := client.GetRedirectURIs()
526556
for _, registeredURI := range registeredURIs {

0 commit comments

Comments
 (0)