fix(oidc): return clients without instance settings (#7036)

This commit is contained in:
Tim Möhlmann 2023-12-07 11:43:45 +02:00 committed by GitHub
parent fb235d7ea1
commit 2e505f40f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 126 additions and 39 deletions

View File

@ -927,6 +927,12 @@ func (s *Server) VerifyClient(ctx context.Context, r *op.Request[op.ClientCreden
if client.State != domain.AppStateActive { if client.State != domain.AppStateActive {
return nil, oidc.ErrInvalidClient().WithDescription("client is not active") return nil, oidc.ErrInvalidClient().WithDescription("client is not active")
} }
if client.Settings == nil {
client.Settings = &query.OIDCSettings{
AccessTokenLifetime: s.defaultAccessTokenLifetime,
IdTokenLifetime: s.defaultIdTokenLifetime,
}
}
switch client.AuthMethodType { switch client.AuthMethodType {
case domain.OIDCAuthMethodTypeBasic, domain.OIDCAuthMethodTypePost: case domain.OIDCAuthMethodTypeBasic, domain.OIDCAuthMethodTypePost:

View File

@ -92,11 +92,11 @@ func (c *Client) RestrictAdditionalAccessTokenScopes() func(scopes []string) []s
} }
func (c *Client) AccessTokenLifetime() time.Duration { func (c *Client) AccessTokenLifetime() time.Duration {
return c.client.AccessTokenLifetime return c.client.Settings.AccessTokenLifetime
} }
func (c *Client) IDTokenLifetime() time.Duration { func (c *Client) IDTokenLifetime() time.Duration {
return c.client.IDTokenLifetime return c.client.Settings.IdTokenLifetime
} }
func (c *Client) AccessTokenType() op.AccessTokenType { func (c *Client) AccessTokenType() op.AccessTokenType {

View File

@ -122,19 +122,21 @@ func NewServer(
} }
server := &Server{ server := &Server{
LegacyServer: op.NewLegacyServer(provider, endpoints(config.CustomEndpoints)), LegacyServer: op.NewLegacyServer(provider, endpoints(config.CustomEndpoints)),
features: config.Features, features: config.Features,
repo: repo, repo: repo,
query: query, query: query,
command: command, command: command,
keySet: newKeySet(context.TODO(), time.Hour, query.GetActivePublicKeyByID), keySet: newKeySet(context.TODO(), time.Hour, query.GetActivePublicKeyByID),
defaultLoginURL: fmt.Sprintf("%s%s?%s=", login.HandlerPrefix, login.EndpointLogin, login.QueryAuthRequestID), defaultLoginURL: fmt.Sprintf("%s%s?%s=", login.HandlerPrefix, login.EndpointLogin, login.QueryAuthRequestID),
defaultLoginURLV2: config.DefaultLoginURLV2, defaultLoginURLV2: config.DefaultLoginURLV2,
defaultLogoutURLV2: config.DefaultLogoutURLV2, defaultLogoutURLV2: config.DefaultLogoutURLV2,
fallbackLogger: fallbackLogger, defaultAccessTokenLifetime: config.DefaultAccessTokenLifetime,
hashAlg: crypto.NewBCrypt(10), // as we are only verifying in oidc, the cost is already part of the hash string and the config here is irrelevant. defaultIdTokenLifetime: config.DefaultIdTokenLifetime,
signingKeyAlgorithm: config.SigningKeyAlgorithm, fallbackLogger: fallbackLogger,
assetAPIPrefix: assets.AssetAPI(externalSecure), hashAlg: crypto.NewBCrypt(10), // as we are only verifying in oidc, the cost is already part of the hash string and the config here is irrelevant.
signingKeyAlgorithm: config.SigningKeyAlgorithm,
assetAPIPrefix: assets.AssetAPI(externalSecure),
} }
metricTypes := []metrics.MetricType{metrics.MetricTypeRequestCount, metrics.MetricTypeStatusCode, metrics.MetricTypeTotalCount} metricTypes := []metrics.MetricType{metrics.MetricTypeRequestCount, metrics.MetricTypeStatusCode, metrics.MetricTypeTotalCount}
server.Handler = op.RegisterLegacyServer(server, op.WithHTTPMiddleware( server.Handler = op.RegisterLegacyServer(server, op.WithHTTPMiddleware(

View File

@ -3,6 +3,7 @@ package oidc
import ( import (
"context" "context"
"net/http" "net/http"
"time"
"github.com/zitadel/logging" "github.com/zitadel/logging"
"github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/oidc/v3/pkg/oidc"
@ -27,9 +28,11 @@ type Server struct {
command *command.Commands command *command.Commands
keySet *keySetCache keySet *keySetCache
defaultLoginURL string defaultLoginURL string
defaultLoginURLV2 string defaultLoginURLV2 string
defaultLogoutURLV2 string defaultLogoutURLV2 string
defaultAccessTokenLifetime time.Duration
defaultIdTokenLifetime time.Duration
fallbackLogger *slog.Logger fallbackLogger *slog.Logger
hashAlg crypto.HashAlgorithm hashAlg crypto.HashAlgorithm

View File

@ -29,18 +29,18 @@ keys as (
group by identifier group by identifier
), ),
settings as ( settings as (
select instance_id, access_token_lifetime, id_token_lifetime select instance_id, json_build_object('access_token_lifetime', access_token_lifetime, 'id_token_lifetime', id_token_lifetime) as settings
from projections.oidc_settings2 from projections.oidc_settings2
where aggregate_id = $1 where aggregate_id = $1
and instance_id = $1 and instance_id = $1
) )
select row_to_json(r) as client from ( select row_to_json(r) as client from (
select c.*, r.project_role_keys, k.public_keys, s.access_token_lifetime, s.id_token_lifetime select c.*, r.project_role_keys, k.public_keys, s.settings
from client c from client c
left join roles r on r.project_id = c.project_id left join roles r on r.project_id = c.project_id
left join keys k on k.client_id = c.client_id left join keys k on k.client_id = c.client_id
join settings s on s.instance_id = s.instance_id left join settings s on s.instance_id = s.instance_id
) r; ) r;
--execute q('230690539048009730', '236647088211951618@tests', true); --execute q('230690539048009730', '236647088211951618@tests', true);

View File

@ -37,8 +37,7 @@ type OIDCClient struct {
PublicKeys map[string][]byte `json:"public_keys,omitempty"` PublicKeys map[string][]byte `json:"public_keys,omitempty"`
ProjectID string `json:"project_id,omitempty"` ProjectID string `json:"project_id,omitempty"`
ProjectRoleKeys []string `json:"project_role_keys,omitempty"` ProjectRoleKeys []string `json:"project_role_keys,omitempty"`
AccessTokenLifetime time.Duration `json:"access_token_lifetime,omitempty"` Settings *OIDCSettings `json:"settings,omitempty"`
IDTokenLifetime time.Duration `json:"id_token_lifetime,omitempty"`
} }
//go:embed embed/oidc_client_by_id.sql //go:embed embed/oidc_client_by_id.sql

View File

@ -24,6 +24,8 @@ var (
testdataOidcClientPublic string testdataOidcClientPublic string
//go:embed testdata/oidc_client_secret.json //go:embed testdata/oidc_client_secret.json
testdataOidcClientSecret string testdataOidcClientSecret string
//go:embed testdata/oidc_client_no_settings.json
testdataOidcClientNoSettings string
) )
func TestQueries_GetOIDCClientByID(t *testing.T) { func TestQueries_GetOIDCClientByID(t *testing.T) {
@ -81,8 +83,10 @@ low2kyJov38V4Uk2I8kuXpLcnrpw5Tio2ooiUE27b0vHZqBKOei9Uo88qCrn3EKx
ProjectID: "236645808328409090", ProjectID: "236645808328409090",
PublicKeys: map[string][]byte{"236647201860747266": []byte(pubkey)}, PublicKeys: map[string][]byte{"236647201860747266": []byte(pubkey)},
ProjectRoleKeys: []string{"role1", "role2"}, ProjectRoleKeys: []string{"role1", "role2"},
AccessTokenLifetime: 43200000000000, Settings: &OIDCSettings{
IDTokenLifetime: 43200000000000, AccessTokenLifetime: 43200000000000,
IdTokenLifetime: 43200000000000,
},
}, },
}, },
{ {
@ -110,8 +114,10 @@ low2kyJov38V4Uk2I8kuXpLcnrpw5Tio2ooiUE27b0vHZqBKOei9Uo88qCrn3EKx
PublicKeys: nil, PublicKeys: nil,
ProjectID: "236645808328409090", ProjectID: "236645808328409090",
ProjectRoleKeys: []string{"role1", "role2"}, ProjectRoleKeys: []string{"role1", "role2"},
AccessTokenLifetime: 43200000000000, Settings: &OIDCSettings{
IDTokenLifetime: 43200000000000, AccessTokenLifetime: 43200000000000,
IdTokenLifetime: 43200000000000,
},
}, },
}, },
{ {
@ -143,8 +149,43 @@ low2kyJov38V4Uk2I8kuXpLcnrpw5Tio2ooiUE27b0vHZqBKOei9Uo88qCrn3EKx
PublicKeys: nil, PublicKeys: nil,
ProjectID: "236645808328409090", ProjectID: "236645808328409090",
ProjectRoleKeys: []string{"role1", "role2"}, ProjectRoleKeys: []string{"role1", "role2"},
AccessTokenLifetime: 43200000000000, Settings: &OIDCSettings{
IDTokenLifetime: 43200000000000, AccessTokenLifetime: 43200000000000,
IdTokenLifetime: 43200000000000,
},
},
},
{
name: "no oidc settings",
mock: mockQuery(expQuery, cols, []driver.Value{testdataOidcClientNoSettings}, "instanceID", "clientID", true),
want: &OIDCClient{
InstanceID: "239520764275982338",
AppID: "239520764276441090",
State: domain.AppStateActive,
ClientID: "239520764779364354@zitadel",
ClientSecret: nil,
RedirectURIs: []string{
"http://test2-qucuh5.localhost:9000/ui/console/auth/callback",
"http://test.localhost.com:9000/ui/console/auth/callback"},
ResponseTypes: []domain.OIDCResponseType{0},
GrantTypes: []domain.OIDCGrantType{0},
ApplicationType: domain.OIDCApplicationTypeUserAgent,
AuthMethodType: domain.OIDCAuthMethodTypeNone,
PostLogoutRedirectURIs: []string{
"http://test2-qucuh5.localhost:9000/ui/console/signedout",
"http://test.localhost.com:9000/ui/console/signedout",
},
IsDevMode: true,
AccessTokenType: domain.OIDCTokenTypeBearer,
AccessTokenRoleAssertion: false,
IDTokenRoleAssertion: false,
IDTokenUserinfoAssertion: false,
ClockSkew: 0,
AdditionalOrigins: nil,
PublicKeys: nil,
ProjectID: "239520764276178946",
ProjectRoleKeys: nil,
Settings: nil,
}, },
}, },
} }

