mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 01:37:31 +00:00
feat(api): add otp (sms and email) checks in session api (#6422)
* feat: add otp (sms and email) checks in session api * implement sending * fix tests * add tests * add integration tests * fix merge main and add tests * put default OTP Email url into config --------- Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com>
This commit is contained in:
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
SessionsProjectionTable = "projections.sessions4"
|
||||
SessionsProjectionTable = "projections.sessions5"
|
||||
|
||||
SessionColumnID = "id"
|
||||
SessionColumnCreationDate = "creation_date"
|
||||
@@ -31,6 +31,8 @@ const (
|
||||
SessionColumnWebAuthNCheckedAt = "webauthn_checked_at"
|
||||
SessionColumnWebAuthNUserVerified = "webauthn_user_verified"
|
||||
SessionColumnTOTPCheckedAt = "totp_checked_at"
|
||||
SessionColumnOTPSMSCheckedAt = "otp_sms_checked_at"
|
||||
SessionColumnOTPEmailCheckedAt = "otp_email_checked_at"
|
||||
SessionColumnMetadata = "metadata"
|
||||
SessionColumnTokenID = "token_id"
|
||||
)
|
||||
@@ -60,6 +62,8 @@ func newSessionProjection(ctx context.Context, config crdb.StatementHandlerConfi
|
||||
crdb.NewColumn(SessionColumnWebAuthNCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()),
|
||||
crdb.NewColumn(SessionColumnWebAuthNUserVerified, crdb.ColumnTypeBool, crdb.Nullable()),
|
||||
crdb.NewColumn(SessionColumnTOTPCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()),
|
||||
crdb.NewColumn(SessionColumnOTPSMSCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()),
|
||||
crdb.NewColumn(SessionColumnOTPEmailCheckedAt, crdb.ColumnTypeTimestamp, crdb.Nullable()),
|
||||
crdb.NewColumn(SessionColumnMetadata, crdb.ColumnTypeJSONB, crdb.Nullable()),
|
||||
crdb.NewColumn(SessionColumnTokenID, crdb.ColumnTypeText, crdb.Nullable()),
|
||||
},
|
||||
@@ -99,6 +103,14 @@ func (p *sessionProjection) reducers() []handler.AggregateReducer {
|
||||
Event: session.TOTPCheckedType,
|
||||
Reduce: p.reduceTOTPChecked,
|
||||
},
|
||||
{
|
||||
Event: session.OTPSMSCheckedType,
|
||||
Reduce: p.reduceOTPSMSChecked,
|
||||
},
|
||||
{
|
||||
Event: session.OTPEmailCheckedType,
|
||||
Reduce: p.reduceOTPEmailChecked,
|
||||
},
|
||||
{
|
||||
Event: session.TokenSetType,
|
||||
Reduce: p.reduceTokenSet,
|
||||
@@ -255,6 +267,46 @@ func (p *sessionProjection) reduceTOTPChecked(event eventstore.Event) (*handler.
|
||||
), nil
|
||||
}
|
||||
|
||||
func (p *sessionProjection) reduceOTPSMSChecked(event eventstore.Event) (*handler.Statement, error) {
|
||||
e, err := assertEvent[*session.OTPSMSCheckedEvent](event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return crdb.NewUpdateStatement(
|
||||
e,
|
||||
[]handler.Column{
|
||||
handler.NewCol(SessionColumnChangeDate, e.CreationDate()),
|
||||
handler.NewCol(SessionColumnSequence, e.Sequence()),
|
||||
handler.NewCol(SessionColumnOTPSMSCheckedAt, e.CheckedAt),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(SessionColumnID, e.Aggregate().ID),
|
||||
handler.NewCond(SessionColumnInstanceID, e.Aggregate().InstanceID),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
||||
func (p *sessionProjection) reduceOTPEmailChecked(event eventstore.Event) (*handler.Statement, error) {
|
||||
e, err := assertEvent[*session.OTPEmailCheckedEvent](event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return crdb.NewUpdateStatement(
|
||||
e,
|
||||
[]handler.Column{
|
||||
handler.NewCol(SessionColumnChangeDate, e.CreationDate()),
|
||||
handler.NewCol(SessionColumnSequence, e.Sequence()),
|
||||
handler.NewCol(SessionColumnOTPEmailCheckedAt, e.CheckedAt),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(SessionColumnID, e.Aggregate().ID),
|
||||
handler.NewCond(SessionColumnInstanceID, e.Aggregate().InstanceID),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
||||
func (p *sessionProjection) reduceTokenSet(event eventstore.Event) (*handler.Statement, error) {
|
||||
e, ok := event.(*session.TokenSetEvent)
|
||||
if !ok {
|
||||
|
@@ -43,7 +43,7 @@ func TestSessionProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.sessions4 (id, instance_id, creation_date, change_date, resource_owner, state, sequence, creator) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
expectedStmt: "INSERT INTO projections.sessions5 (id, instance_id, creation_date, change_date, resource_owner, state, sequence, creator) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@@ -79,7 +79,7 @@ func TestSessionProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.sessions4 SET (change_date, sequence, user_id, user_checked_at) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)",
|
||||
expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, user_id, user_checked_at) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
anyArg{},
|
||||
@@ -112,7 +112,7 @@ func TestSessionProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.sessions4 SET (change_date, sequence, password_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, password_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
anyArg{},
|
||||
@@ -145,7 +145,7 @@ func TestSessionProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.sessions4 SET (change_date, sequence, webauthn_checked_at, webauthn_user_verified) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)",
|
||||
expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, webauthn_checked_at, webauthn_user_verified) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
anyArg{},
|
||||
@@ -178,7 +178,7 @@ func TestSessionProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.sessions4 SET (change_date, sequence, intent_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, intent_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
anyArg{},
|
||||
@@ -210,7 +210,7 @@ func TestSessionProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.sessions4 SET (change_date, sequence, totp_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, totp_checked_at) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
anyArg{},
|
||||
@@ -242,7 +242,7 @@ func TestSessionProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.sessions4 SET (change_date, sequence, token_id) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, token_id) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
anyArg{},
|
||||
@@ -276,7 +276,7 @@ func TestSessionProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.sessions4 SET (change_date, sequence, metadata) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.sessions5 SET (change_date, sequence, metadata) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
anyArg{},
|
||||
@@ -308,7 +308,7 @@ func TestSessionProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.sessions4 WHERE (id = $1) AND (instance_id = $2)",
|
||||
expectedStmt: "DELETE FROM projections.sessions5 WHERE (id = $1) AND (instance_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@@ -335,7 +335,7 @@ func TestSessionProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.sessions4 WHERE (instance_id = $1)",
|
||||
expectedStmt: "DELETE FROM projections.sessions5 WHERE (instance_id = $1)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
},
|
||||
@@ -366,7 +366,7 @@ func TestSessionProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.sessions4 SET password_checked_at = $1 WHERE (user_id = $2) AND (password_checked_at < $3)",
|
||||
expectedStmt: "UPDATE projections.sessions5 SET password_checked_at = $1 WHERE (user_id = $2) AND (password_checked_at < $3)",
|
||||
expectedArgs: []interface{}{
|
||||
nil,
|
||||
"agg-id",
|
||||
|
@@ -35,6 +35,8 @@ type Session struct {
|
||||
IntentFactor SessionIntentFactor
|
||||
WebAuthNFactor SessionWebAuthNFactor
|
||||
TOTPFactor SessionTOTPFactor
|
||||
OTPSMSFactor SessionOTPFactor
|
||||
OTPEmailFactor SessionOTPFactor
|
||||
Metadata map[string][]byte
|
||||
}
|
||||
|
||||
@@ -63,6 +65,10 @@ type SessionTOTPFactor struct {
|
||||
TOTPCheckedAt time.Time
|
||||
}
|
||||
|
||||
type SessionOTPFactor struct {
|
||||
OTPCheckedAt time.Time
|
||||
}
|
||||
|
||||
type SessionsSearchQueries struct {
|
||||
SearchRequest
|
||||
Queries []SearchQuery
|
||||
@@ -141,6 +147,14 @@ var (
|
||||
name: projection.SessionColumnTOTPCheckedAt,
|
||||
table: sessionsTable,
|
||||
}
|
||||
SessionColumnOTPSMSCheckedAt = Column{
|
||||
name: projection.SessionColumnOTPSMSCheckedAt,
|
||||
table: sessionsTable,
|
||||
}
|
||||
SessionColumnOTPEmailCheckedAt = Column{
|
||||
name: projection.SessionColumnOTPEmailCheckedAt,
|
||||
table: sessionsTable,
|
||||
}
|
||||
SessionColumnMetadata = Column{
|
||||
name: projection.SessionColumnMetadata,
|
||||
table: sessionsTable,
|
||||
@@ -243,6 +257,8 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil
|
||||
SessionColumnWebAuthNCheckedAt.identifier(),
|
||||
SessionColumnWebAuthNUserVerified.identifier(),
|
||||
SessionColumnTOTPCheckedAt.identifier(),
|
||||
SessionColumnOTPSMSCheckedAt.identifier(),
|
||||
SessionColumnOTPEmailCheckedAt.identifier(),
|
||||
SessionColumnMetadata.identifier(),
|
||||
SessionColumnToken.identifier(),
|
||||
).From(sessionsTable.identifier()).
|
||||
@@ -263,6 +279,8 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil
|
||||
webAuthNCheckedAt sql.NullTime
|
||||
webAuthNUserPresent sql.NullBool
|
||||
totpCheckedAt sql.NullTime
|
||||
otpSMSCheckedAt sql.NullTime
|
||||
otpEmailCheckedAt sql.NullTime
|
||||
metadata database.Map[[]byte]
|
||||
token sql.NullString
|
||||
)
|
||||
@@ -285,6 +303,8 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil
|
||||
&webAuthNCheckedAt,
|
||||
&webAuthNUserPresent,
|
||||
&totpCheckedAt,
|
||||
&otpSMSCheckedAt,
|
||||
&otpEmailCheckedAt,
|
||||
&metadata,
|
||||
&token,
|
||||
)
|
||||
@@ -306,6 +326,8 @@ func prepareSessionQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuil
|
||||
session.WebAuthNFactor.WebAuthNCheckedAt = webAuthNCheckedAt.Time
|
||||
session.WebAuthNFactor.UserVerified = webAuthNUserPresent.Bool
|
||||
session.TOTPFactor.TOTPCheckedAt = totpCheckedAt.Time
|
||||
session.OTPSMSFactor.OTPCheckedAt = otpSMSCheckedAt.Time
|
||||
session.OTPEmailFactor.OTPCheckedAt = otpEmailCheckedAt.Time
|
||||
session.Metadata = metadata
|
||||
|
||||
return session, token.String, nil
|
||||
@@ -331,6 +353,8 @@ func prepareSessionsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBui
|
||||
SessionColumnWebAuthNCheckedAt.identifier(),
|
||||
SessionColumnWebAuthNUserVerified.identifier(),
|
||||
SessionColumnTOTPCheckedAt.identifier(),
|
||||
SessionColumnOTPSMSCheckedAt.identifier(),
|
||||
SessionColumnOTPEmailCheckedAt.identifier(),
|
||||
SessionColumnMetadata.identifier(),
|
||||
countColumn.identifier(),
|
||||
).From(sessionsTable.identifier()).
|
||||
@@ -354,6 +378,8 @@ func prepareSessionsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBui
|
||||
webAuthNCheckedAt sql.NullTime
|
||||
webAuthNUserPresent sql.NullBool
|
||||
totpCheckedAt sql.NullTime
|
||||
otpSMSCheckedAt sql.NullTime
|
||||
otpEmailCheckedAt sql.NullTime
|
||||
metadata database.Map[[]byte]
|
||||
)
|
||||
|
||||
@@ -375,6 +401,8 @@ func prepareSessionsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBui
|
||||
&webAuthNCheckedAt,
|
||||
&webAuthNUserPresent,
|
||||
&totpCheckedAt,
|
||||
&otpSMSCheckedAt,
|
||||
&otpEmailCheckedAt,
|
||||
&metadata,
|
||||
&sessions.Count,
|
||||
)
|
||||
@@ -392,6 +420,8 @@ func prepareSessionsQuery(ctx context.Context, db prepareDatabase) (sq.SelectBui
|
||||
session.WebAuthNFactor.WebAuthNCheckedAt = webAuthNCheckedAt.Time
|
||||
session.WebAuthNFactor.UserVerified = webAuthNUserPresent.Bool
|
||||
session.TOTPFactor.TOTPCheckedAt = totpCheckedAt.Time
|
||||
session.OTPSMSFactor.OTPCheckedAt = otpSMSCheckedAt.Time
|
||||
session.OTPEmailFactor.OTPCheckedAt = otpEmailCheckedAt.Time
|
||||
session.Metadata = metadata
|
||||
|
||||
sessions.Sessions = append(sessions.Sessions, session)
|
||||
|
@@ -17,53 +17,57 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
expectedSessionQuery = regexp.QuoteMeta(`SELECT projections.sessions4.id,` +
|
||||
` projections.sessions4.creation_date,` +
|
||||
` projections.sessions4.change_date,` +
|
||||
` projections.sessions4.sequence,` +
|
||||
` projections.sessions4.state,` +
|
||||
` projections.sessions4.resource_owner,` +
|
||||
` projections.sessions4.creator,` +
|
||||
` projections.sessions4.user_id,` +
|
||||
` projections.sessions4.user_checked_at,` +
|
||||
expectedSessionQuery = regexp.QuoteMeta(`SELECT projections.sessions5.id,` +
|
||||
` projections.sessions5.creation_date,` +
|
||||
` projections.sessions5.change_date,` +
|
||||
` projections.sessions5.sequence,` +
|
||||
` projections.sessions5.state,` +
|
||||
` projections.sessions5.resource_owner,` +
|
||||
` projections.sessions5.creator,` +
|
||||
` projections.sessions5.user_id,` +
|
||||
` projections.sessions5.user_checked_at,` +
|
||||
` projections.login_names2.login_name,` +
|
||||
` projections.users8_humans.display_name,` +
|
||||
` projections.users8.resource_owner,` +
|
||||
` projections.sessions4.password_checked_at,` +
|
||||
` projections.sessions4.intent_checked_at,` +
|
||||
` projections.sessions4.webauthn_checked_at,` +
|
||||
` projections.sessions4.webauthn_user_verified,` +
|
||||
` projections.sessions4.totp_checked_at,` +
|
||||
` projections.sessions4.metadata,` +
|
||||
` projections.sessions4.token_id` +
|
||||
` FROM projections.sessions4` +
|
||||
` LEFT JOIN projections.login_names2 ON projections.sessions4.user_id = projections.login_names2.user_id AND projections.sessions4.instance_id = projections.login_names2.instance_id` +
|
||||
` LEFT JOIN projections.users8_humans ON projections.sessions4.user_id = projections.users8_humans.user_id AND projections.sessions4.instance_id = projections.users8_humans.instance_id` +
|
||||
` LEFT JOIN projections.users8 ON projections.sessions4.user_id = projections.users8.id AND projections.sessions4.instance_id = projections.users8.instance_id` +
|
||||
` projections.sessions5.password_checked_at,` +
|
||||
` projections.sessions5.intent_checked_at,` +
|
||||
` projections.sessions5.webauthn_checked_at,` +
|
||||
` projections.sessions5.webauthn_user_verified,` +
|
||||
` projections.sessions5.totp_checked_at,` +
|
||||
` projections.sessions5.otp_sms_checked_at,` +
|
||||
` projections.sessions5.otp_email_checked_at,` +
|
||||
` projections.sessions5.metadata,` +
|
||||
` projections.sessions5.token_id` +
|
||||
` FROM projections.sessions5` +
|
||||
` LEFT JOIN projections.login_names2 ON projections.sessions5.user_id = projections.login_names2.user_id AND projections.sessions5.instance_id = projections.login_names2.instance_id` +
|
||||
` LEFT JOIN projections.users8_humans ON projections.sessions5.user_id = projections.users8_humans.user_id AND projections.sessions5.instance_id = projections.users8_humans.instance_id` +
|
||||
` LEFT JOIN projections.users8 ON projections.sessions5.user_id = projections.users8.id AND projections.sessions5.instance_id = projections.users8.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`)
|
||||
expectedSessionsQuery = regexp.QuoteMeta(`SELECT projections.sessions4.id,` +
|
||||
` projections.sessions4.creation_date,` +
|
||||
` projections.sessions4.change_date,` +
|
||||
` projections.sessions4.sequence,` +
|
||||
` projections.sessions4.state,` +
|
||||
` projections.sessions4.resource_owner,` +
|
||||
` projections.sessions4.creator,` +
|
||||
` projections.sessions4.user_id,` +
|
||||
` projections.sessions4.user_checked_at,` +
|
||||
expectedSessionsQuery = regexp.QuoteMeta(`SELECT projections.sessions5.id,` +
|
||||
` projections.sessions5.creation_date,` +
|
||||
` projections.sessions5.change_date,` +
|
||||
` projections.sessions5.sequence,` +
|
||||
` projections.sessions5.state,` +
|
||||
` projections.sessions5.resource_owner,` +
|
||||
` projections.sessions5.creator,` +
|
||||
` projections.sessions5.user_id,` +
|
||||
` projections.sessions5.user_checked_at,` +
|
||||
` projections.login_names2.login_name,` +
|
||||
` projections.users8_humans.display_name,` +
|
||||
` projections.users8.resource_owner,` +
|
||||
` projections.sessions4.password_checked_at,` +
|
||||
` projections.sessions4.intent_checked_at,` +
|
||||
` projections.sessions4.webauthn_checked_at,` +
|
||||
` projections.sessions4.webauthn_user_verified,` +
|
||||
` projections.sessions4.totp_checked_at,` +
|
||||
` projections.sessions4.metadata,` +
|
||||
` projections.sessions5.password_checked_at,` +
|
||||
` projections.sessions5.intent_checked_at,` +
|
||||
` projections.sessions5.webauthn_checked_at,` +
|
||||
` projections.sessions5.webauthn_user_verified,` +
|
||||
` projections.sessions5.totp_checked_at,` +
|
||||
` projections.sessions5.otp_sms_checked_at,` +
|
||||
` projections.sessions5.otp_email_checked_at,` +
|
||||
` projections.sessions5.metadata,` +
|
||||
` COUNT(*) OVER ()` +
|
||||
` FROM projections.sessions4` +
|
||||
` LEFT JOIN projections.login_names2 ON projections.sessions4.user_id = projections.login_names2.user_id AND projections.sessions4.instance_id = projections.login_names2.instance_id` +
|
||||
` LEFT JOIN projections.users8_humans ON projections.sessions4.user_id = projections.users8_humans.user_id AND projections.sessions4.instance_id = projections.users8_humans.instance_id` +
|
||||
` LEFT JOIN projections.users8 ON projections.sessions4.user_id = projections.users8.id AND projections.sessions4.instance_id = projections.users8.instance_id` +
|
||||
` FROM projections.sessions5` +
|
||||
` LEFT JOIN projections.login_names2 ON projections.sessions5.user_id = projections.login_names2.user_id AND projections.sessions5.instance_id = projections.login_names2.instance_id` +
|
||||
` LEFT JOIN projections.users8_humans ON projections.sessions5.user_id = projections.users8_humans.user_id AND projections.sessions5.instance_id = projections.users8_humans.instance_id` +
|
||||
` LEFT JOIN projections.users8 ON projections.sessions5.user_id = projections.users8.id AND projections.sessions5.instance_id = projections.users8.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`)
|
||||
|
||||
sessionCols = []string{
|
||||
@@ -84,6 +88,8 @@ var (
|
||||
"webauthn_checked_at",
|
||||
"webauthn_user_verified",
|
||||
"totp_checked_at",
|
||||
"otp_sms_checked_at",
|
||||
"otp_email_checked_at",
|
||||
"metadata",
|
||||
"token",
|
||||
}
|
||||
@@ -106,6 +112,8 @@ var (
|
||||
"webauthn_checked_at",
|
||||
"webauthn_user_verified",
|
||||
"totp_checked_at",
|
||||
"otp_sms_checked_at",
|
||||
"otp_email_checked_at",
|
||||
"metadata",
|
||||
"count",
|
||||
}
|
||||
@@ -160,6 +168,8 @@ func Test_SessionsPrepare(t *testing.T) {
|
||||
testNow,
|
||||
true,
|
||||
testNow,
|
||||
testNow,
|
||||
testNow,
|
||||
[]byte(`{"key": "dmFsdWU="}`),
|
||||
},
|
||||
},
|
||||
@@ -198,6 +208,12 @@ func Test_SessionsPrepare(t *testing.T) {
|
||||
TOTPFactor: SessionTOTPFactor{
|
||||
TOTPCheckedAt: testNow,
|
||||
},
|
||||
OTPSMSFactor: SessionOTPFactor{
|
||||
OTPCheckedAt: testNow,
|
||||
},
|
||||
OTPEmailFactor: SessionOTPFactor{
|
||||
OTPCheckedAt: testNow,
|
||||
},
|
||||
Metadata: map[string][]byte{
|
||||
"key": []byte("value"),
|
||||
},
|
||||
@@ -231,6 +247,8 @@ func Test_SessionsPrepare(t *testing.T) {
|
||||
testNow,
|
||||
true,
|
||||
testNow,
|
||||
testNow,
|
||||
testNow,
|
||||
[]byte(`{"key": "dmFsdWU="}`),
|
||||
},
|
||||
{
|
||||
@@ -251,6 +269,8 @@ func Test_SessionsPrepare(t *testing.T) {
|
||||
testNow,
|
||||
false,
|
||||
testNow,
|
||||
testNow,
|
||||
testNow,
|
||||
[]byte(`{"key": "dmFsdWU="}`),
|
||||
},
|
||||
},
|
||||
@@ -289,6 +309,12 @@ func Test_SessionsPrepare(t *testing.T) {
|
||||
TOTPFactor: SessionTOTPFactor{
|
||||
TOTPCheckedAt: testNow,
|
||||
},
|
||||
OTPSMSFactor: SessionOTPFactor{
|
||||
OTPCheckedAt: testNow,
|
||||
},
|
||||
OTPEmailFactor: SessionOTPFactor{
|
||||
OTPCheckedAt: testNow,
|
||||
},
|
||||
Metadata: map[string][]byte{
|
||||
"key": []byte("value"),
|
||||
},
|
||||
@@ -321,6 +347,12 @@ func Test_SessionsPrepare(t *testing.T) {
|
||||
TOTPFactor: SessionTOTPFactor{
|
||||
TOTPCheckedAt: testNow,
|
||||
},
|
||||
OTPSMSFactor: SessionOTPFactor{
|
||||
OTPCheckedAt: testNow,
|
||||
},
|
||||
OTPEmailFactor: SessionOTPFactor{
|
||||
OTPCheckedAt: testNow,
|
||||
},
|
||||
Metadata: map[string][]byte{
|
||||
"key": []byte("value"),
|
||||
},
|
||||
@@ -407,6 +439,8 @@ func Test_SessionPrepare(t *testing.T) {
|
||||
testNow,
|
||||
true,
|
||||
testNow,
|
||||
testNow,
|
||||
testNow,
|
||||
[]byte(`{"key": "dmFsdWU="}`),
|
||||
"tokenID",
|
||||
},
|
||||
@@ -440,6 +474,12 @@ func Test_SessionPrepare(t *testing.T) {
|
||||
TOTPFactor: SessionTOTPFactor{
|
||||
TOTPCheckedAt: testNow,
|
||||
},
|
||||
OTPSMSFactor: SessionOTPFactor{
|
||||
OTPCheckedAt: testNow,
|
||||
},
|
||||
OTPEmailFactor: SessionOTPFactor{
|
||||
OTPCheckedAt: testNow,
|
||||
},
|
||||
Metadata: map[string][]byte{
|
||||
"key": []byte("value"),
|
||||
},
|
||||
|
Reference in New Issue
Block a user