feat: token revocation and OP certification (#2594)

* fix: try using only user session if no user is set (id_token_hint) on prompt none

* fix caos errors As implementation

* implement request mode

* return explicit error on invalid refresh token use

* begin token revocation

* token revocation

* tests

* tests

* cleanup

* set op config

* add revocation endpoint to config

* add revocation endpoint to config

* migration version

* error handling in token revocation

* migration version

* update oidc lib to 1.0.0
This commit is contained in:
Livio Amstutz
2021-11-03 08:35:24 +01:00
committed by GitHub
parent 8df5614e4d
commit fc6154cffc
25 changed files with 638 additions and 236 deletions

View File

@@ -9,7 +9,6 @@ import (
"github.com/caos/oidc/pkg/oidc"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"golang.org/x/text/language"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
@@ -56,15 +55,13 @@ func TestCommands_AddAccessAndRefreshToken(t *testing.T) {
res res
}{
{
name: "error access token, error",
name: "missing ID, error",
fields: fields{
eventstore: eventstoreExpect(t,
expectFilter(),
),
eventstore: eventstoreExpect(t),
},
args: args{},
res: res{
err: caos_errs.IsNotFound,
err: caos_errs.IsErrorInvalidArgument,
},
},
{
@@ -73,8 +70,15 @@ func TestCommands_AddAccessAndRefreshToken(t *testing.T) {
eventstore: eventstoreExpect(t,
expectFilter(),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "refreshTokenID1"),
},
args: args{
ctx: context.Background(),
orgID: "orgID",
agentID: "agentID",
userID: "userID",
clientID: "clientID",
},
args: args{},
res: res{
err: caos_errs.IsNotFound,
},
@@ -82,39 +86,7 @@ func TestCommands_AddAccessAndRefreshToken(t *testing.T) {
{
name: "renew refresh token, invalid token, error",
fields: fields{
eventstore: eventstoreExpect(t,
expectFilter(
eventFromEventPusher(user.NewHumanAddedEvent(
context.Background(),
&user.NewAggregate("userID", "orgID").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"email",
true,
)),
),
expectFilter(
eventFromEventPusher(user.NewHumanAddedEvent(
context.Background(),
&user.NewAggregate("userID", "orgID").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"email",
true,
)),
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "accessTokenID1"),
eventstore: eventstoreExpect(t),
keyAlgorithm: refreshTokenEncryptionAlgorithm(gomock.NewController(t)),
},
args: args{
@@ -128,39 +100,7 @@ func TestCommands_AddAccessAndRefreshToken(t *testing.T) {
{
name: "renew refresh token, invalid token (invalid userID), error",
fields: fields{
eventstore: eventstoreExpect(t,
expectFilter(
eventFromEventPusher(user.NewHumanAddedEvent(
context.Background(),
&user.NewAggregate("userID", "orgID").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"email",
true,
)),
),
expectFilter(
eventFromEventPusher(user.NewHumanAddedEvent(
context.Background(),
&user.NewAggregate("userID", "orgID").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"email",
true,
)),
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "accessTokenID1"),
eventstore: eventstoreExpect(t),
keyAlgorithm: refreshTokenEncryptionAlgorithm(gomock.NewController(t)),
},
args: args{
@@ -177,36 +117,6 @@ func TestCommands_AddAccessAndRefreshToken(t *testing.T) {
name: "renew refresh token, token inactive, error",
fields: fields{
eventstore: eventstoreExpect(t,
expectFilter(
eventFromEventPusher(user.NewHumanAddedEvent(
context.Background(),
&user.NewAggregate("userID", "orgID").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"email",
true,
)),
),
expectFilter(
eventFromEventPusher(user.NewHumanAddedEvent(
context.Background(),
&user.NewAggregate("userID", "orgID").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"email",
true,
)),
),
expectFilter(
eventFromEventPusher(user.NewHumanRefreshTokenAddedEvent(
context.Background(),
@@ -229,7 +139,6 @@ func TestCommands_AddAccessAndRefreshToken(t *testing.T) {
)),
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "accessTokenID1"),
keyAlgorithm: refreshTokenEncryptionAlgorithm(gomock.NewController(t)),
},
args: args{
@@ -246,36 +155,6 @@ func TestCommands_AddAccessAndRefreshToken(t *testing.T) {
name: "renew refresh token, token expired, error",
fields: fields{
eventstore: eventstoreExpect(t,
expectFilter(
eventFromEventPusher(user.NewHumanAddedEvent(
context.Background(),
&user.NewAggregate("userID", "orgID").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"email",
true,
)),
),
expectFilter(
eventFromEventPusher(user.NewHumanAddedEvent(
context.Background(),
&user.NewAggregate("userID", "orgID").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"email",
true,
)),
),
expectFilter(
eventFromEventPusher(user.NewHumanRefreshTokenAddedEvent(
context.Background(),
@@ -293,7 +172,6 @@ func TestCommands_AddAccessAndRefreshToken(t *testing.T) {
)),
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "accessTokenID1"),
keyAlgorithm: refreshTokenEncryptionAlgorithm(gomock.NewController(t)),
},
args: args{
@@ -809,7 +687,6 @@ func TestCommands_addRefreshToken(t *testing.T) {
authTime := time.Now().Add(-1 * time.Hour)
type fields struct {
eventstore *eventstore.Eventstore
idGenerator id.Generator
keyAlgorithm crypto.EncryptionAlgorithm
}
type args struct {
@@ -836,7 +713,6 @@ func TestCommands_addRefreshToken(t *testing.T) {
name: "add refresh Token",
fields: fields{
eventstore: eventstoreExpect(t),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "refreshTokenID"),
keyAlgorithm: refreshTokenEncryptionAlgorithm(gomock.NewController(t)),
},
args: args{
@@ -849,6 +725,7 @@ func TestCommands_addRefreshToken(t *testing.T) {
TokenID: "accessTokenID1",
ApplicationID: "clientID",
UserAgentID: "agentID",
RefreshTokenID: "refreshTokenID",
Audience: []string{"clientID1"},
Expiration: time.Now().Add(5 * time.Minute),
Scopes: []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess},
@@ -882,7 +759,6 @@ func TestCommands_addRefreshToken(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
c := &Commands{
eventstore: tt.fields.eventstore,
idGenerator: tt.fields.idGenerator,
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)
@@ -915,6 +791,7 @@ func TestCommands_renewRefreshToken(t *testing.T) {
}
type res struct {
event *user.HumanRefreshTokenRenewedEvent
refreshTokenID string
newRefreshToken string
err func(error) bool
}
@@ -1076,6 +953,7 @@ func TestCommands_renewRefreshToken(t *testing.T) {
"refreshToken1",
1*time.Hour,
),
refreshTokenID: "tokenID",
newRefreshToken: base64.RawURLEncoding.EncodeToString([]byte("userID:tokenID:refreshToken1")),
},
},
@@ -1087,7 +965,7 @@ func TestCommands_renewRefreshToken(t *testing.T) {
idGenerator: tt.fields.idGenerator,
keyAlgorithm: tt.fields.keyAlgorithm,
}
gotEvent, gotNewRefreshToken, err := c.renewRefreshToken(tt.args.ctx, tt.args.userID, tt.args.orgID, tt.args.refreshToken, tt.args.idleExpiration)
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)
}
@@ -1096,6 +974,7 @@ func TestCommands_renewRefreshToken(t *testing.T) {
}
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)
}
})