Files
coder/coderd/oauth2_metadata_test.go
T
Thomas Kosiewski 05537c1894 feat: publish RBAC scopes in OAuth2 metadata endpoints (#19942)
<!--

If you have used AI to produce some or all of this PR, please ensure you have read our [AI Contribution guidelines](https://coder.com/docs/about/contributing/AI_CONTRIBUTING) before submitting.

-->

Publish supported OAuth2 scopes from RBAC external scope names

This PR updates the OAuth2 metadata endpoints to publish the supported scopes from the RBAC external scope names. Previously, the `ScopesSupported` field was empty with a TODO to implement a scope system. Now, both the authorization server metadata and protected resource metadata endpoints return the list of scopes from `rbac.ExternalScopeNames()`.

The tests have been updated to verify that the correct scopes are being returned in the metadata responses.
2025-09-26 12:15:36 +02:00

89 lines
2.9 KiB
Go

package coderd_test
import (
"context"
"encoding/json"
"net/http"
"net/url"
"testing"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/testutil"
)
func TestOAuth2AuthorizationServerMetadata(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
serverURL := client.URL
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
// Use a plain HTTP client since this endpoint doesn't require authentication
endpoint := serverURL.ResolveReference(&url.URL{Path: "/.well-known/oauth-authorization-server"}).String()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
require.NoError(t, err)
resp, err := http.DefaultClient.Do(req)
require.NoError(t, err)
defer resp.Body.Close()
require.Equal(t, http.StatusOK, resp.StatusCode)
var metadata codersdk.OAuth2AuthorizationServerMetadata
err = json.NewDecoder(resp.Body).Decode(&metadata)
require.NoError(t, err)
// Verify the metadata
require.NotEmpty(t, metadata.Issuer)
require.NotEmpty(t, metadata.AuthorizationEndpoint)
require.NotEmpty(t, metadata.TokenEndpoint)
require.Contains(t, metadata.ResponseTypesSupported, "code")
require.Contains(t, metadata.GrantTypesSupported, "authorization_code")
require.Contains(t, metadata.GrantTypesSupported, "refresh_token")
require.Contains(t, metadata.CodeChallengeMethodsSupported, "S256")
// Supported scopes are published from the curated catalog
require.Equal(t, rbac.ExternalScopeNames(), metadata.ScopesSupported)
}
func TestOAuth2ProtectedResourceMetadata(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
serverURL := client.URL
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
// Use a plain HTTP client since this endpoint doesn't require authentication
endpoint := serverURL.ResolveReference(&url.URL{Path: "/.well-known/oauth-protected-resource"}).String()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
require.NoError(t, err)
resp, err := http.DefaultClient.Do(req)
require.NoError(t, err)
defer resp.Body.Close()
require.Equal(t, http.StatusOK, resp.StatusCode)
var metadata codersdk.OAuth2ProtectedResourceMetadata
err = json.NewDecoder(resp.Body).Decode(&metadata)
require.NoError(t, err)
// Verify the metadata
require.NotEmpty(t, metadata.Resource)
require.NotEmpty(t, metadata.AuthorizationServers)
require.Len(t, metadata.AuthorizationServers, 1)
require.Equal(t, metadata.Resource, metadata.AuthorizationServers[0])
// RFC 6750 bearer tokens are now supported as fallback methods
require.Contains(t, metadata.BearerMethodsSupported, "header")
require.Contains(t, metadata.BearerMethodsSupported, "query")
// Supported scopes are published from the curated catalog
require.Equal(t, rbac.ExternalScopeNames(), metadata.ScopesSupported)
}