2023-06-07 17:28:42 +02:00
//go:build integration
package session_test
import (
"context"
2023-07-14 13:16:16 +02:00
"fmt"
2023-06-07 17:28:42 +02:00
"testing"
"time"
2023-07-14 13:16:16 +02:00
"github.com/muhlemmer/gu"
2023-08-15 12:50:42 +03:00
"github.com/pquerna/otp/totp"
2023-06-07 17:28:42 +02:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
2024-05-31 00:08:48 +02:00
"google.golang.org/grpc/codes"
2023-07-14 13:16:16 +02:00
"google.golang.org/grpc/metadata"
2024-05-31 00:08:48 +02:00
"google.golang.org/grpc/status"
2023-10-12 15:16:59 +03:00
"google.golang.org/protobuf/proto"
2023-11-06 11:48:28 +02:00
"google.golang.org/protobuf/types/known/durationpb"
2024-02-28 12:21:11 +02:00
"google.golang.org/protobuf/types/known/timestamppb"
2023-06-21 16:06:18 +02:00
2023-06-07 17:28:42 +02:00
"github.com/zitadel/zitadel/internal/integration"
2025-02-20 13:27:20 +01:00
"github.com/zitadel/zitadel/internal/integration/sink"
2024-05-31 00:08:48 +02:00
mgmt "github.com/zitadel/zitadel/pkg/grpc/management"
2024-07-26 22:39:55 +02:00
"github.com/zitadel/zitadel/pkg/grpc/object/v2"
"github.com/zitadel/zitadel/pkg/grpc/session/v2"
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
2023-06-07 17:28:42 +02:00
)
2025-09-25 14:30:10 +02:00
func verifyCurrentSession ( t * testing . T , id , token string , sequence uint64 , creationDate , changeDate * timestamppb . Timestamp , metadata map [ string ] [ ] byte , userAgent * session . UserAgent , expirationWindow time . Duration , userID string , factors ... wantFactor ) * session . Session {
2023-08-15 12:50:42 +03:00
t . Helper ( )
2023-06-07 17:28:42 +02:00
require . NotEmpty ( t , id )
require . NotEmpty ( t , token )
2023-07-07 11:15:05 +03:00
resp , err := Client . GetSession ( CTX , & session . GetSessionRequest {
SessionId : id ,
SessionToken : & token ,
} )
require . NoError ( t , err )
s := resp . GetSession ( )
2025-01-14 14:15:59 +01:00
want := & session . Session {
2025-09-25 14:30:10 +02:00
Id : id ,
Sequence : sequence ,
Metadata : metadata ,
UserAgent : userAgent ,
CreationDate : creationDate ,
ChangeDate : changeDate ,
2025-01-14 14:15:59 +01:00
}
2025-09-25 14:30:10 +02:00
verifySession ( t , s , want , expirationWindow , userID , factors ... )
2025-01-14 14:15:59 +01:00
return s
}
2023-06-07 17:28:42 +02:00
2025-09-25 14:30:10 +02:00
func verifySession ( t assert . TestingT , s * session . Session , want * session . Session , expirationWindow time . Duration , userID string , factors ... wantFactor ) {
2025-01-14 14:15:59 +01:00
assert . Equal ( t , want . Id , s . GetId ( ) )
2025-09-25 14:30:10 +02:00
assert . WithinRange ( t , s . GetCreationDate ( ) . AsTime ( ) , want . CreationDate . AsTime ( ) , want . CreationDate . AsTime ( ) )
assert . WithinRange ( t , s . GetChangeDate ( ) . AsTime ( ) , want . ChangeDate . AsTime ( ) , want . ChangeDate . AsTime ( ) )
2025-01-14 14:15:59 +01:00
assert . Equal ( t , want . Sequence , s . GetSequence ( ) )
assert . Equal ( t , want . Metadata , s . GetMetadata ( ) )
2023-10-12 15:16:59 +03:00
2025-01-14 14:15:59 +01:00
if ! proto . Equal ( want . UserAgent , s . GetUserAgent ( ) ) {
t . Errorf ( "user agent =\n%v\nwant\n%v" , s . GetUserAgent ( ) , want . UserAgent )
2023-10-12 15:16:59 +03:00
}
2023-11-06 11:48:28 +02:00
if expirationWindow == 0 {
assert . Nil ( t , s . GetExpirationDate ( ) )
} else {
assert . WithinRange ( t , s . GetExpirationDate ( ) . AsTime ( ) , time . Now ( ) . Add ( - expirationWindow ) , time . Now ( ) . Add ( expirationWindow ) )
}
2023-10-12 15:16:59 +03:00
2025-09-25 14:30:10 +02:00
verifyFactors ( t , s . GetFactors ( ) , want . CreationDate , want . ChangeDate , userID , factors )
2023-06-07 17:28:42 +02:00
}
type wantFactor int
const (
wantUserFactor wantFactor = iota
wantPasswordFactor
2023-08-11 18:36:18 +03:00
wantWebAuthNFactor
wantWebAuthNFactorUserVerified
2023-08-15 12:50:42 +03:00
wantTOTPFactor
2023-06-21 16:06:18 +02:00
wantIntentFactor
2023-08-24 11:41:52 +02:00
wantOTPSMSFactor
wantOTPEmailFactor
2023-06-07 17:28:42 +02:00
)
2025-09-25 14:30:10 +02:00
func verifyFactors ( t assert . TestingT , factors * session . Factors , creationDate , changeDate * timestamppb . Timestamp , userID string , want [ ] wantFactor ) {
creationDateWithSkew := creationDate . AsTime ( ) . Add ( - 250 * time . Millisecond ) // account for offset because from check (set by application servercreationDateWithSkew db)
changeDateWithSkew := changeDate . AsTime ( ) . Add ( 250 * time . Millisecond ) // account for offset because from check (set by application server) and change date (set by db)
2023-06-07 17:28:42 +02:00
for _ , w := range want {
switch w {
case wantUserFactor :
uf := factors . GetUser ( )
assert . NotNil ( t , uf )
2025-09-25 14:30:10 +02:00
assert . WithinRange ( t , uf . GetVerifiedAt ( ) . AsTime ( ) , creationDateWithSkew , changeDateWithSkew )
2024-05-14 09:20:31 +02:00
assert . Equal ( t , userID , uf . GetId ( ) )
2023-06-07 17:28:42 +02:00
case wantPasswordFactor :
pf := factors . GetPassword ( )
assert . NotNil ( t , pf )
2025-09-25 14:30:10 +02:00
assert . WithinRange ( t , pf . GetVerifiedAt ( ) . AsTime ( ) , creationDateWithSkew , changeDateWithSkew )
2023-08-11 18:36:18 +03:00
case wantWebAuthNFactor :
pf := factors . GetWebAuthN ( )
2023-06-07 17:28:42 +02:00
assert . NotNil ( t , pf )
2025-09-25 14:30:10 +02:00
assert . WithinRange ( t , pf . GetVerifiedAt ( ) . AsTime ( ) , creationDateWithSkew , changeDateWithSkew )
2023-08-15 12:50:42 +03:00
assert . False ( t , pf . GetUserVerified ( ) )
2023-08-11 18:36:18 +03:00
case wantWebAuthNFactorUserVerified :
pf := factors . GetWebAuthN ( )
assert . NotNil ( t , pf )
2025-09-25 14:30:10 +02:00
assert . WithinRange ( t , pf . GetVerifiedAt ( ) . AsTime ( ) , creationDateWithSkew , changeDateWithSkew )
2023-08-15 12:50:42 +03:00
assert . True ( t , pf . GetUserVerified ( ) )
case wantTOTPFactor :
pf := factors . GetTotp ( )
assert . NotNil ( t , pf )
2025-09-25 14:30:10 +02:00
assert . WithinRange ( t , pf . GetVerifiedAt ( ) . AsTime ( ) , creationDateWithSkew , changeDateWithSkew )
2023-06-21 16:06:18 +02:00
case wantIntentFactor :
pf := factors . GetIntent ( )
assert . NotNil ( t , pf )
2025-09-25 14:30:10 +02:00
assert . WithinRange ( t , pf . GetVerifiedAt ( ) . AsTime ( ) , creationDateWithSkew , changeDateWithSkew )
2023-08-24 11:41:52 +02:00
case wantOTPSMSFactor :
pf := factors . GetOtpSms ( )
assert . NotNil ( t , pf )
2025-09-25 14:30:10 +02:00
assert . WithinRange ( t , pf . GetVerifiedAt ( ) . AsTime ( ) , creationDateWithSkew , changeDateWithSkew )
2023-08-24 11:41:52 +02:00
case wantOTPEmailFactor :
pf := factors . GetOtpEmail ( )
assert . NotNil ( t , pf )
2025-09-25 14:30:10 +02:00
assert . WithinRange ( t , pf . GetVerifiedAt ( ) . AsTime ( ) , creationDateWithSkew , changeDateWithSkew )
2023-06-07 17:28:42 +02:00
}
}
}
func TestServer_CreateSession ( t * testing . T ) {
tests := [ ] struct {
2023-11-06 11:48:28 +02:00
name string
req * session . CreateSessionRequest
want * session . CreateSessionResponse
wantErr bool
wantFactors [ ] wantFactor
wantUserAgent * session . UserAgent
wantExpirationWindow time . Duration
2023-06-07 17:28:42 +02:00
} {
{
name : "empty session" ,
req : & session . CreateSessionRequest {
Metadata : map [ string ] [ ] byte { "foo" : [ ] byte ( "bar" ) } ,
} ,
want : & session . CreateSessionResponse {
Details : & object . Details {
2024-02-28 12:21:11 +02:00
ChangeDate : timestamppb . Now ( ) ,
2024-09-06 15:47:57 +03:00
ResourceOwner : Instance . ID ( ) ,
2023-06-07 17:28:42 +02:00
} ,
} ,
} ,
2023-10-12 15:16:59 +03:00
{
2025-01-14 14:15:59 +01:00
name : "full session" ,
2023-10-12 15:16:59 +03:00
req : & session . CreateSessionRequest {
2025-01-14 14:15:59 +01:00
Checks : & session . Checks {
User : & session . CheckUser {
Search : & session . CheckUser_UserId {
UserId : User . GetUserId ( ) ,
} ,
} ,
} ,
2023-10-12 15:16:59 +03:00
Metadata : map [ string ] [ ] byte { "foo" : [ ] byte ( "bar" ) } ,
UserAgent : & session . UserAgent {
FingerprintId : gu . Ptr ( "fingerPrintID" ) ,
Ip : gu . Ptr ( "1.2.3.4" ) ,
Description : gu . Ptr ( "Description" ) ,
Header : map [ string ] * session . UserAgent_HeaderValues {
"foo" : { Values : [ ] string { "foo" , "bar" } } ,
} ,
} ,
2025-01-14 14:15:59 +01:00
Lifetime : durationpb . New ( 5 * time . Minute ) ,
2023-10-12 15:16:59 +03:00
} ,
want : & session . CreateSessionResponse {
Details : & object . Details {
2024-02-28 12:21:11 +02:00
ChangeDate : timestamppb . Now ( ) ,
2024-09-06 15:47:57 +03:00
ResourceOwner : Instance . ID ( ) ,
2023-10-12 15:16:59 +03:00
} ,
} ,
} ,
2023-11-06 11:48:28 +02:00
{
name : "negative lifetime" ,
req : & session . CreateSessionRequest {
Metadata : map [ string ] [ ] byte { "foo" : [ ] byte ( "bar" ) } ,
Lifetime : durationpb . New ( - 5 * time . Minute ) ,
} ,
wantErr : true ,
} ,
2024-02-28 10:30:05 +01:00
{
name : "deactivated user" ,
req : & session . CreateSessionRequest {
Checks : & session . Checks {
User : & session . CheckUser {
Search : & session . CheckUser_UserId {
UserId : DeactivatedUser . GetUserId ( ) ,
} ,
} ,
} ,
} ,
wantErr : true ,
} ,
{
name : "locked user" ,
req : & session . CreateSessionRequest {
Checks : & session . Checks {
User : & session . CheckUser {
Search : & session . CheckUser_UserId {
UserId : LockedUser . GetUserId ( ) ,
} ,
} ,
} ,
} ,
wantErr : true ,
} ,
2023-06-07 17:28:42 +02:00
{
name : "password without user error" ,
req : & session . CreateSessionRequest {
Checks : & session . Checks {
Password : & session . CheckPassword {
Password : "Difficult" ,
} ,
} ,
} ,
wantErr : true ,
} ,
{
name : "passkey without user error" ,
req : & session . CreateSessionRequest {
2023-08-11 18:36:18 +03:00
Challenges : & session . RequestChallenges {
WebAuthN : & session . RequestChallenges_WebAuthN {
2024-09-06 15:47:57 +03:00
Domain : Instance . Domain ,
2023-08-11 18:36:18 +03:00
UserVerificationRequirement : session . UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_REQUIRED ,
} ,
2023-06-07 17:28:42 +02:00
} ,
} ,
wantErr : true ,
} ,
2023-06-27 14:36:07 +02:00
{
name : "passkey without domain (not registered) error" ,
req : & session . CreateSessionRequest {
Checks : & session . Checks {
User : & session . CheckUser {
Search : & session . CheckUser_UserId {
UserId : User . GetUserId ( ) ,
} ,
} ,
} ,
2023-08-11 18:36:18 +03:00
Challenges : & session . RequestChallenges {
WebAuthN : & session . RequestChallenges_WebAuthN {
UserVerificationRequirement : session . UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_REQUIRED ,
} ,
2023-06-27 14:36:07 +02:00
} ,
} ,
wantErr : true ,
} ,
2023-06-07 17:28:42 +02:00
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
2025-07-15 13:38:00 +02:00
got , err := Client . CreateSession ( LoginCTX , tt . req )
2023-06-07 17:28:42 +02:00
if tt . wantErr {
require . Error ( t , err )
return
}
require . NoError ( t , err )
integration . AssertDetails ( t , tt . want , got )
} )
}
}
2024-05-31 00:08:48 +02:00
func TestServer_CreateSession_lock_user ( t * testing . T ) {
// create a separate org so we don't interfere with any other test
2025-09-10 08:00:31 +02:00
org := Instance . CreateOrganization ( IAMOwnerCTX , integration . OrganizationName ( ) , integration . Email ( ) )
2024-05-31 00:08:48 +02:00
userID := org . CreatedAdmins [ 0 ] . GetUserId ( )
2024-09-06 15:47:57 +03:00
Instance . SetUserPassword ( IAMOwnerCTX , userID , integration . UserPassword , false )
2024-05-31 00:08:48 +02:00
// enable password lockout
maxAttempts := 2
ctxOrg := metadata . AppendToOutgoingContext ( IAMOwnerCTX , "x-zitadel-orgid" , org . GetOrganizationId ( ) )
2024-09-06 15:47:57 +03:00
_ , err := Instance . Client . Mgmt . AddCustomLockoutPolicy ( ctxOrg , & mgmt . AddCustomLockoutPolicyRequest {
2024-05-31 00:08:48 +02:00
MaxPasswordAttempts : uint32 ( maxAttempts ) ,
} )
require . NoError ( t , err )
for i := 0 ; i <= maxAttempts ; i ++ {
2025-07-15 13:38:00 +02:00
_ , err := Client . CreateSession ( LoginCTX , & session . CreateSessionRequest {
2024-05-31 00:08:48 +02:00
Checks : & session . Checks {
User : & session . CheckUser {
Search : & session . CheckUser_UserId {
UserId : userID ,
} ,
} ,
Password : & session . CheckPassword {
Password : "invalid" ,
} ,
} ,
} )
assert . Error ( t , err )
statusCode := status . Code ( err )
expectedCode := codes . InvalidArgument
// as soon as we hit the limit the user is locked and following request will
// already deny any check with a precondition failed since the user is locked
if i >= maxAttempts {
expectedCode = codes . FailedPrecondition
}
assert . Equal ( t , expectedCode , statusCode )
}
}
2023-08-11 18:36:18 +03:00
func TestServer_CreateSession_webauthn ( t * testing . T ) {
// create new session with user and request the webauthn challenge
2025-07-15 13:38:00 +02:00
createResp , err := Client . CreateSession ( LoginCTX , & session . CreateSessionRequest {
2023-06-07 17:28:42 +02:00
Checks : & session . Checks {
User : & session . CheckUser {
Search : & session . CheckUser_UserId {
UserId : User . GetUserId ( ) ,
} ,
} ,
} ,
2023-08-11 18:36:18 +03:00
Challenges : & session . RequestChallenges {
WebAuthN : & session . RequestChallenges_WebAuthN {
2024-09-06 15:47:57 +03:00
Domain : Instance . Domain ,
2023-08-11 18:36:18 +03:00
UserVerificationRequirement : session . UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_REQUIRED ,
} ,
2023-06-07 17:28:42 +02:00
} ,
} )
require . NoError ( t , err )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , createResp . GetSessionToken ( ) , createResp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) )
2023-06-07 17:28:42 +02:00
2024-09-06 15:47:57 +03:00
assertionData , err := Instance . WebAuthN . CreateAssertionResponse ( createResp . GetChallenges ( ) . GetWebAuthN ( ) . GetPublicKeyCredentialRequestOptions ( ) , true )
2023-06-07 17:28:42 +02:00
require . NoError ( t , err )
2023-08-11 18:36:18 +03:00
// update the session with webauthn assertion data
2025-07-15 13:38:00 +02:00
updateResp , err := Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
2023-06-07 17:28:42 +02:00
Checks : & session . Checks {
2023-08-11 18:36:18 +03:00
WebAuthN : & session . CheckWebAuthN {
2023-06-07 17:28:42 +02:00
CredentialAssertionData : assertionData ,
} ,
} ,
} )
require . NoError ( t , err )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , updateResp . GetSessionToken ( ) , updateResp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , updateResp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) , wantUserFactor , wantWebAuthNFactorUserVerified )
2023-06-07 17:28:42 +02:00
}
2023-06-21 16:06:18 +02:00
func TestServer_CreateSession_successfulIntent ( t * testing . T ) {
2025-08-07 15:27:01 +02:00
idpID := Instance . AddGenericOAuthProvider ( IAMOwnerCTX , integration . IDPName ( ) ) . GetId ( )
2025-02-20 13:27:20 +01:00
createResp , err := Client . CreateSession ( LoginCTX , & session . CreateSessionRequest {
2023-06-21 16:06:18 +02:00
Checks : & session . Checks {
User : & session . CheckUser {
Search : & session . CheckUser_UserId {
UserId : User . GetUserId ( ) ,
} ,
} ,
} ,
} )
require . NoError ( t , err )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , createResp . GetSessionToken ( ) , createResp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) )
2023-06-21 16:06:18 +02:00
2025-05-02 13:44:24 +02:00
intentID , token , _ , _ , err := sink . SuccessfulOAuthIntent ( Instance . ID ( ) , idpID , "id" , User . GetUserId ( ) , time . Now ( ) . Add ( time . Hour ) )
2025-02-20 13:27:20 +01:00
require . NoError ( t , err )
updateResp , err := Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
2023-06-21 16:06:18 +02:00
Checks : & session . Checks {
2023-08-22 12:05:45 +02:00
IdpIntent : & session . CheckIDPIntent {
IdpIntentId : intentID ,
IdpIntentToken : token ,
2023-06-21 16:06:18 +02:00
} ,
} ,
} )
require . NoError ( t , err )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , updateResp . GetSessionToken ( ) , updateResp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , updateResp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) , wantUserFactor , wantIntentFactor )
2023-06-21 16:06:18 +02:00
}
2024-04-09 19:21:21 +02:00
func TestServer_CreateSession_successfulIntent_instant ( t * testing . T ) {
2025-08-07 15:27:01 +02:00
idpID := Instance . AddGenericOAuthProvider ( IAMOwnerCTX , integration . IDPName ( ) ) . GetId ( )
2024-04-09 19:21:21 +02:00
2025-05-02 13:44:24 +02:00
intentID , token , _ , _ , err := sink . SuccessfulOAuthIntent ( Instance . ID ( ) , idpID , "id" , User . GetUserId ( ) , time . Now ( ) . Add ( time . Hour ) )
2025-02-20 13:27:20 +01:00
require . NoError ( t , err )
2025-07-15 13:38:00 +02:00
createResp , err := Client . CreateSession ( LoginCTX , & session . CreateSessionRequest {
2024-04-09 19:21:21 +02:00
Checks : & session . Checks {
User : & session . CheckUser {
Search : & session . CheckUser_UserId {
UserId : User . GetUserId ( ) ,
} ,
} ,
IdpIntent : & session . CheckIDPIntent {
IdpIntentId : intentID ,
IdpIntentToken : token ,
} ,
} ,
} )
require . NoError ( t , err )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , createResp . GetSessionToken ( ) , createResp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) , wantUserFactor , wantIntentFactor )
2024-04-09 19:21:21 +02:00
}
2023-06-21 16:06:18 +02:00
func TestServer_CreateSession_successfulIntentUnknownUserID ( t * testing . T ) {
2025-08-07 15:27:01 +02:00
idpID := Instance . AddGenericOAuthProvider ( IAMOwnerCTX , integration . IDPName ( ) ) . GetId ( )
2023-06-21 16:06:18 +02:00
2024-05-30 09:06:32 +02:00
// successful intent without known / linked user
idpUserID := "id"
2025-05-02 13:44:24 +02:00
intentID , token , _ , _ , err := sink . SuccessfulOAuthIntent ( Instance . ID ( ) , idpID , idpUserID , "" , time . Now ( ) . Add ( time . Hour ) )
2024-05-30 09:06:32 +02:00
// link the user (with info from intent)
2024-09-06 15:47:57 +03:00
Instance . CreateUserIDPlink ( CTX , User . GetUserId ( ) , idpUserID , idpID , User . GetUserId ( ) )
2024-05-30 09:06:32 +02:00
// session with intent check must now succeed
2025-07-15 13:38:00 +02:00
createResp , err := Client . CreateSession ( LoginCTX , & session . CreateSessionRequest {
2023-06-21 16:06:18 +02:00
Checks : & session . Checks {
User : & session . CheckUser {
Search : & session . CheckUser_UserId {
UserId : User . GetUserId ( ) ,
} ,
} ,
2023-08-22 12:05:45 +02:00
IdpIntent : & session . CheckIDPIntent {
IdpIntentId : intentID ,
IdpIntentToken : token ,
2023-06-21 16:06:18 +02:00
} ,
} ,
} )
require . NoError ( t , err )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , createResp . GetSessionToken ( ) , createResp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) , wantUserFactor , wantIntentFactor )
2023-06-21 16:06:18 +02:00
}
func TestServer_CreateSession_startedIntentFalseToken ( t * testing . T ) {
2025-08-07 15:27:01 +02:00
idpID := Instance . AddGenericOAuthProvider ( IAMOwnerCTX , integration . IDPName ( ) ) . GetId ( )
2023-06-21 16:06:18 +02:00
2025-07-15 13:38:00 +02:00
createResp , err := Client . CreateSession ( LoginCTX , & session . CreateSessionRequest {
2023-06-21 16:06:18 +02:00
Checks : & session . Checks {
User : & session . CheckUser {
Search : & session . CheckUser_UserId {
UserId : User . GetUserId ( ) ,
} ,
} ,
} ,
} )
require . NoError ( t , err )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , createResp . GetSessionToken ( ) , createResp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) )
2023-06-21 16:06:18 +02:00
2025-02-20 13:27:20 +01:00
intent := Instance . CreateIntent ( CTX , idpID )
2025-07-15 13:38:00 +02:00
_ , err = Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
2023-06-21 16:06:18 +02:00
Checks : & session . Checks {
2023-08-22 12:05:45 +02:00
IdpIntent : & session . CheckIDPIntent {
2025-02-20 13:27:20 +01:00
IdpIntentId : intent . GetIdpIntent ( ) . GetIdpIntentId ( ) ,
2023-08-22 12:05:45 +02:00
IdpIntentToken : "false" ,
2023-06-21 16:06:18 +02:00
} ,
} ,
} )
require . Error ( t , err )
}
2025-05-02 13:44:24 +02:00
func TestServer_CreateSession_reuseIntent ( t * testing . T ) {
2025-08-07 15:27:01 +02:00
idpID := Instance . AddGenericOAuthProvider ( IAMOwnerCTX , integration . IDPName ( ) ) . GetId ( )
2025-05-02 13:44:24 +02:00
createResp , err := Client . CreateSession ( LoginCTX , & session . CreateSessionRequest {
Checks : & session . Checks {
User : & session . CheckUser {
Search : & session . CheckUser_UserId {
UserId : User . GetUserId ( ) ,
} ,
} ,
} ,
} )
require . NoError ( t , err )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , createResp . GetSessionToken ( ) , createResp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) )
2025-05-02 13:44:24 +02:00
intentID , token , _ , _ , err := sink . SuccessfulOAuthIntent ( Instance . ID ( ) , idpID , "id" , User . GetUserId ( ) , time . Now ( ) . Add ( time . Hour ) )
require . NoError ( t , err )
updateResp , err := Client . SetSession ( LoginCTX , & session . SetSessionRequest {
SessionId : createResp . GetSessionId ( ) ,
Checks : & session . Checks {
IdpIntent : & session . CheckIDPIntent {
IdpIntentId : intentID ,
IdpIntentToken : token ,
} ,
} ,
} )
require . NoError ( t , err )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , updateResp . GetSessionToken ( ) , updateResp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , updateResp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) , wantUserFactor , wantIntentFactor )
2025-05-02 13:44:24 +02:00
// the reuse of the intent token is not allowed, not even on the same session
session2 , err := Client . SetSession ( LoginCTX , & session . SetSessionRequest {
SessionId : createResp . GetSessionId ( ) ,
Checks : & session . Checks {
IdpIntent : & session . CheckIDPIntent {
IdpIntentId : intentID ,
IdpIntentToken : token ,
} ,
} ,
} )
require . Error ( t , err )
_ = session2
}
func TestServer_CreateSession_expiredIntent ( t * testing . T ) {
2025-08-07 15:27:01 +02:00
idpID := Instance . AddGenericOAuthProvider ( IAMOwnerCTX , integration . IDPName ( ) ) . GetId ( )
2025-05-02 13:44:24 +02:00
createResp , err := Client . CreateSession ( LoginCTX , & session . CreateSessionRequest {
Checks : & session . Checks {
User : & session . CheckUser {
Search : & session . CheckUser_UserId {
UserId : User . GetUserId ( ) ,
} ,
} ,
} ,
} )
require . NoError ( t , err )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , createResp . GetSessionToken ( ) , createResp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) )
2025-05-02 13:44:24 +02:00
intentID , token , _ , _ , err := sink . SuccessfulOAuthIntent ( Instance . ID ( ) , idpID , "id" , User . GetUserId ( ) , time . Now ( ) . Add ( time . Second ) )
require . NoError ( t , err )
// wait for the intent to expire
time . Sleep ( 2 * time . Second )
_ , err = Client . SetSession ( LoginCTX , & session . SetSessionRequest {
SessionId : createResp . GetSessionId ( ) ,
Checks : & session . Checks {
IdpIntent : & session . CheckIDPIntent {
IdpIntentId : intentID ,
IdpIntentToken : token ,
} ,
} ,
} )
require . Error ( t , err )
}
2023-08-15 12:50:42 +03:00
func registerTOTP ( ctx context . Context , t * testing . T , userID string ) ( secret string ) {
2024-09-06 15:47:57 +03:00
resp , err := Instance . Client . UserV2 . RegisterTOTP ( ctx , & user . RegisterTOTPRequest {
2023-08-15 12:50:42 +03:00
UserId : userID ,
} )
require . NoError ( t , err )
secret = resp . GetSecret ( )
code , err := totp . GenerateCode ( secret , time . Now ( ) )
require . NoError ( t , err )
2024-09-06 15:47:57 +03:00
_ , err = Instance . Client . UserV2 . VerifyTOTPRegistration ( ctx , & user . VerifyTOTPRegistrationRequest {
2023-08-15 12:50:42 +03:00
UserId : userID ,
Code : code ,
} )
require . NoError ( t , err )
return secret
}
2023-08-24 11:41:52 +02:00
func registerOTPSMS ( ctx context . Context , t * testing . T , userID string ) {
2024-09-06 15:47:57 +03:00
_ , err := Instance . Client . UserV2 . AddOTPSMS ( ctx , & user . AddOTPSMSRequest {
2023-08-24 11:41:52 +02:00
UserId : userID ,
} )
require . NoError ( t , err )
}
func registerOTPEmail ( ctx context . Context , t * testing . T , userID string ) {
2024-09-06 15:47:57 +03:00
_ , err := Instance . Client . UserV2 . AddOTPEmail ( ctx , & user . AddOTPEmailRequest {
2023-08-24 11:41:52 +02:00
UserId : userID ,
} )
require . NoError ( t , err )
}
2024-05-14 09:20:31 +02:00
func TestServer_SetSession_flow_totp ( t * testing . T ) {
userExisting := createFullUser ( CTX )
// create new, empty session
2025-07-15 13:38:00 +02:00
createResp , err := Client . CreateSession ( LoginCTX , & session . CreateSessionRequest { } )
2024-05-14 09:20:31 +02:00
require . NoError ( t , err )
sessionToken := createResp . GetSessionToken ( )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , sessionToken , createResp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , "" )
2024-05-14 09:20:31 +02:00
t . Run ( "check user" , func ( t * testing . T ) {
2025-07-15 13:38:00 +02:00
resp , err := Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
2024-05-14 09:20:31 +02:00
Checks : & session . Checks {
User : & session . CheckUser {
Search : & session . CheckUser_UserId {
UserId : userExisting . GetUserId ( ) ,
} ,
} ,
} ,
} )
require . NoError ( t , err )
sessionToken = resp . GetSessionToken ( )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , sessionToken , resp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , resp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , userExisting . GetUserId ( ) , wantUserFactor )
2024-05-14 09:20:31 +02:00
} )
t . Run ( "check webauthn, user verified (passkey)" , func ( t * testing . T ) {
2025-07-15 13:38:00 +02:00
resp , err := Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
2024-05-14 09:20:31 +02:00
Challenges : & session . RequestChallenges {
WebAuthN : & session . RequestChallenges_WebAuthN {
2024-09-06 15:47:57 +03:00
Domain : Instance . Domain ,
2024-05-14 09:20:31 +02:00
UserVerificationRequirement : session . UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_REQUIRED ,
} ,
} ,
} )
require . NoError ( t , err )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , resp . GetSessionToken ( ) , resp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , resp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , userExisting . GetUserId ( ) )
2024-05-14 09:20:31 +02:00
sessionToken = resp . GetSessionToken ( )
2024-09-06 15:47:57 +03:00
assertionData , err := Instance . WebAuthN . CreateAssertionResponse ( resp . GetChallenges ( ) . GetWebAuthN ( ) . GetPublicKeyCredentialRequestOptions ( ) , true )
2024-05-14 09:20:31 +02:00
require . NoError ( t , err )
2025-07-15 13:38:00 +02:00
resp , err = Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
2024-05-14 09:20:31 +02:00
Checks : & session . Checks {
WebAuthN : & session . CheckWebAuthN {
CredentialAssertionData : assertionData ,
} ,
} ,
} )
require . NoError ( t , err )
sessionToken = resp . GetSessionToken ( )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , sessionToken , resp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , resp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , userExisting . GetUserId ( ) , wantUserFactor , wantWebAuthNFactorUserVerified )
2024-05-14 09:20:31 +02:00
} )
2024-09-06 15:47:57 +03:00
userAuthCtx := integration . WithAuthorizationToken ( CTX , sessionToken )
Instance . RegisterUserU2F ( userAuthCtx , userExisting . GetUserId ( ) )
2024-05-14 09:20:31 +02:00
totpSecret := registerTOTP ( userAuthCtx , t , userExisting . GetUserId ( ) )
registerOTPSMS ( userAuthCtx , t , userExisting . GetUserId ( ) )
registerOTPEmail ( userAuthCtx , t , userExisting . GetUserId ( ) )
t . Run ( "check TOTP" , func ( t * testing . T ) {
code , err := totp . GenerateCode ( totpSecret , time . Now ( ) )
require . NoError ( t , err )
2025-07-15 13:38:00 +02:00
resp , err := Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
2024-05-14 09:20:31 +02:00
Checks : & session . Checks {
Totp : & session . CheckTOTP {
Code : code ,
} ,
} ,
} )
require . NoError ( t , err )
sessionToken = resp . GetSessionToken ( )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , sessionToken , resp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , resp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , userExisting . GetUserId ( ) , wantUserFactor , wantTOTPFactor )
2024-05-14 09:20:31 +02:00
} )
2024-09-06 15:47:57 +03:00
userImport := Instance . CreateHumanUserWithTOTP ( CTX , totpSecret )
2025-07-15 13:38:00 +02:00
createRespImport , err := Client . CreateSession ( LoginCTX , & session . CreateSessionRequest { } )
2024-05-14 09:20:31 +02:00
require . NoError ( t , err )
sessionTokenImport := createRespImport . GetSessionToken ( )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createRespImport . GetSessionId ( ) , sessionTokenImport , createRespImport . GetDetails ( ) . GetSequence ( ) , createRespImport . GetDetails ( ) . GetChangeDate ( ) , createRespImport . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , "" )
2024-05-14 09:20:31 +02:00
t . Run ( "check user" , func ( t * testing . T ) {
2025-07-15 13:38:00 +02:00
resp , err := Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createRespImport . GetSessionId ( ) ,
2024-05-14 09:20:31 +02:00
Checks : & session . Checks {
User : & session . CheckUser {
Search : & session . CheckUser_UserId {
UserId : userImport . GetUserId ( ) ,
} ,
} ,
} ,
} )
require . NoError ( t , err )
sessionTokenImport = resp . GetSessionToken ( )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createRespImport . GetSessionId ( ) , sessionTokenImport , resp . GetDetails ( ) . GetSequence ( ) , createRespImport . GetDetails ( ) . GetChangeDate ( ) , resp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , userImport . GetUserId ( ) , wantUserFactor )
2024-05-14 09:20:31 +02:00
} )
t . Run ( "check TOTP" , func ( t * testing . T ) {
code , err := totp . GenerateCode ( totpSecret , time . Now ( ) )
require . NoError ( t , err )
2025-07-15 13:38:00 +02:00
resp , err := Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createRespImport . GetSessionId ( ) ,
2024-05-14 09:20:31 +02:00
Checks : & session . Checks {
Totp : & session . CheckTOTP {
Code : code ,
} ,
} ,
} )
require . NoError ( t , err )
sessionTokenImport = resp . GetSessionToken ( )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createRespImport . GetSessionId ( ) , sessionTokenImport , resp . GetDetails ( ) . GetSequence ( ) , createRespImport . GetDetails ( ) . GetChangeDate ( ) , resp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , userImport . GetUserId ( ) , wantUserFactor , wantTOTPFactor )
2024-05-14 09:20:31 +02:00
} )
}
2023-06-07 17:28:42 +02:00
func TestServer_SetSession_flow ( t * testing . T ) {
// create new, empty session
2025-07-15 13:38:00 +02:00
createResp , err := Client . CreateSession ( LoginCTX , & session . CreateSessionRequest { } )
2023-06-07 17:28:42 +02:00
require . NoError ( t , err )
sessionToken := createResp . GetSessionToken ( )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , sessionToken , createResp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) )
2023-06-07 17:28:42 +02:00
t . Run ( "check user" , func ( t * testing . T ) {
2025-07-15 13:38:00 +02:00
resp , err := Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
2023-06-07 17:28:42 +02:00
Checks : & session . Checks {
User : & session . CheckUser {
Search : & session . CheckUser_UserId {
UserId : User . GetUserId ( ) ,
} ,
} ,
} ,
} )
require . NoError ( t , err )
sessionToken = resp . GetSessionToken ( )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , sessionToken , resp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , resp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) , wantUserFactor )
2023-06-07 17:28:42 +02:00
} )
2023-08-11 18:36:18 +03:00
t . Run ( "check webauthn, user verified (passkey)" , func ( t * testing . T ) {
2025-07-15 13:38:00 +02:00
resp , err := Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
2023-08-11 18:36:18 +03:00
Challenges : & session . RequestChallenges {
WebAuthN : & session . RequestChallenges_WebAuthN {
2024-09-06 15:47:57 +03:00
Domain : Instance . Domain ,
2023-08-11 18:36:18 +03:00
UserVerificationRequirement : session . UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_REQUIRED ,
} ,
2023-06-07 17:28:42 +02:00
} ,
} )
require . NoError ( t , err )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , resp . GetSessionToken ( ) , resp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , resp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) )
2023-06-07 17:28:42 +02:00
sessionToken = resp . GetSessionToken ( )
2024-09-06 15:47:57 +03:00
assertionData , err := Instance . WebAuthN . CreateAssertionResponse ( resp . GetChallenges ( ) . GetWebAuthN ( ) . GetPublicKeyCredentialRequestOptions ( ) , true )
2023-06-07 17:28:42 +02:00
require . NoError ( t , err )
2025-07-15 13:38:00 +02:00
resp , err = Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
2023-06-07 17:28:42 +02:00
Checks : & session . Checks {
2023-08-11 18:36:18 +03:00
WebAuthN : & session . CheckWebAuthN {
2023-06-07 17:28:42 +02:00
CredentialAssertionData : assertionData ,
} ,
} ,
} )
require . NoError ( t , err )
2023-08-11 18:36:18 +03:00
sessionToken = resp . GetSessionToken ( )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , sessionToken , resp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , resp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) , wantUserFactor , wantWebAuthNFactorUserVerified )
2023-08-11 18:36:18 +03:00
} )
2024-09-06 15:47:57 +03:00
userAuthCtx := integration . WithAuthorizationToken ( CTX , sessionToken )
Instance . RegisterUserU2F ( userAuthCtx , User . GetUserId ( ) )
2023-08-15 12:50:42 +03:00
totpSecret := registerTOTP ( userAuthCtx , t , User . GetUserId ( ) )
2023-08-24 11:41:52 +02:00
registerOTPSMS ( userAuthCtx , t , User . GetUserId ( ) )
registerOTPEmail ( userAuthCtx , t , User . GetUserId ( ) )
2023-08-15 12:50:42 +03:00
2023-08-11 18:36:18 +03:00
t . Run ( "check webauthn, user not verified (U2F)" , func ( t * testing . T ) {
for _ , userVerificationRequirement := range [ ] session . UserVerificationRequirement {
session . UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_PREFERRED ,
session . UserVerificationRequirement_USER_VERIFICATION_REQUIREMENT_DISCOURAGED ,
} {
t . Run ( userVerificationRequirement . String ( ) , func ( t * testing . T ) {
2025-07-15 13:38:00 +02:00
resp , err := Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
2023-08-11 18:36:18 +03:00
Challenges : & session . RequestChallenges {
WebAuthN : & session . RequestChallenges_WebAuthN {
2024-09-06 15:47:57 +03:00
Domain : Instance . Domain ,
2023-08-11 18:36:18 +03:00
UserVerificationRequirement : userVerificationRequirement ,
} ,
} ,
} )
require . NoError ( t , err )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , resp . GetSessionToken ( ) , resp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , resp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) )
2023-08-11 18:36:18 +03:00
sessionToken = resp . GetSessionToken ( )
2024-09-06 15:47:57 +03:00
assertionData , err := Instance . WebAuthN . CreateAssertionResponse ( resp . GetChallenges ( ) . GetWebAuthN ( ) . GetPublicKeyCredentialRequestOptions ( ) , false )
2023-08-11 18:36:18 +03:00
require . NoError ( t , err )
2025-07-15 13:38:00 +02:00
resp , err = Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
2023-08-11 18:36:18 +03:00
Checks : & session . Checks {
WebAuthN : & session . CheckWebAuthN {
CredentialAssertionData : assertionData ,
} ,
} ,
} )
require . NoError ( t , err )
sessionToken = resp . GetSessionToken ( )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , sessionToken , resp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , resp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) , wantUserFactor , wantWebAuthNFactor )
2023-08-11 18:36:18 +03:00
} )
}
2023-06-07 17:28:42 +02:00
} )
2023-08-15 12:50:42 +03:00
t . Run ( "check TOTP" , func ( t * testing . T ) {
code , err := totp . GenerateCode ( totpSecret , time . Now ( ) )
require . NoError ( t , err )
2025-07-15 13:38:00 +02:00
resp , err := Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
2023-08-15 12:50:42 +03:00
Checks : & session . Checks {
Totp : & session . CheckTOTP {
2023-08-31 09:06:50 +02:00
Code : code ,
2023-08-15 12:50:42 +03:00
} ,
} ,
} )
require . NoError ( t , err )
sessionToken = resp . GetSessionToken ( )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , sessionToken , resp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , resp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) , wantUserFactor , wantWebAuthNFactor , wantTOTPFactor )
2023-08-15 12:50:42 +03:00
} )
2023-08-24 11:41:52 +02:00
t . Run ( "check OTP SMS" , func ( t * testing . T ) {
2025-07-15 13:38:00 +02:00
resp , err := Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
2023-08-24 11:41:52 +02:00
Challenges : & session . RequestChallenges {
OtpSms : & session . RequestChallenges_OTPSMS { ReturnCode : true } ,
} ,
} )
require . NoError ( t , err )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , resp . GetSessionToken ( ) , resp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , resp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) )
2023-08-24 11:41:52 +02:00
sessionToken = resp . GetSessionToken ( )
otp := resp . GetChallenges ( ) . GetOtpSms ( )
require . NotEmpty ( t , otp )
2025-07-15 13:38:00 +02:00
resp , err = Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
2023-08-24 11:41:52 +02:00
Checks : & session . Checks {
OtpSms : & session . CheckOTP {
2023-08-31 09:06:50 +02:00
Code : otp ,
2023-08-24 11:41:52 +02:00
} ,
} ,
} )
require . NoError ( t , err )
sessionToken = resp . GetSessionToken ( )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , sessionToken , resp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , resp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) , wantUserFactor , wantWebAuthNFactor , wantOTPSMSFactor )
2023-08-24 11:41:52 +02:00
} )
t . Run ( "check OTP Email" , func ( t * testing . T ) {
2025-07-15 13:38:00 +02:00
resp , err := Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
2023-08-24 11:41:52 +02:00
Challenges : & session . RequestChallenges {
OtpEmail : & session . RequestChallenges_OTPEmail {
DeliveryType : & session . RequestChallenges_OTPEmail_ReturnCode_ { } ,
} ,
} ,
} )
require . NoError ( t , err )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , resp . GetSessionToken ( ) , resp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , resp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) )
2023-08-24 11:41:52 +02:00
sessionToken = resp . GetSessionToken ( )
otp := resp . GetChallenges ( ) . GetOtpEmail ( )
require . NotEmpty ( t , otp )
2025-07-15 13:38:00 +02:00
resp , err = Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
2023-08-24 11:41:52 +02:00
Checks : & session . Checks {
OtpEmail : & session . CheckOTP {
2023-08-31 09:06:50 +02:00
Code : otp ,
2023-08-24 11:41:52 +02:00
} ,
} ,
} )
require . NoError ( t , err )
sessionToken = resp . GetSessionToken ( )
2025-09-25 14:30:10 +02:00
verifyCurrentSession ( t , createResp . GetSessionId ( ) , sessionToken , resp . GetDetails ( ) . GetSequence ( ) , createResp . GetDetails ( ) . GetChangeDate ( ) , resp . GetDetails ( ) . GetChangeDate ( ) , nil , nil , 0 , User . GetUserId ( ) , wantUserFactor , wantWebAuthNFactor , wantOTPEmailFactor )
2023-08-24 11:41:52 +02:00
} )
2023-06-07 17:28:42 +02:00
}
2023-07-14 13:16:16 +02:00
2023-11-06 11:48:28 +02:00
func TestServer_SetSession_expired ( t * testing . T ) {
2025-07-15 13:38:00 +02:00
createResp , err := Client . CreateSession ( LoginCTX , & session . CreateSessionRequest {
2023-11-06 11:48:28 +02:00
Lifetime : durationpb . New ( 20 * time . Second ) ,
} )
require . NoError ( t , err )
// test session token works
2025-07-15 13:38:00 +02:00
_ , err = Instance . Client . SessionV2 . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
Lifetime : durationpb . New ( 20 * time . Second ) ,
2023-11-06 11:48:28 +02:00
} )
require . NoError ( t , err )
// ensure session expires and does not work anymore
time . Sleep ( 20 * time . Second )
2025-07-15 13:38:00 +02:00
_ , err = Client . SetSession ( LoginCTX , & session . SetSessionRequest {
2024-05-22 07:56:11 +02:00
SessionId : createResp . GetSessionId ( ) ,
Lifetime : durationpb . New ( 20 * time . Second ) ,
2023-11-06 11:48:28 +02:00
} )
require . Error ( t , err )
}
2023-11-16 08:35:50 +02:00
func TestServer_DeleteSession_token ( t * testing . T ) {
2025-07-15 13:38:00 +02:00
createResp , err := Client . CreateSession ( LoginCTX , & session . CreateSessionRequest { } )
2023-11-16 08:35:50 +02:00
require . NoError ( t , err )
_ , err = Client . DeleteSession ( CTX , & session . DeleteSessionRequest {
SessionId : createResp . GetSessionId ( ) ,
SessionToken : gu . Ptr ( "invalid" ) ,
} )
require . Error ( t , err )
_ , err = Client . DeleteSession ( CTX , & session . DeleteSessionRequest {
SessionId : createResp . GetSessionId ( ) ,
SessionToken : gu . Ptr ( createResp . GetSessionToken ( ) ) ,
} )
require . NoError ( t , err )
}
func TestServer_DeleteSession_own_session ( t * testing . T ) {
// create two users for the test and a session each to get tokens for authorization
2024-09-06 15:47:57 +03:00
user1 := Instance . CreateHumanUser ( CTX )
Instance . SetUserPassword ( CTX , user1 . GetUserId ( ) , integration . UserPassword , false )
2025-07-15 13:38:00 +02:00
_ , token1 , _ , _ := Instance . CreatePasswordSession ( t , LoginCTX , user1 . GetUserId ( ) , integration . UserPassword )
2023-11-16 08:35:50 +02:00
2024-09-06 15:47:57 +03:00
user2 := Instance . CreateHumanUser ( CTX )
Instance . SetUserPassword ( CTX , user2 . GetUserId ( ) , integration . UserPassword , false )
2025-07-15 13:38:00 +02:00
_ , token2 , _ , _ := Instance . CreatePasswordSession ( t , LoginCTX , user2 . GetUserId ( ) , integration . UserPassword )
2023-11-16 08:35:50 +02:00
// create a new session for the first user
2025-07-15 13:38:00 +02:00
createResp , err := Client . CreateSession ( LoginCTX , & session . CreateSessionRequest {
2023-11-16 08:35:50 +02:00
Checks : & session . Checks {
User : & session . CheckUser {
Search : & session . CheckUser_UserId {
UserId : user1 . GetUserId ( ) ,
} ,
} ,
} ,
} )
require . NoError ( t , err )
// delete the new (user1) session must not be possible with user (has no permission)
2024-09-06 15:47:57 +03:00
_ , err = Client . DeleteSession ( integration . WithAuthorizationToken ( context . Background ( ) , token2 ) , & session . DeleteSessionRequest {
2023-11-16 08:35:50 +02:00
SessionId : createResp . GetSessionId ( ) ,
} )
require . Error ( t , err )
2024-02-26 14:11:09 +01:00
// delete the new (user1) session by themselves
2024-09-06 15:47:57 +03:00
_ , err = Client . DeleteSession ( integration . WithAuthorizationToken ( context . Background ( ) , token1 ) , & session . DeleteSessionRequest {
2023-11-16 08:35:50 +02:00
SessionId : createResp . GetSessionId ( ) ,
} )
require . NoError ( t , err )
}
func TestServer_DeleteSession_with_permission ( t * testing . T ) {
2025-07-15 13:38:00 +02:00
createResp , err := Client . CreateSession ( LoginCTX , & session . CreateSessionRequest {
2023-11-16 08:35:50 +02:00
Checks : & session . Checks {
User : & session . CheckUser {
Search : & session . CheckUser_UserId {
UserId : User . GetUserId ( ) ,
} ,
} ,
} ,
} )
require . NoError ( t , err )
// delete the new session by ORG_OWNER
2024-09-06 15:47:57 +03:00
_ , err = Client . DeleteSession ( Instance . WithAuthorization ( context . Background ( ) , integration . UserTypeOrgOwner ) , & session . DeleteSessionRequest {
2023-11-16 08:35:50 +02:00
SessionId : createResp . GetSessionId ( ) ,
} )
require . NoError ( t , err )
}
2025-08-07 14:58:59 +02:00
func TestServer_DeleteSession_expired ( t * testing . T ) {
createResp , err := Client . CreateSession ( LoginCTX , & session . CreateSessionRequest {
Lifetime : durationpb . New ( 5 * time . Second ) ,
} )
require . NoError ( t , err )
// wait until the token expires
time . Sleep ( 10 * time . Second )
_ , err = Client . DeleteSession ( Instance . WithAuthorizationToken ( context . Background ( ) , integration . UserTypeOrgOwner ) , & session . DeleteSessionRequest {
SessionId : createResp . GetSessionId ( ) ,
SessionToken : gu . Ptr ( createResp . GetSessionToken ( ) ) ,
} )
require . NoError ( t , err )
// get session should return an error
sessionResp , err := Client . GetSession ( Instance . WithAuthorizationToken ( context . Background ( ) , integration . UserTypeOrgOwner ) ,
& session . GetSessionRequest { SessionId : createResp . GetSessionId ( ) } )
require . Error ( t , err )
require . Nil ( t , sessionResp )
}
2023-07-14 13:16:16 +02:00
func Test_ZITADEL_API_missing_authentication ( t * testing . T ) {
// create new, empty session
2025-07-15 13:38:00 +02:00
createResp , err := Client . CreateSession ( LoginCTX , & session . CreateSessionRequest { } )
2023-07-14 13:16:16 +02:00
require . NoError ( t , err )
ctx := metadata . AppendToOutgoingContext ( context . Background ( ) , "Authorization" , fmt . Sprintf ( "Bearer %s" , createResp . GetSessionToken ( ) ) )
2025-01-14 14:15:59 +01:00
retryDuration , tick := integration . WaitForAndTickWithMaxDuration ( ctx , time . Minute )
require . EventuallyWithT ( t , func ( tt * assert . CollectT ) {
sessionResp , err := Client . GetSession ( ctx , & session . GetSessionRequest { SessionId : createResp . GetSessionId ( ) } )
if ! assert . Error ( tt , err ) {
return
}
assert . Nil ( tt , sessionResp )
} , retryDuration , tick )
2023-07-14 13:16:16 +02:00
}
func Test_ZITADEL_API_success ( t * testing . T ) {
2025-07-15 13:38:00 +02:00
id , token , _ , _ := Instance . CreateVerifiedWebAuthNSession ( t , LoginCTX , User . GetUserId ( ) )
2024-09-06 15:47:57 +03:00
ctx := integration . WithAuthorizationToken ( context . Background ( ) , token )
2023-08-11 18:36:18 +03:00
2025-01-14 14:15:59 +01:00
retryDuration , tick := integration . WaitForAndTickWithMaxDuration ( ctx , time . Minute )
require . EventuallyWithT ( t , func ( tt * assert . CollectT ) {
sessionResp , err := Client . GetSession ( ctx , & session . GetSessionRequest { SessionId : id } )
if ! assert . NoError ( tt , err ) {
return
}
webAuthN := sessionResp . GetSession ( ) . GetFactors ( ) . GetWebAuthN ( )
assert . NotNil ( tt , id , webAuthN . GetVerifiedAt ( ) . AsTime ( ) )
assert . True ( tt , webAuthN . GetUserVerified ( ) )
} , retryDuration , tick )
2023-07-14 13:16:16 +02:00
}
func Test_ZITADEL_API_session_not_found ( t * testing . T ) {
2025-07-15 13:38:00 +02:00
id , token , _ , _ := Instance . CreateVerifiedWebAuthNSession ( t , LoginCTX , User . GetUserId ( ) )
2023-07-14 13:16:16 +02:00
// test session token works
2024-09-06 15:47:57 +03:00
ctx := integration . WithAuthorizationToken ( context . Background ( ) , token )
2025-01-14 14:15:59 +01:00
retryDuration , tick := integration . WaitForAndTickWithMaxDuration ( ctx , time . Minute )
require . EventuallyWithT ( t , func ( tt * assert . CollectT ) {
_ , err := Client . GetSession ( ctx , & session . GetSessionRequest { SessionId : id } )
if ! assert . NoError ( tt , err ) {
return
}
} , retryDuration , tick )
2023-07-14 13:16:16 +02:00
//terminate the session and test it does not work anymore
2025-01-14 14:15:59 +01:00
_ , err := Client . DeleteSession ( CTX , & session . DeleteSessionRequest {
2023-07-14 13:16:16 +02:00
SessionId : id ,
SessionToken : gu . Ptr ( token ) ,
} )
require . NoError ( t , err )
2025-01-14 14:15:59 +01:00
2024-09-06 15:47:57 +03:00
ctx = integration . WithAuthorizationToken ( context . Background ( ) , token )
2025-01-14 14:15:59 +01:00
retryDuration , tick = integration . WaitForAndTickWithMaxDuration ( ctx , time . Minute )
require . EventuallyWithT ( t , func ( tt * assert . CollectT ) {
_ , err := Client . GetSession ( ctx , & session . GetSessionRequest { SessionId : id } )
if ! assert . Error ( tt , err ) {
return
}
} , retryDuration , tick )
2023-07-14 13:16:16 +02:00
}
2023-11-06 11:48:28 +02:00
func Test_ZITADEL_API_session_expired ( t * testing . T ) {
2025-07-15 13:38:00 +02:00
id , token , _ , _ := Instance . CreateVerifiedWebAuthNSessionWithLifetime ( t , LoginCTX , User . GetUserId ( ) , 20 * time . Second )
2023-11-06 11:48:28 +02:00
// test session token works
2024-09-06 15:47:57 +03:00
ctx := integration . WithAuthorizationToken ( context . Background ( ) , token )
2025-01-14 14:15:59 +01:00
retryDuration , tick := integration . WaitForAndTickWithMaxDuration ( ctx , time . Minute )
require . EventuallyWithT ( t , func ( tt * assert . CollectT ) {
_ , err := Client . GetSession ( ctx , & session . GetSessionRequest { SessionId : id } )
if ! assert . NoError ( tt , err ) {
return
}
} , retryDuration , tick )
2023-11-06 11:48:28 +02:00
// ensure session expires and does not work anymore
time . Sleep ( 20 * time . Second )
2024-07-26 22:39:55 +02:00
sessionResp , err := Client . GetSession ( ctx , & session . GetSessionRequest { SessionId : id } )
2023-11-06 11:48:28 +02:00
require . Error ( t , err )
require . Nil ( t , sessionResp )
}