mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 20:57:31 +00:00
feat(oidc): token exchange impersonation (#7516)
* add token exchange feature flag * allow setting reason and actor to access tokens * impersonation * set token types and scopes in response * upgrade oidc to working draft state * fix tests * audience and scope validation * id toke and jwt as input * return id tokens * add grant type token exchange to app config * add integration tests * check and deny actors in api calls * fix instance setting tests by triggering projection on write and cleanup * insert sleep statements again * solve linting issues * add translations * pin oidc v3.15.0 * resolve comments, add event translation * fix refreshtoken test * use ValidateAuthReqScopes from oidc * apparently the linter can't make up its mind * persist actor thru refresh tokens and check in tests * remove unneeded triggers
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||
"go.uber.org/mock/gomock"
|
||||
|
||||
@@ -40,6 +41,8 @@ func TestCommands_AddAccessAndRefreshToken(t *testing.T) {
|
||||
authTime time.Time
|
||||
refreshIdleExpiration time.Duration
|
||||
refreshExpiration time.Duration
|
||||
reason domain.TokenReason
|
||||
actor *domain.TokenActor
|
||||
}
|
||||
type res struct {
|
||||
token *domain.Token
|
||||
@@ -135,6 +138,7 @@ func TestCommands_AddAccessAndRefreshToken(t *testing.T) {
|
||||
time.Now(),
|
||||
1*time.Hour,
|
||||
24*time.Hour,
|
||||
nil,
|
||||
)),
|
||||
eventFromEventPusher(user.NewHumanRefreshTokenRemovedEvent(
|
||||
context.Background(),
|
||||
@@ -173,6 +177,7 @@ func TestCommands_AddAccessAndRefreshToken(t *testing.T) {
|
||||
time.Now(),
|
||||
-1*time.Hour,
|
||||
24*time.Hour,
|
||||
nil,
|
||||
)),
|
||||
),
|
||||
),
|
||||
@@ -292,7 +297,7 @@ func TestCommands_AddAccessAndRefreshToken(t *testing.T) {
|
||||
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,
|
||||
tt.args.audience, tt.args.scopes, tt.args.authMethodsReferences, tt.args.lifetime, tt.args.refreshIdleExpiration, tt.args.refreshExpiration, tt.args.authTime)
|
||||
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)
|
||||
}
|
||||
@@ -372,6 +377,7 @@ func TestCommands_RevokeRefreshToken(t *testing.T) {
|
||||
time.Now(),
|
||||
1*time.Hour,
|
||||
10*time.Hour,
|
||||
nil,
|
||||
)),
|
||||
),
|
||||
expectPushFailed(zerrors.ThrowInternal(nil, "ERROR", "internal"),
|
||||
@@ -411,6 +417,7 @@ func TestCommands_RevokeRefreshToken(t *testing.T) {
|
||||
time.Now(),
|
||||
1*time.Hour,
|
||||
10*time.Hour,
|
||||
nil,
|
||||
)),
|
||||
),
|
||||
expectPush(
|
||||
@@ -506,6 +513,7 @@ func TestCommands_RevokeRefreshTokens(t *testing.T) {
|
||||
time.Now(),
|
||||
1*time.Hour,
|
||||
10*time.Hour,
|
||||
nil,
|
||||
)),
|
||||
),
|
||||
expectFilter(),
|
||||
@@ -539,6 +547,7 @@ func TestCommands_RevokeRefreshTokens(t *testing.T) {
|
||||
time.Now(),
|
||||
1*time.Hour,
|
||||
10*time.Hour,
|
||||
nil,
|
||||
)),
|
||||
),
|
||||
expectFilter(
|
||||
@@ -555,6 +564,7 @@ func TestCommands_RevokeRefreshTokens(t *testing.T) {
|
||||
time.Now(),
|
||||
1*time.Hour,
|
||||
10*time.Hour,
|
||||
nil,
|
||||
)),
|
||||
),
|
||||
expectPushFailed(zerrors.ThrowInternal(nil, "ERROR", "internal"),
|
||||
@@ -599,6 +609,7 @@ func TestCommands_RevokeRefreshTokens(t *testing.T) {
|
||||
time.Now(),
|
||||
1*time.Hour,
|
||||
10*time.Hour,
|
||||
nil,
|
||||
)),
|
||||
),
|
||||
expectFilter(
|
||||
@@ -615,6 +626,7 @@ func TestCommands_RevokeRefreshTokens(t *testing.T) {
|
||||
time.Now(),
|
||||
1*time.Hour,
|
||||
10*time.Hour,
|
||||
nil,
|
||||
)),
|
||||
),
|
||||
expectPush(
|
||||
@@ -691,6 +703,7 @@ func TestCommands_addRefreshToken(t *testing.T) {
|
||||
authTime time.Time
|
||||
idleExpiration time.Duration
|
||||
expiration time.Duration
|
||||
actor *domain.TokenActor
|
||||
}
|
||||
type res struct {
|
||||
event *user.HumanRefreshTokenAddedEvent
|
||||
@@ -745,6 +758,7 @@ func TestCommands_addRefreshToken(t *testing.T) {
|
||||
authTime,
|
||||
1*time.Hour,
|
||||
10*time.Hour,
|
||||
nil,
|
||||
),
|
||||
refreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID:refreshTokenID:refreshTokenID")),
|
||||
},
|
||||
@@ -756,7 +770,7 @@ func TestCommands_addRefreshToken(t *testing.T) {
|
||||
eventstore: tt.fields.eventstore,
|
||||
keyAlgorithm: tt.fields.keyAlgorithm,
|
||||
}
|
||||
gotEvent, gotRefreshToken, err := c.addRefreshToken(tt.args.ctx, tt.args.accessToken, tt.args.authMethodsReferences, tt.args.authTime, tt.args.idleExpiration, tt.args.expiration)
|
||||
gotEvent, gotRefreshToken, err := c.addRefreshToken(tt.args.ctx, tt.args.accessToken, tt.args.authMethodsReferences, tt.args.authTime, tt.args.idleExpiration, tt.args.expiration, tt.args.actor)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -788,13 +802,13 @@ func TestCommands_renewRefreshToken(t *testing.T) {
|
||||
event *user.HumanRefreshTokenRenewedEvent
|
||||
refreshTokenID string
|
||||
newRefreshToken string
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *renewedRefreshToken
|
||||
wantErr func(error) bool
|
||||
}{
|
||||
{
|
||||
name: "empty token, error",
|
||||
@@ -804,9 +818,7 @@ func TestCommands_renewRefreshToken(t *testing.T) {
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
},
|
||||
res: res{
|
||||
err: zerrors.IsErrorInvalidArgument,
|
||||
},
|
||||
wantErr: zerrors.IsErrorInvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "invalid token, error",
|
||||
@@ -818,9 +830,7 @@ func TestCommands_renewRefreshToken(t *testing.T) {
|
||||
ctx: context.Background(),
|
||||
refreshToken: "invalid",
|
||||
},
|
||||
res: res{
|
||||
err: zerrors.IsErrorInvalidArgument,
|
||||
},
|
||||
wantErr: zerrors.IsErrorInvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "invalid token (invalid userID), error",
|
||||
@@ -834,9 +844,7 @@ func TestCommands_renewRefreshToken(t *testing.T) {
|
||||
orgID: "orgID",
|
||||
refreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID2:tokenID:token")),
|
||||
},
|
||||
res: res{
|
||||
err: zerrors.IsErrorInvalidArgument,
|
||||
},
|
||||
wantErr: zerrors.IsErrorInvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "token inactive, error",
|
||||
@@ -856,6 +864,7 @@ func TestCommands_renewRefreshToken(t *testing.T) {
|
||||
time.Now(),
|
||||
1*time.Hour,
|
||||
24*time.Hour,
|
||||
nil,
|
||||
)),
|
||||
eventFromEventPusher(user.NewHumanRefreshTokenRemovedEvent(
|
||||
context.Background(),
|
||||
@@ -872,9 +881,7 @@ func TestCommands_renewRefreshToken(t *testing.T) {
|
||||
orgID: "orgID",
|
||||
refreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID:tokenID:token")),
|
||||
},
|
||||
res: res{
|
||||
err: zerrors.IsErrorInvalidArgument,
|
||||
},
|
||||
wantErr: zerrors.IsErrorInvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "token expired, error",
|
||||
@@ -894,6 +901,7 @@ func TestCommands_renewRefreshToken(t *testing.T) {
|
||||
time.Now(),
|
||||
1*time.Hour,
|
||||
24*time.Hour,
|
||||
nil,
|
||||
)),
|
||||
),
|
||||
),
|
||||
@@ -905,9 +913,7 @@ func TestCommands_renewRefreshToken(t *testing.T) {
|
||||
orgID: "orgID",
|
||||
refreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID:tokenID:tokenID")),
|
||||
},
|
||||
res: res{
|
||||
err: zerrors.IsErrorInvalidArgument,
|
||||
},
|
||||
wantErr: zerrors.IsErrorInvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "user deactivated, error",
|
||||
@@ -927,6 +933,7 @@ func TestCommands_renewRefreshToken(t *testing.T) {
|
||||
time.Now(),
|
||||
1*time.Hour,
|
||||
24*time.Hour,
|
||||
nil,
|
||||
)),
|
||||
eventFromEventPusher(
|
||||
user.NewUserDeactivatedEvent(
|
||||
@@ -945,9 +952,7 @@ func TestCommands_renewRefreshToken(t *testing.T) {
|
||||
refreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID:tokenID:tokenID")),
|
||||
idleExpiration: 1 * time.Hour,
|
||||
},
|
||||
res: res{
|
||||
err: zerrors.IsErrorInvalidArgument,
|
||||
},
|
||||
wantErr: zerrors.IsErrorInvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "user signedout, error",
|
||||
@@ -967,6 +972,7 @@ func TestCommands_renewRefreshToken(t *testing.T) {
|
||||
time.Now(),
|
||||
1*time.Hour,
|
||||
24*time.Hour,
|
||||
nil,
|
||||
)),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanSignedOutEvent(
|
||||
@@ -986,9 +992,7 @@ func TestCommands_renewRefreshToken(t *testing.T) {
|
||||
refreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID:tokenID:tokenID")),
|
||||
idleExpiration: 1 * time.Hour,
|
||||
},
|
||||
res: res{
|
||||
err: zerrors.IsErrorInvalidArgument,
|
||||
},
|
||||
wantErr: zerrors.IsErrorInvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "token renewed, ok",
|
||||
@@ -1008,6 +1012,7 @@ func TestCommands_renewRefreshToken(t *testing.T) {
|
||||
time.Now(),
|
||||
1*time.Hour,
|
||||
24*time.Hour,
|
||||
nil,
|
||||
)),
|
||||
),
|
||||
),
|
||||
@@ -1021,7 +1026,7 @@ func TestCommands_renewRefreshToken(t *testing.T) {
|
||||
refreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID:tokenID:tokenID")),
|
||||
idleExpiration: 1 * time.Hour,
|
||||
},
|
||||
res: res{
|
||||
want: &renewedRefreshToken{
|
||||
event: user.NewHumanRefreshTokenRenewedEvent(
|
||||
context.Background(),
|
||||
&user.NewAggregate("userID", "orgID").Aggregate,
|
||||
@@ -1029,8 +1034,9 @@ func TestCommands_renewRefreshToken(t *testing.T) {
|
||||
"refreshToken1",
|
||||
1*time.Hour,
|
||||
),
|
||||
refreshTokenID: "tokenID",
|
||||
newRefreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID:tokenID:refreshToken1")),
|
||||
authMethodsReferences: []string{"password"},
|
||||
tokenID: "tokenID",
|
||||
token: base64.RawURLEncoding.EncodeToString([]byte("userID:tokenID:refreshToken1")),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1041,17 +1047,16 @@ func TestCommands_renewRefreshToken(t *testing.T) {
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
keyAlgorithm: tt.fields.keyAlgorithm,
|
||||
}
|
||||
gotEvent, gotRefreshTokenID, gotNewRefreshToken, err := c.renewRefreshToken(tt.args.ctx, tt.args.userID, tt.args.orgID, tt.args.refreshToken, tt.args.idleExpiration)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
if tt.res.err != nil && !tt.res.err(err) {
|
||||
got, err := c.renewRefreshToken(tt.args.ctx, tt.args.userID, tt.args.orgID, tt.args.refreshToken, tt.args.idleExpiration)
|
||||
if tt.wantErr != nil && !tt.wantErr(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
if tt.res.err == nil {
|
||||
assert.Equal(t, tt.res.event, gotEvent)
|
||||
assert.Equal(t, tt.res.refreshTokenID, gotRefreshTokenID)
|
||||
assert.Equal(t, tt.res.newRefreshToken, gotNewRefreshToken)
|
||||
if tt.wantErr == nil {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want.event, got.event)
|
||||
assert.Equal(t, tt.want.authMethodsReferences, got.authMethodsReferences)
|
||||
assert.Equal(t, tt.want.tokenID, got.tokenID)
|
||||
assert.Equal(t, tt.want.token, got.token)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user