feat: implement OAuth Client ID Metadata Document (#1525) (#1526)

Co-authored-by: Elias Schneider <login@eliasschneider.com>
This commit is contained in:
Jean-François Roy
2026-08-02 15:05:39 +00:00
committed by GitHub
co-authored by Elias Schneider
parent 7c55bdf115
commit 1934efa84c
67 changed files with 2311 additions and 217 deletions
+23 -5
View File
@@ -9,6 +9,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/lestrrat-go/jwx/v3/jwa"
"github.com/pocket-id/pocket-id/backend/internal/model"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"gorm.io/gorm"
)
@@ -42,6 +43,8 @@ type Dependencies struct {
Config Config
HTTPClient *http.Client
GetCIMDURLAllowlist func() []string
Signer TokenSigner
CustomClaims CustomClaimSource
Reauth ReauthenticationTokenConsumer
@@ -52,8 +55,9 @@ type Dependencies struct {
type Module struct {
Preview *ClientPreviewBuilder
config Config
store *Store
config Config
store *Store
cimdResolver *cimdClientResolver
authorizationHandler *authorizationHandler
tokenHandler *tokenHandler
@@ -66,11 +70,19 @@ type Module struct {
func New(ctx context.Context, deps Dependencies) (*Module, error) {
store := NewStore(deps.DB, deps.APIAccess).WithIssuer(deps.Config.BaseURL)
cimdResolver := newCIMDClientResolver(store, cimdResolverConfig{
getURLAllowlist: deps.GetCIMDURLAllowlist,
transportDecorator: func(transport http.RoundTripper) http.RoundTripper {
return otelhttp.NewTransport(transport)
},
})
store.clientResolver = cimdResolver
authenticator, err := newFederatedClientAuthenticator(ctx, store, deps.HTTPClient, deps.Config.BaseURL)
if err != nil {
return nil, fmt.Errorf("failed to create federated client authenticator: %w", err)
}
provider, err := newProvider(store, authenticator, deps.Signer, deps.Config)
provider, err := newProvider(store, authenticator, deps.Signer, deps.Config, cimdResolver)
if err != nil {
return nil, fmt.Errorf("failed to create OAuth2 provider: %w", err)
}
@@ -85,8 +97,9 @@ func New(ctx context.Context, deps Dependencies) (*Module, error) {
return &Module{
Preview: previewBuilder,
config: deps.Config,
store: store,
config: deps.Config,
store: store,
cimdResolver: cimdResolver,
authorizationHandler: newAuthorizationHandler(provider, authorizationService, deps.Config.BaseURL),
tokenHandler: newTokenHandler(provider, claimsService, deps.APIAccess),
@@ -98,6 +111,11 @@ func New(ctx context.Context, deps Dependencies) (*Module, error) {
}, nil
}
// RefreshClientMetadata forces a re-fetch of the OAuth Client ID Metadata Document.
func (m *Module) RefreshClientMetadata(ctx context.Context, clientID string) (model.OidcClient, error) {
return m.cimdResolver.RefreshMetadataClient(ctx, clientID)
}
func (m *Module) RegisterRoutes(rootGroup *gin.RouterGroup, apiGroup *gin.RouterGroup, optionalBrowserAuth gin.HandlerFunc, browserAuth gin.HandlerFunc) {
rootGroup.GET("/authorize", optionalBrowserAuth, m.authorizationHandler.authorize)
rootGroup.POST("/authorize", optionalBrowserAuth, m.authorizationHandler.authorize)