Add OAuth2 API scope registration and enforcement

Register v1 API scopes in coredata, advertise them in OIDC discovery
and protected-resource metadata, show them on the consent screen, and
enforce scope-to-action mapping in the IAM Authorizer before policy
evaluation.

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
This commit is contained in:
Ludovic Vielle
2026-06-15 17:33:15 +02:00
parent 25151fa089
commit 3ebb221a9b
56 changed files with 1918 additions and 290 deletions

View File

@@ -96,6 +96,14 @@ type (
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"`
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"`
ClaimsSupported []string `json:"claims_supported"`
ProtectedResources []string `json:"protected_resources,omitempty"`
}
OAuth2ProtectedResourceMetadataResponse struct {
Resource string `json:"resource"`
AuthorizationServers []string `json:"authorization_servers"`
BearerMethodsSupported []string `json:"bearer_methods_supported"`
ScopesSupported []string `json:"scopes_supported"`
}
OAuth2JWKSResponse struct {
@@ -265,6 +273,28 @@ func OAuth2JWKS(c *Client) (*OAuth2JWKSResponse, *OAuth2HTTPResponse, error) {
return &result, raw, nil
}
// OAuth2ProtectedResourceMetadata fetches the RFC 9728 protected resource
// metadata document.
func OAuth2ProtectedResourceMetadata(
c *Client,
) (*OAuth2ProtectedResourceMetadataResponse, *OAuth2HTTPResponse, error) {
raw, err := getJSON(c.HTTPClient(), c.BaseURL()+"/.well-known/oauth-protected-resource", nil)
if err != nil {
return nil, nil, err
}
if raw.StatusCode != http.StatusOK {
return nil, raw, nil
}
var result OAuth2ProtectedResourceMetadataResponse
if err := json.Unmarshal(raw.Body, &result); err != nil {
return nil, raw, fmt.Errorf("cannot decode protected resource metadata: %w", err)
}
return &result, raw, nil
}
// OAuth2RegisterClient registers a new OAuth2 client via dynamic registration.
func OAuth2RegisterClient(
c *Client,
@@ -890,13 +920,32 @@ func OAuth2PerformAuthorizationCodeFlow(
) *OAuth2TokenResponse {
t.Helper()
return OAuth2PerformAuthorizationCodeFlowWithScopes(
t,
c,
clientID,
clientSecret,
redirectURI,
"openid email profile offline_access",
)
}
// OAuth2PerformAuthorizationCodeFlowWithScopes performs the authorization code
// flow with the requested OAuth2 scopes.
func OAuth2PerformAuthorizationCodeFlowWithScopes(
t testing.TB,
c *Client,
clientID, clientSecret, redirectURI, scopes string,
) *OAuth2TokenResponse {
t.Helper()
verifier, challenge := GeneratePKCE()
params := url.Values{
"client_id": {clientID},
"redirect_uri": {redirectURI},
"response_type": {"code"},
"scope": {"openid email profile offline_access"},
"scope": {scopes},
"state": {"test-state"},
"code_challenge": {challenge},
"code_challenge_method": {"S256"},