feat(oidc): token exchange impersonation (#7516)

* add token exchange feature flag

* allow setting reason and actor to access tokens

* impersonation

* set token types and scopes in response

* upgrade oidc to working draft state

* fix tests

* audience and scope validation

* id toke and jwt as input

* return id tokens

* add grant type  token exchange to app config

* add integration tests

* check and deny actors in api calls

* fix instance setting tests by triggering projection on write and cleanup

* insert sleep statements again

* solve linting issues

* add translations

* pin oidc v3.15.0

* resolve comments, add event translation

* fix refreshtoken test

* use ValidateAuthReqScopes from oidc

* apparently the linter can't make up its mind

* persist actor thru refresh tokens and check in tests

* remove unneeded triggers
This commit is contained in:
Tim Möhlmann
2024-03-20 12:18:46 +02:00
committed by GitHub
parent b338171585
commit 6398349c24
104 changed files with 2149 additions and 248 deletions

View File

@@ -207,27 +207,18 @@ func (o *OPStorage) CreateAccessToken(ctx context.Context, req op.TokenRequest)
err = oidcError(err)
span.EndWithError(err)
}()
var userAgentID, applicationID, userOrgID string
switch authReq := req.(type) {
case *AuthRequest:
userAgentID = authReq.AgentID
applicationID = authReq.ApplicationID
userOrgID = authReq.UserOrgID
case *AuthRequestV2:
// trigger activity log for authentication for user
if authReq, ok := req.(*AuthRequestV2); ok {
activity.Trigger(ctx, "", authReq.CurrentAuthRequest.UserID, activity.OIDCAccessToken, o.eventstore.FilterToQueryReducer)
return o.command.AddOIDCSessionAccessToken(setContextUserSystem(ctx), authReq.GetID())
case op.IDTokenRequest:
applicationID = authReq.GetClientID()
}
userAgentID, applicationID, userOrgID, authTime, amr, reason, actor := getInfoFromRequest(req)
accessTokenLifetime, _, _, _, err := o.getOIDCSettings(ctx)
if err != nil {
return "", time.Time{}, err
}
resp, err := o.command.AddUserToken(setContextUserSystem(ctx), userOrgID, userAgentID, applicationID, req.GetSubject(), req.GetAudience(), req.GetScopes(), accessTokenLifetime) //PLANNED: lifetime from client
resp, err := o.command.AddUserToken(setContextUserSystem(ctx), userOrgID, userAgentID, applicationID, req.GetSubject(), req.GetAudience(), req.GetScopes(), amr, accessTokenLifetime, authTime, reason, actor)
if err != nil {
return "", time.Time{}, err
}
@@ -256,7 +247,7 @@ func (o *OPStorage) CreateAccessAndRefreshTokens(ctx context.Context, req op.Tok
return o.command.ExchangeOIDCSessionRefreshAndAccessToken(setContextUserSystem(ctx), tokenReq.OIDCSessionWriteModel.AggregateID, refreshToken, tokenReq.RequestedScopes)
}
userAgentID, applicationID, userOrgID, authTime, authMethodsReferences := getInfoFromRequest(req)
userAgentID, applicationID, userOrgID, authTime, authMethodsReferences, reason, actor := getInfoFromRequest(req)
scopes, err := o.assertProjectRoleScopes(ctx, applicationID, req.GetScopes())
if err != nil {
return "", "", time.Time{}, zerrors.ThrowPreconditionFailed(err, "OIDC-Df2fq", "Errors.Internal")
@@ -272,7 +263,7 @@ func (o *OPStorage) CreateAccessAndRefreshTokens(ctx context.Context, req op.Tok
resp, token, err := o.command.AddAccessAndRefreshToken(setContextUserSystem(ctx), userOrgID, userAgentID, applicationID, req.GetSubject(),
refreshToken, req.GetAudience(), scopes, authMethodsReferences, accessTokenLifetime,
refreshTokenIdleExpiration, refreshTokenExpiration, authTime) //PLANNED: lifetime from client
refreshTokenIdleExpiration, refreshTokenExpiration, authTime, reason, actor) //PLANNED: lifetime from client
if err != nil {
if zerrors.IsErrorInvalidArgument(err) {
err = oidc.ErrInvalidGrant().WithParent(err)
@@ -285,16 +276,20 @@ func (o *OPStorage) CreateAccessAndRefreshTokens(ctx context.Context, req op.Tok
return resp.TokenID, token, resp.Expiration, nil
}
func getInfoFromRequest(req op.TokenRequest) (string, string, string, time.Time, []string) {
func getInfoFromRequest(req op.TokenRequest) (agentID string, clientID string, userOrgID string, authTime time.Time, amr []string, reason domain.TokenReason, actor *domain.TokenActor) {
switch r := req.(type) {
case *AuthRequest:
return r.AgentID, r.ApplicationID, r.UserOrgID, r.AuthTime, r.GetAMR()
return r.AgentID, r.ApplicationID, r.UserOrgID, r.AuthTime, r.GetAMR(), domain.TokenReasonAuthRequest, nil
case *RefreshTokenRequest:
return r.UserAgentID, r.ClientID, "", r.AuthTime, r.AuthMethodsReferences
return r.UserAgentID, r.ClientID, "", r.AuthTime, r.AuthMethodsReferences, domain.TokenReasonRefresh, r.Actor
case op.IDTokenRequest:
return "", r.GetClientID(), "", r.GetAuthTime(), r.GetAMR()
return "", r.GetClientID(), "", r.GetAuthTime(), r.GetAMR(), domain.TokenReasonAuthRequest, nil
case *oidc.JWTTokenRequest:
return "", "", "", r.GetAuthTime(), nil, domain.TokenReasonJWTProfile, nil
case *clientCredentialsRequest:
return "", "", "", time.Time{}, nil, domain.TokenReasonClientCredentials, nil
default:
return "", "", "", time.Time{}, nil
return "", "", "", time.Time{}, nil, domain.TokenReasonAuthRequest, nil
}
}