diff --git a/cmd/setup/26.go b/cmd/setup/26.go new file mode 100644 index 00000000000..b3bcf4a0c28 --- /dev/null +++ b/cmd/setup/26.go @@ -0,0 +1,27 @@ +package setup + +import ( + "context" + _ "embed" + + "github.com/zitadel/zitadel/internal/database" + "github.com/zitadel/zitadel/internal/eventstore" +) + +var ( + //go:embed 26.sql + addTokenProjectID string +) + +type AddProjectIDToAuthTokens struct { + dbClient *database.DB +} + +func (mig *AddProjectIDToAuthTokens) Execute(ctx context.Context, _ eventstore.Event) error { + _, err := mig.dbClient.ExecContext(ctx, addTokenProjectID) + return err +} + +func (mig *AddProjectIDToAuthTokens) String() string { + return "26_add_project_id_col_to_auth_tokens" +} diff --git a/cmd/setup/26.sql b/cmd/setup/26.sql new file mode 100644 index 00000000000..76755ad5d33 --- /dev/null +++ b/cmd/setup/26.sql @@ -0,0 +1 @@ +ALTER TABLE auth.tokens ADD COLUMN project_id varchar null; diff --git a/cmd/setup/config.go b/cmd/setup/config.go index 8c44f0b21ad..ebb84ca27b2 100644 --- a/cmd/setup/config.go +++ b/cmd/setup/config.go @@ -104,6 +104,7 @@ type Steps struct { s23CorrectGlobalUniqueConstraints *CorrectGlobalUniqueConstraints s24AddActorToAuthTokens *AddActorToAuthTokens s25User11AddLowerFieldsToVerifiedEmail *User11AddLowerFieldsToVerifiedEmail + s26AddProjectIDToAuthTokens *AddProjectIDToAuthTokens } func MustNewSteps(v *viper.Viper) *Steps { diff --git a/cmd/setup/setup.go b/cmd/setup/setup.go index 693ec81e322..d21a28c8fe4 100644 --- a/cmd/setup/setup.go +++ b/cmd/setup/setup.go @@ -138,6 +138,7 @@ func Setup(config *Config, steps *Steps, masterKey string) { steps.s23CorrectGlobalUniqueConstraints = &CorrectGlobalUniqueConstraints{dbClient: esPusherDBClient} steps.s24AddActorToAuthTokens = &AddActorToAuthTokens{dbClient: queryDBClient} steps.s25User11AddLowerFieldsToVerifiedEmail = &User11AddLowerFieldsToVerifiedEmail{dbClient: esPusherDBClient} + steps.s26AddProjectIDToAuthTokens = &AddProjectIDToAuthTokens{dbClient: queryDBClient} err = projection.Create(ctx, projectionDBClient, eventstoreClient, config.Projections, nil, nil, nil) logging.OnError(err).Fatal("unable to start projections") @@ -175,6 +176,7 @@ func Setup(config *Config, steps *Steps, masterKey string) { steps.s22ActiveInstancesIndex, steps.s23CorrectGlobalUniqueConstraints, steps.s24AddActorToAuthTokens, + steps.s26AddProjectIDToAuthTokens, } { mustExecuteMigration(ctx, eventstoreClient, step, "migration failed") } diff --git a/internal/api/oidc/access_token.go b/internal/api/oidc/access_token.go index b4aca5f9297..48110911b12 100644 --- a/internal/api/oidc/access_token.go +++ b/internal/api/oidc/access_token.go @@ -23,6 +23,7 @@ type accessToken struct { resourceOwner string subject string clientID string + projectID string audience []string scope []string authMethods []domain.UserAuthMethodType @@ -75,6 +76,7 @@ func accessTokenV1(tokenID, subject string, token *model.TokenView) *accessToken resourceOwner: token.ResourceOwner, subject: subject, clientID: token.ApplicationID, + projectID: token.ProjectID, audience: token.Audience, scope: token.Scopes, tokenCreation: token.CreationDate, @@ -91,6 +93,7 @@ func accessTokenV2(tokenID, subject string, token *query.OIDCSessionAccessTokenR resourceOwner: token.ResourceOwner, subject: subject, clientID: token.ClientID, + projectID: token.ProjectID, audience: token.Audience, scope: token.Scope, authMethods: token.AuthMethods, diff --git a/internal/api/oidc/auth_request.go b/internal/api/oidc/auth_request.go index 4f6dcdb5d1a..400d0d5b335 100644 --- a/internal/api/oidc/auth_request.go +++ b/internal/api/oidc/auth_request.go @@ -218,7 +218,7 @@ func (o *OPStorage) CreateAccessToken(ctx context.Context, req op.TokenRequest) }() 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()) + return o.command.AddOIDCSessionAccessToken(setContextUserSystem(ctx), authReq.GetID(), "") } userAgentID, applicationID, userOrgID, authTime, amr, reason, actor := getInfoFromRequest(req) @@ -227,7 +227,7 @@ func (o *OPStorage) CreateAccessToken(ctx context.Context, req op.TokenRequest) return "", time.Time{}, err } - resp, err := o.command.AddUserToken(setContextUserSystem(ctx), userOrgID, userAgentID, applicationID, req.GetSubject(), req.GetAudience(), req.GetScopes(), amr, accessTokenLifetime, authTime, reason, actor) + 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 } @@ -249,7 +249,7 @@ func (o *OPStorage) CreateAccessAndRefreshTokens(ctx context.Context, req op.Tok case *AuthRequestV2: // trigger activity log for authentication for user activity.Trigger(ctx, "", tokenReq.GetSubject(), activity.OIDCRefreshToken, o.eventstore.FilterToQueryReducer) - return o.command.AddOIDCSessionRefreshAndAccessToken(setContextUserSystem(ctx), tokenReq.GetID()) + return o.command.AddOIDCSessionRefreshAndAccessToken(setContextUserSystem(ctx), tokenReq.GetID(), "") case *RefreshTokenRequestV2: // trigger activity log for authentication for user activity.Trigger(ctx, "", tokenReq.GetSubject(), activity.OIDCRefreshToken, o.eventstore.FilterToQueryReducer) @@ -270,7 +270,7 @@ func (o *OPStorage) CreateAccessAndRefreshTokens(ctx context.Context, req op.Tok return "", "", time.Time{}, err } - resp, token, err := o.command.AddAccessAndRefreshToken(setContextUserSystem(ctx), userOrgID, userAgentID, applicationID, req.GetSubject(), + resp, token, err := o.command.AddAccessAndRefreshToken(setContextUserSystem(ctx), userOrgID, userAgentID, applicationID, "", req.GetSubject(), refreshToken, req.GetAudience(), scopes, authMethodsReferences, accessTokenLifetime, refreshTokenIdleExpiration, refreshTokenExpiration, authTime, reason, actor) //PLANNED: lifetime from client if err != nil { diff --git a/internal/api/oidc/token_exchange.go b/internal/api/oidc/token_exchange.go index 77c9b46fa27..ce45b121e83 100644 --- a/internal/api/oidc/token_exchange.go +++ b/internal/api/oidc/token_exchange.go @@ -249,7 +249,7 @@ func (s *Server) createExchangeTokens(ctx context.Context, tokenType oidc.TokenT resp.IssuedTokenType = oidc.JWTTokenType case oidc.IDTokenType: - resp.AccessToken, resp.ExpiresIn, err = s.createExchangeIDToken(ctx, signingKey, client, subjectToken.userID, "", audience, userInfo, actorToken.authMethods, actorToken.authTime, reason, actor) + resp.AccessToken, resp.ExpiresIn, err = s.createExchangeIDToken(ctx, signingKey, client, subjectToken.userID, "", audience, userInfo, actorToken.authMethods, actorToken.authTime, actor) resp.TokenType = TokenTypeNA resp.IssuedTokenType = oidc.IDTokenType case oidc.RefreshTokenType, UserIDTokenType: @@ -262,7 +262,7 @@ func (s *Server) createExchangeTokens(ctx context.Context, tokenType oidc.TokenT } if slices.Contains(scopes, oidc.ScopeOpenID) && tokenType != oidc.IDTokenType { - resp.IDToken, _, err = s.createExchangeIDToken(ctx, signingKey, client, subjectToken.userID, resp.AccessToken, audience, userInfo, actorToken.authMethods, actorToken.authTime, reason, actor) + resp.IDToken, _, err = s.createExchangeIDToken(ctx, signingKey, client, subjectToken.userID, resp.AccessToken, audience, userInfo, actorToken.authMethods, actorToken.authTime, actor) if err != nil { return nil, err } @@ -306,7 +306,7 @@ func (s *Server) createExchangeJWT(ctx context.Context, signingKey op.SigningKey return accessToken, refreshToken, timeToOIDCExpiresIn(expTime), nil } -func (s *Server) createExchangeIDToken(ctx context.Context, signingKey op.SigningKey, client *Client, userID, accessToken string, audience []string, userInfo *oidc.UserInfo, authMethods []domain.UserAuthMethodType, authTime time.Time, reason domain.TokenReason, actor *domain.TokenActor) (idToken string, exp uint64, err error) { +func (s *Server) createExchangeIDToken(ctx context.Context, signingKey op.SigningKey, client *Client, userID, accessToken string, audience []string, userInfo *oidc.UserInfo, authMethods []domain.UserAuthMethodType, authTime time.Time, actor *domain.TokenActor) (idToken string, exp uint64, err error) { expTime := time.Now().Add(client.IDTokenLifetime()).Add(client.ClockSkew()) claims := oidc.NewIDTokenClaims(op.IssuerFromContext(ctx), userID, audience, expTime, authTime, "", "", AuthMethodTypesToAMR(authMethods), client.GetID(), client.ClockSkew()) claims.Actor = actorDomainToClaims(actor) @@ -333,15 +333,14 @@ func (s *Server) createAccessTokenCommands(ctx context.Context, client *Client, settings := client.client.Settings if slices.Contains(scopes, oidc.ScopeOfflineAccess) { return s.command.AddAccessAndRefreshToken( - ctx, resourceOwner, "", client.GetID(), userID, "", audience, scopes, AuthMethodTypesToAMR(authMethods), - settings.AccessTokenLifetime, settings.RefreshTokenIdleExpiration, settings.RefreshTokenExpiration, - authTime, reason, actor, + ctx, resourceOwner, "", client.GetID(), client.client.ProjectID, userID, "", audience, scopes, + AuthMethodTypesToAMR(authMethods), settings.AccessTokenLifetime, settings.RefreshTokenIdleExpiration, + settings.RefreshTokenExpiration, authTime, reason, actor, ) } tokenInfo, err = s.command.AddUserToken( - ctx, resourceOwner, "", client.GetID(), userID, audience, scopes, AuthMethodTypesToAMR(authMethods), - settings.AccessTokenLifetime, - authTime, reason, actor, + ctx, resourceOwner, "", client.GetID(), client.client.ProjectID, userID, audience, scopes, + AuthMethodTypesToAMR(authMethods), settings.AccessTokenLifetime, authTime, reason, actor, ) return tokenInfo, "", err } diff --git a/internal/api/oidc/userinfo.go b/internal/api/oidc/userinfo.go index fdba9b7e4ed..25d920794a5 100644 --- a/internal/api/oidc/userinfo.go +++ b/internal/api/oidc/userinfo.go @@ -41,7 +41,7 @@ func (s *Server) UserInfo(ctx context.Context, r *op.Request[oidc.UserInfoReques if err != nil { return nil, op.NewStatusError(oidc.ErrAccessDenied().WithDescription("access token invalid").WithParent(err), http.StatusUnauthorized) } - userInfo, err := s.userInfo(ctx, token.userID, "", token.scope, token.audience) + userInfo, err := s.userInfo(ctx, token.userID, token.projectID, token.scope, token.audience) if err != nil { return nil, err } diff --git a/internal/command/oidc_session.go b/internal/command/oidc_session.go index 90d852d8727..f291e23c06e 100644 --- a/internal/command/oidc_session.go +++ b/internal/command/oidc_session.go @@ -30,13 +30,13 @@ const ( // AddOIDCSessionAccessToken creates a new OIDC Session, creates an access token and returns its id and expiration. // If the underlying [AuthRequest] is a OIDC Auth Code Flow, it will set the code as exchanged. -func (c *Commands) AddOIDCSessionAccessToken(ctx context.Context, authRequestID string) (string, time.Time, error) { +func (c *Commands) AddOIDCSessionAccessToken(ctx context.Context, authRequestID, projectID string) (string, time.Time, error) { cmd, err := c.newOIDCSessionAddEvents(ctx, authRequestID) if err != nil { return "", time.Time{}, err } - cmd.AddSession(ctx) - if err = cmd.AddAccessToken(ctx, cmd.authRequestWriteModel.Scope, domain.TokenReasonAuthRequest, nil); err != nil { + cmd.AddSession(ctx, projectID) + if err = cmd.AddAccessToken(ctx, projectID, cmd.authRequestWriteModel.Scope, domain.TokenReasonAuthRequest, nil); err != nil { return "", time.Time{}, err } cmd.SetAuthRequestSuccessful(ctx) @@ -47,13 +47,13 @@ func (c *Commands) AddOIDCSessionAccessToken(ctx context.Context, authRequestID // AddOIDCSessionRefreshAndAccessToken creates a new OIDC Session, creates an access token and refresh token. // It returns the access token id, expiration and the refresh token. // If the underlying [AuthRequest] is a OIDC Auth Code Flow, it will set the code as exchanged. -func (c *Commands) AddOIDCSessionRefreshAndAccessToken(ctx context.Context, authRequestID string) (tokenID, refreshToken string, tokenExpiration time.Time, err error) { +func (c *Commands) AddOIDCSessionRefreshAndAccessToken(ctx context.Context, authRequestID, projectID string) (tokenID, refreshToken string, tokenExpiration time.Time, err error) { cmd, err := c.newOIDCSessionAddEvents(ctx, authRequestID) if err != nil { return "", "", time.Time{}, err } - cmd.AddSession(ctx) - if err = cmd.AddAccessToken(ctx, cmd.authRequestWriteModel.Scope, domain.TokenReasonAuthRequest, nil); err != nil { + cmd.AddSession(ctx, projectID) + if err = cmd.AddAccessToken(ctx, projectID, cmd.authRequestWriteModel.Scope, domain.TokenReasonAuthRequest, nil); err != nil { return "", "", time.Time{}, err } if err = cmd.AddRefreshToken(ctx); err != nil { @@ -70,7 +70,7 @@ func (c *Commands) ExchangeOIDCSessionRefreshAndAccessToken(ctx context.Context, if err != nil { return "", "", time.Time{}, err } - if err = cmd.AddAccessToken(ctx, scope, domain.TokenReasonRefresh, nil); err != nil { + if err = cmd.AddAccessToken(ctx, cmd.oidcSessionWriteModel.ProjectID, scope, domain.TokenReasonRefresh, nil); err != nil { return "", "", time.Time{}, err } if err = cmd.RenewRefreshToken(ctx); err != nil { @@ -273,7 +273,7 @@ type OIDCSessionEvents struct { refreshToken string } -func (c *OIDCSessionEvents) AddSession(ctx context.Context) { +func (c *OIDCSessionEvents) AddSession(ctx context.Context, projectID string) { c.events = append(c.events, oidcsession.NewAddedEvent( ctx, c.oidcSessionWriteModel.aggregate, @@ -291,13 +291,13 @@ func (c *OIDCSessionEvents) SetAuthRequestSuccessful(ctx context.Context) { c.events = append(c.events, authrequest.NewSucceededEvent(ctx, c.authRequestWriteModel.aggregate)) } -func (c *OIDCSessionEvents) AddAccessToken(ctx context.Context, scope []string, reason domain.TokenReason, actor *domain.TokenActor) error { +func (c *OIDCSessionEvents) AddAccessToken(ctx context.Context, projectID string, scope []string, reason domain.TokenReason, actor *domain.TokenActor) error { accessTokenID, err := c.idGenerator.Next() if err != nil { return err } c.accessTokenID = AccessTokenPrefix + accessTokenID - c.events = append(c.events, oidcsession.NewAccessTokenAddedEvent(ctx, c.oidcSessionWriteModel.aggregate, c.accessTokenID, scope, c.accessTokenLifetime, reason, actor)) + c.events = append(c.events, oidcsession.NewAccessTokenAddedEvent(ctx, c.oidcSessionWriteModel.aggregate, c.accessTokenID, projectID, scope, c.accessTokenLifetime, reason, actor)) return nil } diff --git a/internal/command/oidc_session_model.go b/internal/command/oidc_session_model.go index a561fc4165f..01bb712f3fc 100644 --- a/internal/command/oidc_session_model.go +++ b/internal/command/oidc_session_model.go @@ -15,6 +15,7 @@ type OIDCSessionWriteModel struct { UserID string SessionID string ClientID string + ProjectID string Audience []string Scope []string AuthMethods []domain.UserAuthMethodType @@ -101,6 +102,7 @@ func (wm *OIDCSessionWriteModel) reduceAdded(e *oidcsession.AddedEvent) { func (wm *OIDCSessionWriteModel) reduceAccessTokenAdded(e *oidcsession.AccessTokenAddedEvent) { wm.AccessTokenID = e.ID + wm.ProjectID = e.ProjectID wm.AccessTokenExpiration = e.CreationDate().Add(e.Lifetime) wm.AccessTokenReason = e.Reason wm.AccessTokenActor = e.Actor diff --git a/internal/command/oidc_session_test.go b/internal/command/oidc_session_test.go index 6dcd96c6236..298b051bb7d 100644 --- a/internal/command/oidc_session_test.go +++ b/internal/command/oidc_session_test.go @@ -196,7 +196,7 @@ func TestCommands_AddOIDCSessionAccessToken(t *testing.T) { oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, "userID", "sessionID", "clientID", []string{"audience"}, []string{"openid"}, []domain.UserAuthMethodType{domain.UserAuthMethodTypePassword}, testNow), oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, - "at_accessTokenID", []string{"openid"}, time.Hour, domain.TokenReasonAuthRequest, nil), + "at_accessTokenID", "projectID", []string{"openid"}, time.Hour, domain.TokenReasonAuthRequest, nil), authrequest.NewSucceededEvent(context.Background(), &authrequest.NewAggregate("V2_authRequestID", "instanceID").Aggregate), ), ), @@ -223,7 +223,7 @@ func TestCommands_AddOIDCSessionAccessToken(t *testing.T) { defaultRefreshTokenIdleLifetime: tt.fields.defaultRefreshTokenIdleLifetime, keyAlgorithm: tt.fields.keyAlgorithm, } - gotID, gotExpiration, err := c.AddOIDCSessionAccessToken(tt.args.ctx, tt.args.authRequestID) + gotID, gotExpiration, err := c.AddOIDCSessionAccessToken(tt.args.ctx, tt.args.authRequestID, "projectID") assert.Equal(t, tt.res.id, gotID) assert.Equal(t, tt.res.expiration, gotExpiration) assert.ErrorIs(t, err, tt.res.err) @@ -397,7 +397,7 @@ func TestCommands_AddOIDCSessionRefreshAndAccessToken(t *testing.T) { oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, "userID", "sessionID", "clientID", []string{"audience"}, []string{"openid", "offline_access"}, []domain.UserAuthMethodType{domain.UserAuthMethodTypePassword}, testNow), oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, - "at_accessTokenID", []string{"openid", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil), + "at_accessTokenID", "projectID", []string{"openid", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil), oidcsession.NewRefreshTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, "rt_refreshTokenID", 7*24*time.Hour, 24*time.Hour), authrequest.NewSucceededEvent(context.Background(), &authrequest.NewAggregate("V2_authRequestID", "instanceID").Aggregate), @@ -430,7 +430,7 @@ func TestCommands_AddOIDCSessionRefreshAndAccessToken(t *testing.T) { defaultRefreshTokenIdleLifetime: tt.fields.defaultRefreshTokenIdleLifetime, keyAlgorithm: tt.fields.keyAlgorithm, } - gotID, gotRefreshToken, gotExpiration, err := c.AddOIDCSessionRefreshAndAccessToken(tt.args.ctx, tt.args.authRequestID) + gotID, gotRefreshToken, gotExpiration, err := c.AddOIDCSessionRefreshAndAccessToken(tt.args.ctx, tt.args.authRequestID, "projectID") assert.Equal(t, tt.res.id, gotID) assert.Equal(t, tt.res.refreshToken, gotRefreshToken) assert.Equal(t, tt.res.expiration, gotExpiration) @@ -509,7 +509,7 @@ func TestCommands_ExchangeOIDCSessionRefreshAndAccessToken(t *testing.T) { ), eventFromEventPusher( oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, - "accessTokenID", []string{"openid", "profile", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil), + "accessTokenID", "projectID", []string{"openid", "profile", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil), ), ), ), @@ -535,7 +535,7 @@ func TestCommands_ExchangeOIDCSessionRefreshAndAccessToken(t *testing.T) { ), eventFromEventPusher( oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, - "at_accessTokenID", []string{"openid", "profile", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil), + "at_accessTokenID", "projectID", []string{"openid", "profile", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil), ), eventFromEventPusher( oidcsession.NewRefreshTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, @@ -565,7 +565,7 @@ func TestCommands_ExchangeOIDCSessionRefreshAndAccessToken(t *testing.T) { ), eventFromEventPusherWithCreationDateNow( oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, - "at_accessTokenID", []string{"openid", "profile", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil), + "at_accessTokenID", "projectID", []string{"openid", "profile", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil), ), eventFromEventPusherWithCreationDateNow( oidcsession.NewRefreshTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, @@ -575,7 +575,7 @@ func TestCommands_ExchangeOIDCSessionRefreshAndAccessToken(t *testing.T) { expectFilter(), // token lifetime expectPush( oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, - "at_accessTokenID", []string{"openid", "offline_access"}, time.Hour, domain.TokenReasonRefresh, nil), + "at_accessTokenID", "projectID", []string{"openid", "offline_access"}, time.Hour, domain.TokenReasonRefresh, nil), oidcsession.NewRefreshTokenRenewedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, "rt_refreshTokenID2", 24*time.Hour), ), @@ -682,7 +682,7 @@ func TestCommands_OIDCSessionByRefreshToken(t *testing.T) { ), eventFromEventPusher( oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, - "at_accessTokenID", []string{"openid", "profile", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil), + "at_accessTokenID", "projectID", []string{"openid", "profile", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil), ), ), ), @@ -707,7 +707,7 @@ func TestCommands_OIDCSessionByRefreshToken(t *testing.T) { ), eventFromEventPusher( oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, - "at_accessTokenID", []string{"openid", "profile", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil), + "at_accessTokenID", "projectID", []string{"openid", "profile", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil), ), eventFromEventPusher( oidcsession.NewRefreshTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, @@ -736,7 +736,7 @@ func TestCommands_OIDCSessionByRefreshToken(t *testing.T) { ), eventFromEventPusherWithCreationDateNow( oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, - "at_accessTokenID", []string{"openid", "profile", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil), + "at_accessTokenID", "projectID", []string{"openid", "profile", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil), ), eventFromEventPusherWithCreationDateNow( oidcsession.NewRefreshTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, @@ -892,7 +892,7 @@ func TestCommands_RevokeOIDCSessionToken(t *testing.T) { ), eventFromEventPusherWithCreationDateNow( oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, - "at_accessTokenID", []string{"openid", "profile", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil), + "at_accessTokenID", "projectID", []string{"openid", "profile", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil), ), eventFromEventPusherWithCreationDateNow( oidcsession.NewRefreshTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, @@ -969,7 +969,7 @@ func TestCommands_RevokeOIDCSessionToken(t *testing.T) { ), eventFromEventPusherWithCreationDateNow( oidcsession.NewAccessTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, - "at_accessTokenID", []string{"openid", "profile", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil), + "at_accessTokenID", "projectID", []string{"openid", "profile", "offline_access"}, time.Hour, domain.TokenReasonAuthRequest, nil), ), eventFromEventPusherWithCreationDateNow( oidcsession.NewRefreshTokenAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate, diff --git a/internal/command/user.go b/internal/command/user.go index 6d22b212e0f..398ab5d9baf 100644 --- a/internal/command/user.go +++ b/internal/command/user.go @@ -237,6 +237,7 @@ func (c *Commands) AddUserToken( orgID, agentID, clientID, + projectID, userID string, audience, scopes, @@ -250,7 +251,7 @@ func (c *Commands) AddUserToken( return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-Dbge4", "Errors.IDMissing") } userWriteModel := NewUserWriteModel(userID, orgID) - cmds, accessToken, err := c.addUserToken(ctx, userWriteModel, agentID, clientID, "", audience, scopes, authMethodsReferences, lifetime, authTime, reason, actor) + cmds, accessToken, err := c.addUserToken(ctx, userWriteModel, agentID, clientID, projectID, "", audience, scopes, authMethodsReferences, lifetime, authTime, reason, actor) if err != nil { return nil, err } @@ -277,7 +278,7 @@ func (c *Commands) RevokeAccessToken(ctx context.Context, userID, orgID, tokenID return writeModelToObjectDetails(&accessTokenWriteModel.WriteModel), nil } -func (c *Commands) addUserToken(ctx context.Context, userWriteModel *UserWriteModel, agentID, clientID, refreshTokenID string, audience, scopes, authMethodsReferences []string, lifetime time.Duration, authTime time.Time, reason domain.TokenReason, actor *domain.TokenActor) ([]eventstore.Command, *domain.Token, error) { +func (c *Commands) addUserToken(ctx context.Context, userWriteModel *UserWriteModel, agentID, clientID, projectID, refreshTokenID string, audience, scopes, authMethodsReferences []string, lifetime time.Duration, authTime time.Time, reason domain.TokenReason, actor *domain.TokenActor) ([]eventstore.Command, *domain.Token, error) { err := c.eventstore.FilterToQueryReducer(ctx, userWriteModel) if err != nil { return nil, nil, err @@ -312,7 +313,7 @@ func (c *Commands) addUserToken(ctx context.Context, userWriteModel *UserWriteMo } cmds = append(cmds, - user.NewUserTokenAddedEvent(ctx, userAgg, tokenID, clientID, agentID, preferredLanguage, refreshTokenID, audience, scopes, authMethodsReferences, authTime, expiration, reason, actor), + user.NewUserTokenAddedEvent(ctx, userAgg, tokenID, clientID, projectID, agentID, preferredLanguage, refreshTokenID, audience, scopes, authMethodsReferences, authTime, expiration, reason, actor), ) return cmds, &domain.Token{ diff --git a/internal/command/user_human_access_token_model.go b/internal/command/user_human_access_token_model.go index 9a4c6cd2c80..fdfa67edbf4 100644 --- a/internal/command/user_human_access_token_model.go +++ b/internal/command/user_human_access_token_model.go @@ -13,6 +13,7 @@ type UserAccessTokenWriteModel struct { TokenID string ApplicationID string + ProjectID string UserAgentID string Audience []string Scopes []string @@ -66,6 +67,7 @@ func (wm *UserAccessTokenWriteModel) Reduce() error { case *user.UserTokenAddedEvent: wm.TokenID = e.TokenID wm.ApplicationID = e.ApplicationID + wm.ProjectID = e.ProjectID wm.UserAgentID = e.UserAgentID wm.Audience = e.Audience wm.Scopes = e.Scopes diff --git a/internal/command/user_human_refresh_token.go b/internal/command/user_human_refresh_token.go index 61ec2da7bdf..fe52b3f5373 100644 --- a/internal/command/user_human_refresh_token.go +++ b/internal/command/user_human_refresh_token.go @@ -15,6 +15,7 @@ func (c *Commands) AddAccessAndRefreshToken( orgID, agentID, clientID, + projectID, userID, refreshToken string, audience, @@ -28,9 +29,9 @@ func (c *Commands) AddAccessAndRefreshToken( actor *domain.TokenActor, ) (accessToken *domain.Token, newRefreshToken string, err error) { if refreshToken == "" { - return c.AddNewRefreshTokenAndAccessToken(ctx, userID, orgID, agentID, clientID, audience, scopes, authMethodsReferences, refreshExpiration, accessLifetime, refreshIdleExpiration, authTime, reason, actor) + return c.AddNewRefreshTokenAndAccessToken(ctx, userID, orgID, agentID, clientID, projectID, audience, scopes, authMethodsReferences, refreshExpiration, accessLifetime, refreshIdleExpiration, authTime, reason, actor) } - return c.RenewRefreshTokenAndAccessToken(ctx, userID, orgID, refreshToken, agentID, clientID, audience, scopes, refreshIdleExpiration, accessLifetime, actor) + return c.RenewRefreshTokenAndAccessToken(ctx, userID, orgID, refreshToken, agentID, clientID, projectID, audience, scopes, refreshIdleExpiration, accessLifetime, actor) } func (c *Commands) AddNewRefreshTokenAndAccessToken( @@ -38,7 +39,8 @@ func (c *Commands) AddNewRefreshTokenAndAccessToken( userID, orgID, agentID, - clientID string, + clientID, + projectID string, audience, scopes, authMethodsReferences []string, @@ -57,7 +59,7 @@ func (c *Commands) AddNewRefreshTokenAndAccessToken( if err != nil { return nil, "", err } - cmds, accessToken, err := c.addUserToken(ctx, userWriteModel, agentID, clientID, refreshTokenID, audience, scopes, authMethodsReferences, accessLifetime, authTime, reason, actor) + cmds, accessToken, err := c.addUserToken(ctx, userWriteModel, agentID, clientID, projectID, refreshTokenID, audience, scopes, authMethodsReferences, accessLifetime, authTime, reason, actor) if err != nil { return nil, "", err } @@ -79,7 +81,8 @@ func (c *Commands) RenewRefreshTokenAndAccessToken( orgID, refreshToken, agentID, - clientID string, + clientID, + projectID string, audience, scopes []string, idleExpiration, @@ -91,7 +94,7 @@ func (c *Commands) RenewRefreshTokenAndAccessToken( return nil, "", err } userWriteModel := NewUserWriteModel(userID, orgID) - cmds, accessToken, err := c.addUserToken(ctx, userWriteModel, agentID, clientID, renewed.tokenID, audience, scopes, renewed.authMethodsReferences, accessLifetime, renewed.authTime, domain.TokenReasonRefresh, actor) + cmds, accessToken, err := c.addUserToken(ctx, userWriteModel, agentID, clientID, projectID, renewed.tokenID, audience, scopes, renewed.authMethodsReferences, accessLifetime, renewed.authTime, domain.TokenReasonRefresh, actor) if err != nil { return nil, "", err } diff --git a/internal/command/user_human_refresh_token_test.go b/internal/command/user_human_refresh_token_test.go index ca9279ac1a0..51b8b1ade48 100644 --- a/internal/command/user_human_refresh_token_test.go +++ b/internal/command/user_human_refresh_token_test.go @@ -296,7 +296,7 @@ func TestCommands_AddAccessAndRefreshToken(t *testing.T) { idGenerator: tt.fields.idGenerator, keyAlgorithm: tt.fields.keyAlgorithm, } - got, gotRefresh, err := c.AddAccessAndRefreshToken(tt.args.ctx, tt.args.orgID, tt.args.agentID, tt.args.clientID, tt.args.userID, tt.args.refreshToken, + got, gotRefresh, err := c.AddAccessAndRefreshToken(tt.args.ctx, tt.args.orgID, tt.args.agentID, tt.args.clientID, "projectID", tt.args.userID, tt.args.refreshToken, tt.args.audience, tt.args.scopes, tt.args.authMethodsReferences, tt.args.lifetime, tt.args.refreshIdleExpiration, tt.args.refreshExpiration, tt.args.authTime, tt.args.reason, tt.args.actor) if tt.res.err == nil { assert.NoError(t, err) diff --git a/internal/command/user_test.go b/internal/command/user_test.go index 6538e006600..7b695e392ca 100644 --- a/internal/command/user_test.go +++ b/internal/command/user_test.go @@ -1504,7 +1504,7 @@ func TestCommandSide_AddUserToken(t *testing.T) { eventstore: tt.fields.eventstore, idGenerator: tt.fields.idGenerator, } - got, err := r.AddUserToken(tt.args.ctx, tt.args.orgID, tt.args.agentID, tt.args.clientID, tt.args.userID, tt.args.audience, tt.args.scopes, tt.args.authMethodsReferences, tt.args.lifetime, tt.args.authTime, tt.args.reason, tt.args.actor) + got, err := r.AddUserToken(tt.args.ctx, tt.args.orgID, tt.args.agentID, tt.args.clientID, "projectID", tt.args.userID, tt.args.audience, tt.args.scopes, tt.args.authMethodsReferences, tt.args.lifetime, tt.args.authTime, tt.args.reason, tt.args.actor) if tt.res.err == nil { assert.NoError(t, err) } @@ -1564,6 +1564,7 @@ func TestCommands_RevokeAccessToken(t *testing.T) { &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", "clientID", + "projectID", "agentID", "de", "refreshTokenID", @@ -1600,6 +1601,7 @@ func TestCommands_RevokeAccessToken(t *testing.T) { &user.NewAggregate("userID", "orgID").Aggregate, "tokenID", "clientID", + "projectID", "agentID", "de", "refreshTokenID", diff --git a/internal/query/access_token.go b/internal/query/access_token.go index 2b927cf7b5e..6bc0940ebd9 100644 --- a/internal/query/access_token.go +++ b/internal/query/access_token.go @@ -20,6 +20,7 @@ type OIDCSessionAccessTokenReadModel struct { UserID string SessionID string ClientID string + ProjectID string Audience []string Scope []string AuthMethods []domain.UserAuthMethodType @@ -84,6 +85,7 @@ func (wm *OIDCSessionAccessTokenReadModel) reduceAdded(e *oidcsession.AddedEvent func (wm *OIDCSessionAccessTokenReadModel) reduceAccessTokenAdded(e *oidcsession.AccessTokenAddedEvent) { wm.AccessTokenID = e.ID + wm.ProjectID = e.ProjectID wm.AccessTokenCreation = e.CreationDate() wm.AccessTokenExpiration = e.CreationDate().Add(e.Lifetime) wm.Reason = e.Reason diff --git a/internal/repository/authrequest/auth_request.go b/internal/repository/authrequest/auth_request.go index 5e9f1ad390d..0f057a65090 100644 --- a/internal/repository/authrequest/auth_request.go +++ b/internal/repository/authrequest/auth_request.go @@ -24,6 +24,7 @@ type AddedEvent struct { LoginClient string `json:"login_client"` ClientID string `json:"client_id"` + ProjectID string `json:"project_id"` RedirectURI string `json:"redirect_uri"` State string `json:"state,omitempty"` Nonce string `json:"nonce,omitempty"` diff --git a/internal/repository/oidcsession/oidc_session.go b/internal/repository/oidcsession/oidc_session.go index ff127b36967..92e73ba6a03 100644 --- a/internal/repository/oidcsession/oidc_session.go +++ b/internal/repository/oidcsession/oidc_session.go @@ -22,7 +22,7 @@ type AddedEvent struct { eventstore.BaseEvent `json:"-"` UserID string `json:"userID"` - SessionID string `json:"sessionID"` + SessionID string `json:"sessionID,omitempty"` ClientID string `json:"clientID"` Audience []string `json:"audience"` Scope []string `json:"scope"` @@ -71,11 +71,12 @@ func NewAddedEvent(ctx context.Context, type AccessTokenAddedEvent struct { eventstore.BaseEvent `json:"-"` - ID string `json:"id,omitempty"` - Scope []string `json:"scope,omitempty"` - Lifetime time.Duration `json:"lifetime,omitempty"` - Reason domain.TokenReason `json:"reason,omitempty"` - Actor *domain.TokenActor `json:"actor,omitempty"` + ID string `json:"id,omitempty"` + ProjectID string `json:"project_id,omitempty"` + Scope []string `json:"scope,omitempty"` + Lifetime time.Duration `json:"lifetime,omitempty"` + Reason domain.TokenReason `json:"reason,omitempty"` + Actor *domain.TokenActor `json:"actor,omitempty"` } func (e *AccessTokenAddedEvent) Payload() interface{} { @@ -93,7 +94,8 @@ func (e *AccessTokenAddedEvent) SetBaseEvent(event *eventstore.BaseEvent) { func NewAccessTokenAddedEvent( ctx context.Context, aggregate *eventstore.Aggregate, - id string, + id, + projectID string, scope []string, lifetime time.Duration, reason domain.TokenReason, @@ -105,11 +107,12 @@ func NewAccessTokenAddedEvent( aggregate, AccessTokenAddedType, ), - ID: id, - Scope: scope, - Lifetime: lifetime, - Reason: reason, - Actor: actor, + ID: id, + ProjectID: projectID, + Scope: scope, + Lifetime: lifetime, + Reason: reason, + Actor: actor, } } diff --git a/internal/repository/user/user.go b/internal/repository/user/user.go index bb64609cd89..dfea54ff5d5 100644 --- a/internal/repository/user/user.go +++ b/internal/repository/user/user.go @@ -212,6 +212,7 @@ type UserTokenAddedEvent struct { TokenID string `json:"tokenId,omitempty"` ApplicationID string `json:"applicationId,omitempty"` + ProjectID string `json:"projectID,omitempty"` UserAgentID string `json:"userAgentId,omitempty"` RefreshTokenID string `json:"refreshTokenID,omitempty"` Audience []string `json:"audience,omitempty"` @@ -237,6 +238,7 @@ func NewUserTokenAddedEvent( aggregate *eventstore.Aggregate, tokenID, applicationID, + projectID, userAgentID, preferredLanguage, refreshTokenID string, @@ -256,6 +258,7 @@ func NewUserTokenAddedEvent( ), TokenID: tokenID, ApplicationID: applicationID, + ProjectID: projectID, UserAgentID: userAgentID, RefreshTokenID: refreshTokenID, Audience: audience, diff --git a/internal/user/model/token_view.go b/internal/user/model/token_view.go index 7e182e31bcb..530742203ca 100644 --- a/internal/user/model/token_view.go +++ b/internal/user/model/token_view.go @@ -14,6 +14,7 @@ type TokenView struct { ResourceOwner string UserID string ApplicationID string + ProjectID string UserAgentID string Audience []string Expiration time.Time diff --git a/internal/user/repository/view/model/token.go b/internal/user/repository/view/model/token.go index ebe1564738b..733acf976aa 100644 --- a/internal/user/repository/view/model/token.go +++ b/internal/user/repository/view/model/token.go @@ -33,6 +33,7 @@ type TokenView struct { ResourceOwner string `json:"-" gorm:"column:resource_owner"` UserID string `json:"-" gorm:"column:user_id"` ApplicationID string `json:"applicationId" gorm:"column:application_id"` + ProjectID string `json:"projectID" gorm:"column:project_id"` UserAgentID string `json:"userAgentId" gorm:"column:user_agent_id"` Audience database.TextArray[string] `json:"audience" gorm:"column:audience"` Scopes database.TextArray[string] `json:"scopes" gorm:"column:scopes"` @@ -87,6 +88,7 @@ func TokenViewToModel(token *TokenView) *usr_model.TokenView { ResourceOwner: token.ResourceOwner, UserID: token.UserID, ApplicationID: token.ApplicationID, + ProjectID: token.ProjectID, UserAgentID: token.UserAgentID, Audience: token.Audience, Scopes: token.Scopes,