mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:37:32 +00:00
fix(oidc): do not return access token for response type id_token (#8777)
# Which Problems Are Solved Do not return an access token for implicit flow from v1 login, if the `response_type` is `id_token` # How the Problems Are Solved Do not create the access token event if if the `response_type` is `id_token`. # Additional Changes Token endpoint calls without auth request, such as machine users, token exchange and refresh token, do not have a `response_type`. For such calls the `OIDCResponseTypeUnspecified` enum is added at a `-1` offset, in order not to break existing client configs. # Additional Context - https://discord.com/channels/927474939156643850/1294001717725237298 - Fixes https://github.com/zitadel/zitadel/issues/8776
This commit is contained in:
@@ -147,6 +147,7 @@ func (c *Commands) CreateOIDCSession(ctx context.Context,
|
||||
actor *domain.TokenActor,
|
||||
needRefreshToken bool,
|
||||
sessionID string,
|
||||
responseType domain.OIDCResponseType,
|
||||
) (session *OIDCSession, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
@@ -164,8 +165,10 @@ func (c *Commands) CreateOIDCSession(ctx context.Context,
|
||||
|
||||
cmd.AddSession(ctx, userID, resourceOwner, sessionID, clientID, audience, scope, authMethods, authTime, nonce, preferredLanguage, userAgent)
|
||||
cmd.RegisterLogout(ctx, sessionID, userID, clientID, backChannelLogoutURI)
|
||||
if err = cmd.AddAccessToken(ctx, scope, userID, resourceOwner, reason, actor); err != nil {
|
||||
return nil, err
|
||||
if responseType != domain.OIDCResponseTypeIDToken {
|
||||
if err = cmd.AddAccessToken(ctx, scope, userID, resourceOwner, reason, actor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if needRefreshToken {
|
||||
if err = cmd.AddRefreshToken(ctx, userID); err != nil {
|
||||
|
@@ -749,6 +749,7 @@ func TestCommands_CreateOIDCSession(t *testing.T) {
|
||||
actor *domain.TokenActor
|
||||
needRefreshToken bool
|
||||
sessionID string
|
||||
responseType domain.OIDCResponseType
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -788,6 +789,7 @@ func TestCommands_CreateOIDCSession(t *testing.T) {
|
||||
Issuer: "foo.com",
|
||||
},
|
||||
needRefreshToken: false,
|
||||
responseType: domain.OIDCResponseTypeUnspecified,
|
||||
},
|
||||
wantErr: io.ErrClosedPipe,
|
||||
},
|
||||
@@ -844,6 +846,7 @@ func TestCommands_CreateOIDCSession(t *testing.T) {
|
||||
Issuer: "foo.com",
|
||||
},
|
||||
needRefreshToken: false,
|
||||
responseType: domain.OIDCResponseTypeUnspecified,
|
||||
},
|
||||
wantErr: zerrors.ThrowPreconditionFailed(nil, "OIDCS-kj3g2", "Errors.User.NotActive"),
|
||||
},
|
||||
@@ -918,6 +921,7 @@ func TestCommands_CreateOIDCSession(t *testing.T) {
|
||||
Issuer: "foo.com",
|
||||
},
|
||||
needRefreshToken: false,
|
||||
responseType: domain.OIDCResponseTypeUnspecified,
|
||||
},
|
||||
want: &OIDCSession{
|
||||
TokenID: "V2_oidcSessionID-at_accessTokenID",
|
||||
@@ -943,6 +947,87 @@ func TestCommands_CreateOIDCSession(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ID token only",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
user.NewHumanAddedEvent(
|
||||
context.Background(),
|
||||
&user.NewAggregate("userID", "org1").Aggregate,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.Afrikaans,
|
||||
domain.GenderUnspecified,
|
||||
"email",
|
||||
false,
|
||||
),
|
||||
),
|
||||
expectFilter(), // token lifetime
|
||||
expectPush(
|
||||
oidcsession.NewAddedEvent(context.Background(), &oidcsession.NewAggregate("V2_oidcSessionID", "org1").Aggregate,
|
||||
"userID", "org1", "", "clientID", []string{"audience"}, []string{"openid", "offline_access"},
|
||||
[]domain.UserAuthMethodType{domain.UserAuthMethodTypePassword}, testNow, "nonce", &language.Afrikaans,
|
||||
&domain.UserAgent{
|
||||
FingerprintID: gu.Ptr("fp1"),
|
||||
IP: net.ParseIP("1.2.3.4"),
|
||||
Description: gu.Ptr("firefox"),
|
||||
Header: http.Header{"foo": []string{"bar"}},
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
idGenerator: mock.NewIDGeneratorExpectIDs(t, "oidcSessionID"),
|
||||
defaultAccessTokenLifetime: time.Hour,
|
||||
defaultRefreshTokenLifetime: 7 * 24 * time.Hour,
|
||||
defaultRefreshTokenIdleLifetime: 24 * time.Hour,
|
||||
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "instanceID"),
|
||||
userID: "userID",
|
||||
resourceOwner: "org1",
|
||||
clientID: "clientID",
|
||||
audience: []string{"audience"},
|
||||
scope: []string{"openid", "offline_access"},
|
||||
authMethods: []domain.UserAuthMethodType{domain.UserAuthMethodTypePassword},
|
||||
authTime: testNow,
|
||||
nonce: "nonce",
|
||||
preferredLanguage: &language.Afrikaans,
|
||||
userAgent: &domain.UserAgent{
|
||||
FingerprintID: gu.Ptr("fp1"),
|
||||
IP: net.ParseIP("1.2.3.4"),
|
||||
Description: gu.Ptr("firefox"),
|
||||
Header: http.Header{"foo": []string{"bar"}},
|
||||
},
|
||||
reason: domain.TokenReasonAuthRequest,
|
||||
actor: &domain.TokenActor{
|
||||
UserID: "user2",
|
||||
Issuer: "foo.com",
|
||||
},
|
||||
needRefreshToken: false,
|
||||
responseType: domain.OIDCResponseTypeIDToken,
|
||||
},
|
||||
want: &OIDCSession{
|
||||
ClientID: "clientID",
|
||||
UserID: "userID",
|
||||
Audience: []string{"audience"},
|
||||
Scope: []string{"openid", "offline_access"},
|
||||
AuthMethods: []domain.UserAuthMethodType{domain.UserAuthMethodTypePassword},
|
||||
AuthTime: testNow,
|
||||
Nonce: "nonce",
|
||||
PreferredLanguage: &language.Afrikaans,
|
||||
UserAgent: &domain.UserAgent{
|
||||
FingerprintID: gu.Ptr("fp1"),
|
||||
IP: net.ParseIP("1.2.3.4"),
|
||||
Description: gu.Ptr("firefox"),
|
||||
Header: http.Header{"foo": []string{"bar"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "disable user token event",
|
||||
fields: fields{
|
||||
@@ -1018,6 +1103,7 @@ func TestCommands_CreateOIDCSession(t *testing.T) {
|
||||
Issuer: "foo.com",
|
||||
},
|
||||
needRefreshToken: false,
|
||||
responseType: domain.OIDCResponseTypeUnspecified,
|
||||
},
|
||||
want: &OIDCSession{
|
||||
TokenID: "V2_oidcSessionID-at_accessTokenID",
|
||||
@@ -1115,6 +1201,7 @@ func TestCommands_CreateOIDCSession(t *testing.T) {
|
||||
Issuer: "foo.com",
|
||||
},
|
||||
needRefreshToken: true,
|
||||
responseType: domain.OIDCResponseTypeUnspecified,
|
||||
},
|
||||
want: &OIDCSession{
|
||||
TokenID: "V2_oidcSessionID-at_accessTokenID",
|
||||
@@ -1213,6 +1300,7 @@ func TestCommands_CreateOIDCSession(t *testing.T) {
|
||||
},
|
||||
needRefreshToken: false,
|
||||
sessionID: "sessionID",
|
||||
responseType: domain.OIDCResponseTypeUnspecified,
|
||||
},
|
||||
want: &OIDCSession{
|
||||
TokenID: "V2_oidcSessionID-at_accessTokenID",
|
||||
@@ -1594,6 +1682,7 @@ func TestCommands_CreateOIDCSession(t *testing.T) {
|
||||
Issuer: "foo.com",
|
||||
},
|
||||
needRefreshToken: false,
|
||||
responseType: domain.OIDCResponseTypeUnspecified,
|
||||
},
|
||||
wantErr: zerrors.ThrowPermissionDenied(nil, "test", "test"),
|
||||
},
|
||||
@@ -1675,6 +1764,7 @@ func TestCommands_CreateOIDCSession(t *testing.T) {
|
||||
Issuer: "foo.com",
|
||||
},
|
||||
needRefreshToken: false,
|
||||
responseType: domain.OIDCResponseTypeUnspecified,
|
||||
},
|
||||
want: &OIDCSession{
|
||||
TokenID: "V2_oidcSessionID-at_accessTokenID",
|
||||
@@ -1729,6 +1819,7 @@ func TestCommands_CreateOIDCSession(t *testing.T) {
|
||||
tt.args.actor,
|
||||
tt.args.needRefreshToken,
|
||||
tt.args.sessionID,
|
||||
tt.args.responseType,
|
||||
)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
if got != nil {
|
||||
|
Reference in New Issue
Block a user