mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 19:17:32 +00:00
feat(oidc): sid claim for id_tokens issued through login V1 (#8525)
# Which Problems Are Solved id_tokens issued for auth requests created through the login UI currently do not provide a sid claim. This is due to the fact that (SSO) sessions for the login UI do not have one and are only computed by the userAgent(ID), the user(ID) and the authentication checks of the latter. This prevents client to track sessions and terminate specific session on the end_session_endpoint. # How the Problems Are Solved - An `id` column is added to the `auth.user_sessions` table. - The `id` (prefixed with `V1_`) is set whenever a session is added or updated to active (from terminated) - The id is passed to the `oidc session` (as v2 sessionIDs), to expose it as `sid` claim # Additional Changes - refactored `getUpdateCols` to handle different column value types and add arguments for query # Additional Context - closes #8499 - relates to #8501
This commit is contained in:
@@ -1048,6 +1048,7 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.Auth
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request.SessionID = userSession.ID
|
||||
request.DisplayName = userSession.DisplayName
|
||||
request.AvatarKey = userSession.AvatarKey
|
||||
if user.HumanView != nil && user.HumanView.PreferredLanguage != "" {
|
||||
|
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
|
||||
handler2 "github.com/zitadel/zitadel/internal/eventstore/handler/v2"
|
||||
"github.com/zitadel/zitadel/internal/id"
|
||||
query2 "github.com/zitadel/zitadel/internal/query"
|
||||
)
|
||||
|
||||
@@ -40,6 +41,7 @@ func Register(ctx context.Context, configs Config, view *view.View, queries *que
|
||||
configs.overwrite("UserSession"),
|
||||
view,
|
||||
queries,
|
||||
id.SonyFlakeGenerator(),
|
||||
))
|
||||
|
||||
projections = append(projections, newToken(ctx,
|
||||
|
@@ -2,12 +2,14 @@ package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
auth_view "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing/view"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
|
||||
"github.com/zitadel/zitadel/internal/id"
|
||||
query2 "github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||
"github.com/zitadel/zitadel/internal/repository/org"
|
||||
@@ -18,12 +20,15 @@ import (
|
||||
|
||||
const (
|
||||
userSessionTable = "auth.user_sessions"
|
||||
|
||||
IDPrefixV1 = "V1_"
|
||||
)
|
||||
|
||||
type UserSession struct {
|
||||
queries *query2.Queries
|
||||
view *auth_view.View
|
||||
es handler.EventStore
|
||||
queries *query2.Queries
|
||||
view *auth_view.View
|
||||
es handler.EventStore
|
||||
idGenerator id.Generator
|
||||
}
|
||||
|
||||
var _ handler.Projection = (*UserSession)(nil)
|
||||
@@ -33,14 +38,16 @@ func newUserSession(
|
||||
config handler.Config,
|
||||
view *auth_view.View,
|
||||
queries *query2.Queries,
|
||||
idGenerator id.Generator,
|
||||
) *handler.Handler {
|
||||
return handler.NewHandler(
|
||||
ctx,
|
||||
&config,
|
||||
&UserSession{
|
||||
queries: queries,
|
||||
view: view,
|
||||
es: config.Eventstore,
|
||||
queries: queries,
|
||||
view: view,
|
||||
es: config.Eventstore,
|
||||
idGenerator: idGenerator,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -187,7 +194,7 @@ func (s *UserSession) Reducers() []handler.AggregateReducer {
|
||||
}
|
||||
}
|
||||
|
||||
func sessionColumns(event eventstore.Event, columns ...handler.Column) ([]handler.Column, error) {
|
||||
func (u *UserSession) sessionColumns(event eventstore.Event, columns ...handler.Column) ([]handler.Column, error) {
|
||||
userAgent, err := agentIDFromSession(event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -203,14 +210,34 @@ func sessionColumns(event eventstore.Event, columns ...handler.Column) ([]handle
|
||||
}, columns...), nil
|
||||
}
|
||||
|
||||
func (u *UserSession) sessionColumnsActivate(event eventstore.Event, columns ...handler.Column) ([]handler.Column, error) {
|
||||
sessionID, err := u.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sessionID = IDPrefixV1 + sessionID
|
||||
columns = slices.Grow(columns, 2)
|
||||
columns = append(columns,
|
||||
handler.NewCol(view_model.UserSessionKeyState, domain.UserSessionStateActive),
|
||||
handler.NewCol(view_model.UserSessionKeyID,
|
||||
handler.OnlySetValueInCase(userSessionTable, sessionID,
|
||||
handler.ConditionOr(
|
||||
handler.ColumnChangedCondition(userSessionTable, view_model.UserSessionKeyState, domain.UserSessionStateTerminated, domain.UserSessionStateActive),
|
||||
handler.ColumnIsNullCondition(userSessionTable, view_model.UserSessionKeyID),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
return u.sessionColumns(event, columns...)
|
||||
}
|
||||
|
||||
func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err error) {
|
||||
// in case anything needs to be change here check if appendEvent function needs the change as well
|
||||
switch event.Type() {
|
||||
case user.UserV1PasswordCheckSucceededType,
|
||||
user.HumanPasswordCheckSucceededType:
|
||||
columns, err := sessionColumns(event,
|
||||
columns, err := u.sessionColumnsActivate(event,
|
||||
handler.NewCol(view_model.UserSessionKeyPasswordVerification, event.CreatedAt()),
|
||||
handler.NewCol(view_model.UserSessionKeyState, domain.UserSessionStateActive),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -218,9 +245,8 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err
|
||||
return handler.NewUpsertStatement(event, columns[0:3], columns), nil
|
||||
case user.UserV1PasswordCheckFailedType,
|
||||
user.HumanPasswordCheckFailedType:
|
||||
columns, err := sessionColumns(event,
|
||||
columns, err := u.sessionColumnsActivate(event,
|
||||
handler.NewCol(view_model.UserSessionKeyPasswordVerification, time.Time{}),
|
||||
handler.NewCol(view_model.UserSessionKeyState, domain.UserSessionStateActive),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -228,10 +254,9 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err
|
||||
return handler.NewUpsertStatement(event, columns[0:3], columns), nil
|
||||
case user.UserV1MFAOTPCheckSucceededType,
|
||||
user.HumanMFAOTPCheckSucceededType:
|
||||
columns, err := sessionColumns(event,
|
||||
columns, err := u.sessionColumnsActivate(event,
|
||||
handler.NewCol(view_model.UserSessionKeySecondFactorVerification, event.CreatedAt()),
|
||||
handler.NewCol(view_model.UserSessionKeySecondFactorVerificationType, domain.MFATypeTOTP),
|
||||
handler.NewCol(view_model.UserSessionKeyState, domain.UserSessionStateActive),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -240,9 +265,8 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err
|
||||
case user.UserV1MFAOTPCheckFailedType,
|
||||
user.HumanMFAOTPCheckFailedType,
|
||||
user.HumanU2FTokenCheckFailedType:
|
||||
columns, err := sessionColumns(event,
|
||||
columns, err := u.sessionColumnsActivate(event,
|
||||
handler.NewCol(view_model.UserSessionKeySecondFactorVerification, time.Time{}),
|
||||
handler.NewCol(view_model.UserSessionKeyState, domain.UserSessionStateActive),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -250,7 +274,7 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err
|
||||
return handler.NewUpsertStatement(event, columns[0:3], columns), nil
|
||||
case user.UserV1SignedOutType,
|
||||
user.HumanSignedOutType:
|
||||
columns, err := sessionColumns(event,
|
||||
columns, err := u.sessionColumns(event,
|
||||
handler.NewCol(view_model.UserSessionKeyPasswordlessVerification, time.Time{}),
|
||||
handler.NewCol(view_model.UserSessionKeyPasswordVerification, time.Time{}),
|
||||
handler.NewCol(view_model.UserSessionKeySecondFactorVerification, time.Time{}),
|
||||
@@ -270,10 +294,9 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
columns, err := sessionColumns(event,
|
||||
columns, err := u.sessionColumnsActivate(event,
|
||||
handler.NewCol(view_model.UserSessionKeyExternalLoginVerification, event.CreatedAt()),
|
||||
handler.NewCol(view_model.UserSessionKeySelectedIDPConfigID, data.SelectedIDPConfigID),
|
||||
handler.NewCol(view_model.UserSessionKeyState, domain.UserSessionStateActive),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -285,10 +308,9 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
columns, err := sessionColumns(event,
|
||||
columns, err := u.sessionColumnsActivate(event,
|
||||
handler.NewCol(view_model.UserSessionKeySecondFactorVerification, event.CreatedAt()),
|
||||
handler.NewCol(view_model.UserSessionKeySecondFactorVerificationType, domain.MFATypeU2F),
|
||||
handler.NewCol(view_model.UserSessionKeyState, domain.UserSessionStateActive),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -300,11 +322,10 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
columns, err := sessionColumns(event,
|
||||
columns, err := u.sessionColumnsActivate(event,
|
||||
handler.NewCol(view_model.UserSessionKeyPasswordlessVerification, event.CreatedAt()),
|
||||
handler.NewCol(view_model.UserSessionKeyMultiFactorVerification, event.CreatedAt()),
|
||||
handler.NewCol(view_model.UserSessionKeyMultiFactorVerificationType, domain.MFATypeU2FUserVerification),
|
||||
handler.NewCol(view_model.UserSessionKeyState, domain.UserSessionStateActive),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -316,10 +337,9 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
columns, err := sessionColumns(event,
|
||||
columns, err := u.sessionColumnsActivate(event,
|
||||
handler.NewCol(view_model.UserSessionKeyPasswordlessVerification, time.Time{}),
|
||||
handler.NewCol(view_model.UserSessionKeyMultiFactorVerification, time.Time{}),
|
||||
handler.NewCol(view_model.UserSessionKeyState, domain.UserSessionStateActive),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -429,8 +449,7 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err
|
||||
},
|
||||
), nil
|
||||
case user.HumanRegisteredType:
|
||||
columns, err := sessionColumns(event,
|
||||
handler.NewCol(view_model.UserSessionKeyState, domain.UserSessionStateActive),
|
||||
columns, err := u.sessionColumnsActivate(event,
|
||||
handler.NewCol(view_model.UserSessionKeyPasswordVerification, event.CreatedAt()),
|
||||
)
|
||||
if err != nil {
|
||||
|
Reference in New Issue
Block a user