View File

@ -69,10 +69,10 @@ type OIDCSettings struct {
ResourceOwner string ResourceOwner string
Sequence uint64 Sequence uint64
AccessTokenLifetime time.Duration AccessTokenLifetime time.Duration `json:"access_token_lifetime,omitempty"`
IdTokenLifetime time.Duration IdTokenLifetime time.Duration `json:"id_token_lifetime,omitempty"`
RefreshTokenIdleExpiration time.Duration RefreshTokenIdleExpiration time.Duration `json:"refresh_token_idle_expiration,omitempty"`
RefreshTokenExpiration time.Duration RefreshTokenExpiration time.Duration `json:"refresh_token_expiration,omitempty"`
} }
func (q *Queries) OIDCSettingsByAggID(ctx context.Context, aggregateID string) (settings *OIDCSettings, err error) { func (q *Queries) OIDCSettingsByAggID(ctx context.Context, aggregateID string) (settings *OIDCSettings, err error) {

View File

@ -22,6 +22,8 @@
"public_keys": { "public_keys": {
"236647201860747266": "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFB\nT0NBUThBTUlJQkNnS0NBUUVBMnVmQUwxYjcyYkl5MWFyK1dzNmIKR29oSkpRRkI3ZGZSYXBEcWVx\nTThVa3A2Q1ZkUHpxL3BPejF2aUFxNTB5eldaSnJ5Risyd3NoRkFLR0Y5QTIvQgoyWWY5YkpYUFov\nS2JrRnJZVDNOVHZZRGt2bGFTVGw5bU1uenJVMjlzNDhGMVBUV0tmQitDM2FNc09FRzFCdWZWCnM2\nM3FGNG5yRVBqU2JobGpJY285RlpxNFhwcEl6aE1RMGZEZEEvK1h5Z0NKcXZ1YUwwTGliTTFLcmxV\nZG51NzEKWWVraFNKakVQbnZPaXNYSWs0SVh5d29HSU93dGp4a0R2Tkl0UXZhTVZsZHI0L2tiNnV2\nYmdkV3dxNUV3QlpYcQpsb3cya3lKb3YzOFY0VWsySThrdVhwTGNucnB3NVRpbzJvb2lVRTI3YjB2\nSFpxQktPZWk5VW84OHFDcm4zRUt4CjZRSURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0t\nLS0K" "236647201860747266": "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFB\nT0NBUThBTUlJQkNnS0NBUUVBMnVmQUwxYjcyYkl5MWFyK1dzNmIKR29oSkpRRkI3ZGZSYXBEcWVx\nTThVa3A2Q1ZkUHpxL3BPejF2aUFxNTB5eldaSnJ5Risyd3NoRkFLR0Y5QTIvQgoyWWY5YkpYUFov\nS2JrRnJZVDNOVHZZRGt2bGFTVGw5bU1uenJVMjlzNDhGMVBUV0tmQitDM2FNc09FRzFCdWZWCnM2\nM3FGNG5yRVBqU2JobGpJY285RlpxNFhwcEl6aE1RMGZEZEEvK1h5Z0NKcXZ1YUwwTGliTTFLcmxV\nZG51NzEKWWVraFNKakVQbnZPaXNYSWs0SVh5d29HSU93dGp4a0R2Tkl0UXZhTVZsZHI0L2tiNnV2\nYmdkV3dxNUV3QlpYcQpsb3cya3lKb3YzOFY0VWsySThrdVhwTGNucnB3NVRpbzJvb2lVRTI3YjB2\nSFpxQktPZWk5VW84OHFDcm4zRUt4CjZRSURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0t\nLS0K"
}, },
"access_token_lifetime": 43200000000000, "settings": {
"id_token_lifetime": 43200000000000 "access_token_lifetime": 43200000000000,
"id_token_lifetime": 43200000000000
}
} }

