mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-06 19:36:41 +00:00
feat: token introspection, api clients and auth method private_key_jwt (#1276)
* introspect * testingapplication key * date * client keys * fix client keys * fix client keys * access tokens only for users * AuthMethodPrivateKeyJWT * client keys * set introspection info correctly * managae apis * update oidc pkg * cleanup * merge msater * set current sequence in migration * set current sequence in migration * set current sequence in migration * Apply suggestions from code review Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> * DeleteAuthNKeysByObjectID * ensure authn keys uptodate * update oidc version * merge master * merge master Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
)
|
||||
|
||||
type APIConfig struct {
|
||||
es_models.ObjectRoot
|
||||
AppID string `json:"appId"`
|
||||
ClientID string `json:"clientId,omitempty"`
|
||||
ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"`
|
||||
AuthMethodType int32 `json:"authMethodType,omitempty"`
|
||||
ClientKeys []*ClientKey `json:"-"`
|
||||
}
|
||||
|
||||
func (c *APIConfig) Changes(changed *APIConfig) map[string]interface{} {
|
||||
changes := make(map[string]interface{}, 1)
|
||||
changes["appId"] = c.AppID
|
||||
if c.AuthMethodType != changed.AuthMethodType {
|
||||
changes["authMethodType"] = changed.AuthMethodType
|
||||
}
|
||||
return changes
|
||||
}
|
||||
|
||||
func APIConfigFromModel(config *model.APIConfig) *APIConfig {
|
||||
return &APIConfig{
|
||||
ObjectRoot: config.ObjectRoot,
|
||||
AppID: config.AppID,
|
||||
ClientID: config.ClientID,
|
||||
ClientSecret: config.ClientSecret,
|
||||
AuthMethodType: int32(config.AuthMethodType),
|
||||
}
|
||||
}
|
||||
|
||||
func APIConfigToModel(config *APIConfig) *model.APIConfig {
|
||||
oidcConfig := &model.APIConfig{
|
||||
ObjectRoot: config.ObjectRoot,
|
||||
AppID: config.AppID,
|
||||
ClientID: config.ClientID,
|
||||
ClientSecret: config.ClientSecret,
|
||||
AuthMethodType: model.APIAuthMethodType(config.AuthMethodType),
|
||||
ClientKeys: ClientKeysToModel(config.ClientKeys),
|
||||
}
|
||||
return oidcConfig
|
||||
}
|
||||
|
||||
func (p *Project) appendAddAPIConfigEvent(event *es_models.Event) error {
|
||||
config := new(APIConfig)
|
||||
err := config.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.ObjectRoot.CreationDate = event.CreationDate
|
||||
if i, a := GetApplication(p.Applications, config.AppID); a != nil {
|
||||
p.Applications[i].Type = int32(model.AppTypeAPI)
|
||||
p.Applications[i].APIConfig = config
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Project) appendChangeAPIConfigEvent(event *es_models.Event) error {
|
||||
config := new(APIConfig)
|
||||
err := config.setData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if i, a := GetApplication(p.Applications, config.AppID); a != nil {
|
||||
return p.Applications[i].APIConfig.setData(event)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *APIConfig) setData(event *es_models.Event) error {
|
||||
o.ObjectRoot.AppendEvent(event)
|
||||
if err := json.Unmarshal(event.Data, o); err != nil {
|
||||
logging.Log("EVEN-d8e3s").WithError(err).Error("could not unmarshal event data")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -2,7 +2,9 @@ package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
)
|
||||
@@ -14,6 +16,7 @@ type Application struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Type int32 `json:"appType,omitempty"`
|
||||
OIDCConfig *OIDCConfig `json:"-"`
|
||||
APIConfig *APIConfig `json:"-"`
|
||||
}
|
||||
|
||||
type ApplicationID struct {
|
||||
@@ -66,6 +69,9 @@ func AppFromModel(app *model.Application) *Application {
|
||||
if app.OIDCConfig != nil {
|
||||
converted.OIDCConfig = OIDCConfigFromModel(app.OIDCConfig)
|
||||
}
|
||||
if app.APIConfig != nil {
|
||||
converted.APIConfig = APIConfigFromModel(app.APIConfig)
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
@@ -80,6 +86,9 @@ func AppToModel(app *Application) *model.Application {
|
||||
if app.OIDCConfig != nil {
|
||||
converted.OIDCConfig = OIDCConfigToModel(app.OIDCConfig)
|
||||
}
|
||||
if app.APIConfig != nil {
|
||||
converted.APIConfig = APIConfigToModel(app.APIConfig)
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
@@ -101,7 +110,7 @@ func (p *Project) appendChangeAppEvent(event *es_models.Event) error {
|
||||
return err
|
||||
}
|
||||
if i, a := GetApplication(p.Applications, app.AppID); a != nil {
|
||||
p.Applications[i].setData(event)
|
||||
return p.Applications[i].setData(event)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ import (
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
key_model "github.com/caos/zitadel/internal/key/model"
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
)
|
||||
|
||||
@@ -30,6 +32,7 @@ type OIDCConfig struct {
|
||||
IDTokenRoleAssertion bool `json:"idTokenRoleAssertion,omitempty"`
|
||||
IDTokenUserinfoAssertion bool `json:"idTokenUserinfoAssertion,omitempty"`
|
||||
ClockSkew time.Duration `json:"clockSkew,omitempty"`
|
||||
ClientKeys []*ClientKey `json:"-"`
|
||||
}
|
||||
|
||||
func (c *OIDCConfig) Changes(changed *OIDCConfig) map[string]interface{} {
|
||||
@@ -134,6 +137,7 @@ func OIDCConfigToModel(config *OIDCConfig) *model.OIDCConfig {
|
||||
IDTokenRoleAssertion: config.IDTokenRoleAssertion,
|
||||
IDTokenUserinfoAssertion: config.IDTokenUserinfoAssertion,
|
||||
ClockSkew: config.ClockSkew,
|
||||
ClientKeys: ClientKeysToModel(config.ClientKeys),
|
||||
}
|
||||
oidcConfig.FillCompliance()
|
||||
return oidcConfig
|
||||
@@ -161,7 +165,50 @@ func (p *Project) appendChangeOIDCConfigEvent(event *es_models.Event) error {
|
||||
}
|
||||
|
||||
if i, a := GetApplication(p.Applications, config.AppID); a != nil {
|
||||
p.Applications[i].OIDCConfig.setData(event)
|
||||
return p.Applications[i].OIDCConfig.setData(event)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Project) appendAddClientKeyEvent(event *es_models.Event) error {
|
||||
key := new(ClientKey)
|
||||
err := key.SetData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if i, a := GetApplication(p.Applications, key.ApplicationID); a != nil {
|
||||
if a.OIDCConfig != nil {
|
||||
p.Applications[i].OIDCConfig.ClientKeys = append(p.Applications[i].OIDCConfig.ClientKeys, key)
|
||||
}
|
||||
if a.APIConfig != nil {
|
||||
p.Applications[i].APIConfig.ClientKeys = append(p.Applications[i].APIConfig.ClientKeys, key)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Project) appendRemoveClientKeyEvent(event *es_models.Event) error {
|
||||
key := new(ClientKey)
|
||||
err := key.SetData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if i, a := GetApplication(p.Applications, key.ApplicationID); a != nil {
|
||||
if a.OIDCConfig != nil {
|
||||
if j, k := GetClientKey(p.Applications[i].OIDCConfig.ClientKeys, key.KeyID); k != nil {
|
||||
p.Applications[i].OIDCConfig.ClientKeys[j] = p.Applications[i].OIDCConfig.ClientKeys[len(p.Applications[i].OIDCConfig.ClientKeys)-1]
|
||||
p.Applications[i].OIDCConfig.ClientKeys[len(p.Applications[i].OIDCConfig.ClientKeys)-1] = nil
|
||||
p.Applications[i].OIDCConfig.ClientKeys = p.Applications[i].OIDCConfig.ClientKeys[:len(p.Applications[i].OIDCConfig.ClientKeys)-1]
|
||||
}
|
||||
}
|
||||
if a.APIConfig != nil {
|
||||
if j, k := GetClientKey(p.Applications[i].APIConfig.ClientKeys, key.KeyID); k != nil {
|
||||
p.Applications[i].APIConfig.ClientKeys[j] = p.Applications[i].APIConfig.ClientKeys[len(p.Applications[i].APIConfig.ClientKeys)-1]
|
||||
p.Applications[i].APIConfig.ClientKeys[len(p.Applications[i].APIConfig.ClientKeys)-1] = nil
|
||||
p.Applications[i].APIConfig.ClientKeys = p.Applications[i].APIConfig.ClientKeys[:len(p.Applications[i].APIConfig.ClientKeys)-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -174,3 +221,100 @@ func (o *OIDCConfig) setData(event *es_models.Event) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetClientKey(keys []*ClientKey, id string) (int, *ClientKey) {
|
||||
for i, k := range keys {
|
||||
if k.KeyID == id {
|
||||
return i, k
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
type ClientKey struct {
|
||||
es_models.ObjectRoot `json:"-"`
|
||||
ApplicationID string `json:"applicationId,omitempty"`
|
||||
ClientID string `json:"clientId,omitempty"`
|
||||
KeyID string `json:"keyId,omitempty"`
|
||||
Type int32 `json:"type,omitempty"`
|
||||
ExpirationDate time.Time `json:"expirationDate,omitempty"`
|
||||
PublicKey []byte `json:"publicKey,omitempty"`
|
||||
privateKey []byte
|
||||
}
|
||||
|
||||
func (key *ClientKey) SetData(event *es_models.Event) error {
|
||||
key.ObjectRoot.AppendEvent(event)
|
||||
if err := json.Unmarshal(event.Data, key); err != nil {
|
||||
logging.Log("EVEN-SADdg").WithError(err).Error("could not unmarshal event data")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (key *ClientKey) AppendEvents(events ...*es_models.Event) error {
|
||||
for _, event := range events {
|
||||
err := key.AppendEvent(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (key *ClientKey) AppendEvent(event *es_models.Event) (err error) {
|
||||
key.ObjectRoot.AppendEvent(event)
|
||||
switch event.Type {
|
||||
case ClientKeyAdded:
|
||||
err = json.Unmarshal(event.Data, key)
|
||||
if err != nil {
|
||||
return errors.ThrowInternal(err, "MODEL-Fetg3", "Errors.Internal")
|
||||
}
|
||||
case ClientKeyRemoved:
|
||||
key.ExpirationDate = event.CreationDate
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func ClientKeyFromModel(key *model.ClientKey) *ClientKey {
|
||||
return &ClientKey{
|
||||
ObjectRoot: key.ObjectRoot,
|
||||
ExpirationDate: key.ExpirationDate,
|
||||
ApplicationID: key.ApplicationID,
|
||||
ClientID: key.ClientID,
|
||||
KeyID: key.KeyID,
|
||||
Type: int32(key.Type),
|
||||
}
|
||||
}
|
||||
|
||||
func ClientKeysToModel(keys []*ClientKey) []*model.ClientKey {
|
||||
clientKeys := make([]*model.ClientKey, len(keys))
|
||||
for i, key := range keys {
|
||||
clientKeys[i] = ClientKeyToModel(key)
|
||||
}
|
||||
return clientKeys
|
||||
}
|
||||
|
||||
func ClientKeyToModel(key *ClientKey) *model.ClientKey {
|
||||
return &model.ClientKey{
|
||||
ObjectRoot: key.ObjectRoot,
|
||||
ExpirationDate: key.ExpirationDate,
|
||||
ApplicationID: key.ApplicationID,
|
||||
ClientID: key.ClientID,
|
||||
KeyID: key.KeyID,
|
||||
PrivateKey: key.privateKey,
|
||||
Type: key_model.AuthNKeyType(key.Type),
|
||||
}
|
||||
}
|
||||
|
||||
func (key *ClientKey) GenerateClientKeyPair(keySize int) error {
|
||||
privateKey, publicKey, err := crypto.GenerateKeyPair(keySize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key.PublicKey, err = crypto.PublicKeyToBytes(publicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key.privateKey = crypto.PrivateKeyToBytes(privateKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -139,6 +139,14 @@ func (p *Project) AppendEvent(event *es_models.Event) error {
|
||||
return p.appendAddOIDCConfigEvent(event)
|
||||
case OIDCConfigChanged, OIDCConfigSecretChanged:
|
||||
return p.appendChangeOIDCConfigEvent(event)
|
||||
case APIConfigAdded:
|
||||
return p.appendAddAPIConfigEvent(event)
|
||||
case APIConfigChanged, APIConfigSecretChanged:
|
||||
return p.appendChangeAPIConfigEvent(event)
|
||||
case ClientKeyAdded:
|
||||
return p.appendAddClientKeyEvent(event)
|
||||
case ClientKeyRemoved:
|
||||
return p.appendRemoveClientKeyEvent(event)
|
||||
case ProjectGrantAdded:
|
||||
return p.appendAddGrantEvent(event)
|
||||
case ProjectGrantChanged, ProjectGrantCascadeChanged:
|
||||
|
||||
@@ -41,4 +41,11 @@ const (
|
||||
OIDCConfigSecretChanged models.EventType = "project.application.config.oidc.secret.changed"
|
||||
OIDCClientSecretCheckSucceeded models.EventType = "project.application.oidc.secret.check.succeeded"
|
||||
OIDCClientSecretCheckFailed models.EventType = "project.application.oidc.secret.check.failed"
|
||||
|
||||
APIConfigAdded models.EventType = "project.application.config.api.added"
|
||||
APIConfigChanged models.EventType = "project.application.config.api.changed"
|
||||
APIConfigSecretChanged models.EventType = "project.application.config.api.secret.changed"
|
||||
|
||||
ClientKeyAdded models.EventType = "project.application.oidc.key.added"
|
||||
ClientKeyRemoved models.EventType = "project.application.oidc.key.removed"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user