feat(oidc): optimize the userinfo endpoint (#7706)

* feat(oidc): optimize the userinfo endpoint

* store project ID in the access token

* query for projectID if not in token

* add scope based tests

* Revert "store project ID in the access token"

This reverts commit 5f0262f239.

* query project role assertion

* use project role assertion setting to return roles

* workaround eventual consistency and handle PAT

* do not append empty project id
This commit is contained in:
Tim Möhlmann
2024-04-09 16:15:35 +03:00
committed by GitHub
parent c8e0b30e17
commit 6a51c4b0f5
25 changed files with 528 additions and 159 deletions

View File

@@ -28,7 +28,6 @@ func (s *Server) Introspect(ctx context.Context, r *op.Request[op.IntrospectionR
return s.LegacyServer.Introspect(ctx, r)
}
if features.TriggerIntrospectionProjections {
// Execute all triggers in one concurrent sweep.
query.TriggerIntrospectionProjections(ctx)
}
@@ -100,7 +99,7 @@ func (s *Server) Introspect(ctx context.Context, r *op.Request[op.IntrospectionR
if err = validateIntrospectionAudience(token.audience, client.clientID, client.projectID); err != nil {
return nil, err
}
userInfo, err := s.userInfo(ctx, token.userID, client.projectID, token.scope, []string{client.projectID})
userInfo, err := s.userInfo(ctx, token.userID, client.projectID, client.projectRoleAssertion, token.scope, []string{client.projectID})
if err != nil {
return nil, err
}
@@ -124,9 +123,10 @@ func (s *Server) Introspect(ctx context.Context, r *op.Request[op.IntrospectionR
}
type introspectionClientResult struct {
clientID string
projectID string
err error
clientID string
projectID string
projectRoleAssertion bool
err error
}
var errNoClientSecret = errors.New("client has no configured secret")
@@ -134,35 +134,36 @@ var errNoClientSecret = errors.New("client has no configured secret")
func (s *Server) introspectionClientAuth(ctx context.Context, cc *op.ClientCredentials, rc chan<- *introspectionClientResult) {
ctx, span := tracing.NewSpan(ctx)
clientID, projectID, err := func() (string, string, error) {
clientID, projectID, projectRoleAssertion, err := func() (string, string, bool, error) {
client, err := s.clientFromCredentials(ctx, cc)
if err != nil {
return "", "", err
return "", "", false, err
}
if cc.ClientAssertion != "" {
verifier := op.NewJWTProfileVerifierKeySet(keySetMap(client.PublicKeys), op.IssuerFromContext(ctx), time.Hour, time.Second)
if _, err := op.VerifyJWTAssertion(ctx, cc.ClientAssertion, verifier); err != nil {
return "", "", oidc.ErrUnauthorizedClient().WithParent(err)
return "", "", false, oidc.ErrUnauthorizedClient().WithParent(err)
}
return client.ClientID, client.ProjectID, nil
return client.ClientID, client.ProjectID, client.ProjectRoleAssertion, nil
}
if client.HashedSecret != "" {
if err := s.introspectionClientSecretAuth(ctx, client, cc.ClientSecret); err != nil {
return "", "", oidc.ErrUnauthorizedClient().WithParent(err)
return "", "", false, oidc.ErrUnauthorizedClient().WithParent(err)
}
return client.ClientID, client.ProjectID, nil
return client.ClientID, client.ProjectID, client.ProjectRoleAssertion, nil
}
return "", "", oidc.ErrUnauthorizedClient().WithParent(errNoClientSecret)
return "", "", false, oidc.ErrUnauthorizedClient().WithParent(errNoClientSecret)
}()
span.EndWithError(err)
rc <- &introspectionClientResult{
clientID: clientID,
projectID: projectID,
err: err,
clientID: clientID,
projectID: projectID,
projectRoleAssertion: projectRoleAssertion,
err: err,
}
}