View File

@ -0,0 +1,30 @@
{
"instance_id": "239520764275982338",
"app_id": "239520764276441090",
"client_id": "239520764779364354@zitadel",
"client_secret": null,
"redirect_uris": [
"http://test2-qucuh5.localhost:9000/ui/console/auth/callback",
"http://test.localhost.com:9000/ui/console/auth/callback"
],
"response_types": [0],
"grant_types": [0],
"application_type": 1,
"auth_method_type": 2,
"post_logout_redirect_uris": [
"http://test2-qucuh5.localhost:9000/ui/console/signedout",
"http://test.localhost.com:9000/ui/console/signedout"
],
"is_dev_mode": true,
"access_token_type": 0,
"access_token_role_assertion": false,
"id_token_role_assertion": false,
"id_token_userinfo_assertion": false,
"clock_skew": 0,
"additional_origins": null,
"project_id": "239520764276178946",
"state": 1,
"project_role_keys": null,
"public_keys": null,
"settings": null
}

View File

@ -20,6 +20,8 @@
"state": 1, "state": 1,
"project_role_keys": ["role1", "role2"], "project_role_keys": ["role1", "role2"],
"public_keys": null, "public_keys": null,
"access_token_lifetime": 43200000000000, "settings": {
"id_token_lifetime": 43200000000000 "access_token_lifetime": 43200000000000,
"id_token_lifetime": 43200000000000
}
} }

View File

@ -25,6 +25,8 @@
"state": 1, "state": 1,
"project_role_keys": ["role1", "role2"], "project_role_keys": ["role1", "role2"],
"public_keys": null, "public_keys": null,
"access_token_lifetime": 43200000000000, "settings": {
"id_token_lifetime": 43200000000000 "access_token_lifetime": 43200000000000,
"id_token_lifetime": 43200000000000
}
} }