2023-01-31 19:52:47 +00:00
|
|
|
package oidc
|
|
|
|
|
|
|
|
import (
|
2023-12-05 17:01:03 +00:00
|
|
|
"context"
|
2023-01-31 19:52:47 +00:00
|
|
|
"time"
|
|
|
|
|
2023-10-17 15:19:51 +00:00
|
|
|
"github.com/zitadel/oidc/v3/pkg/oidc"
|
|
|
|
"github.com/zitadel/oidc/v3/pkg/op"
|
2023-12-05 17:01:03 +00:00
|
|
|
|
|
|
|
"github.com/zitadel/zitadel/internal/crypto"
|
|
|
|
"github.com/zitadel/zitadel/internal/errors"
|
|
|
|
"github.com/zitadel/zitadel/internal/query"
|
2023-01-31 19:52:47 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type clientCredentialsRequest struct {
|
2023-02-08 08:06:34 +00:00
|
|
|
sub string
|
|
|
|
audience []string
|
|
|
|
scopes []string
|
2023-01-31 19:52:47 +00:00
|
|
|
}
|
|
|
|
|
2023-02-08 08:06:34 +00:00
|
|
|
// GetSubject returns the subject for token to be created because of the client credentials request
|
|
|
|
// the subject will be the id of the service user
|
2023-01-31 19:52:47 +00:00
|
|
|
func (c *clientCredentialsRequest) GetSubject() string {
|
|
|
|
return c.sub
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetAudience returns the audience for token to be created because of the client credentials request
|
|
|
|
func (c *clientCredentialsRequest) GetAudience() []string {
|
2023-02-08 08:06:34 +00:00
|
|
|
return c.audience
|
2023-01-31 19:52:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *clientCredentialsRequest) GetScopes() []string {
|
|
|
|
return c.scopes
|
|
|
|
}
|
|
|
|
|
2023-12-05 17:01:03 +00:00
|
|
|
func (s *Server) clientCredentialsAuth(ctx context.Context, clientID, clientSecret string) (op.Client, error) {
|
2023-12-08 12:14:22 +00:00
|
|
|
user, err := s.query.GetUserByLoginName(ctx, false, clientID)
|
2023-12-05 17:01:03 +00:00
|
|
|
if errors.IsNotFound(err) {
|
|
|
|
return nil, oidc.ErrInvalidClient().WithParent(err).WithDescription("client not found")
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, err // defaults to server error
|
|
|
|
}
|
|
|
|
if user.Machine == nil || user.Machine.Secret == nil {
|
|
|
|
return nil, errors.ThrowPreconditionFailed(nil, "OIDC-pieP8", "Errors.User.Machine.Secret.NotExisting")
|
|
|
|
}
|
|
|
|
if err = crypto.CompareHash(user.Machine.Secret, []byte(clientSecret), s.hashAlg); err != nil {
|
|
|
|
s.command.MachineSecretCheckFailed(ctx, user.ID, user.ResourceOwner)
|
|
|
|
return nil, errors.ThrowInvalidArgument(err, "OIDC-VoXo6", "Errors.User.Machine.Secret.Invalid")
|
|
|
|
}
|
|
|
|
|
|
|
|
s.command.MachineSecretCheckSucceeded(ctx, user.ID, user.ResourceOwner)
|
|
|
|
return &clientCredentialsClient{
|
|
|
|
id: clientID,
|
|
|
|
user: user,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2023-01-31 19:52:47 +00:00
|
|
|
type clientCredentialsClient struct {
|
2023-12-05 17:01:03 +00:00
|
|
|
id string
|
|
|
|
user *query.User
|
2023-01-31 19:52:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// AccessTokenType returns the AccessTokenType for the token to be created because of the client credentials request
|
|
|
|
// machine users currently only have opaque tokens ([op.AccessTokenTypeBearer])
|
|
|
|
func (c *clientCredentialsClient) AccessTokenType() op.AccessTokenType {
|
2023-12-05 17:01:03 +00:00
|
|
|
return accessTokenTypeToOIDC(c.user.Machine.AccessTokenType)
|
2023-01-31 19:52:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetID returns the client_id (username of the machine user) for the token to be created because of the client credentials request
|
|
|
|
func (c *clientCredentialsClient) GetID() string {
|
|
|
|
return c.id
|
|
|
|
}
|
|
|
|
|
|
|
|
// RedirectURIs returns nil as there are no redirect uris
|
|
|
|
func (c *clientCredentialsClient) RedirectURIs() []string {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// PostLogoutRedirectURIs returns nil as there are no logout redirect uris
|
|
|
|
func (c *clientCredentialsClient) PostLogoutRedirectURIs() []string {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ApplicationType returns [op.ApplicationTypeWeb] as the machine users is a confidential client
|
|
|
|
func (c *clientCredentialsClient) ApplicationType() op.ApplicationType {
|
|
|
|
return op.ApplicationTypeWeb
|
|
|
|
}
|
|
|
|
|
|
|
|
// AuthMethod returns the allowed auth method type for machine user.
|
|
|
|
// It returns Basic Auth
|
|
|
|
func (c *clientCredentialsClient) AuthMethod() oidc.AuthMethod {
|
|
|
|
return oidc.AuthMethodBasic
|
|
|
|
}
|
|
|
|
|
|
|
|
// ResponseTypes returns nil as the types are only required for an authorization request
|
|
|
|
func (c *clientCredentialsClient) ResponseTypes() []oidc.ResponseType {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GrantTypes returns the grant types supported by the machine users, which is currently only client credentials ([oidc.GrantTypeClientCredentials])
|
|
|
|
func (c *clientCredentialsClient) GrantTypes() []oidc.GrantType {
|
|
|
|
return []oidc.GrantType{
|
|
|
|
oidc.GrantTypeClientCredentials,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoginURL returns an empty string as there is no login UI involved
|
|
|
|
func (c *clientCredentialsClient) LoginURL(_ string) string {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
// IDTokenLifetime returns 0 as there is no id_token issued
|
|
|
|
func (c *clientCredentialsClient) IDTokenLifetime() time.Duration {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// DevMode returns false as there is no dev mode
|
|
|
|
func (c *clientCredentialsClient) DevMode() bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// RestrictAdditionalIdTokenScopes returns nil as no id_token is issued
|
|
|
|
func (c *clientCredentialsClient) RestrictAdditionalIdTokenScopes() func(scopes []string) []string {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// RestrictAdditionalAccessTokenScopes returns the scope allowed for the token to be created because of the client credentials request
|
|
|
|
// currently it allows all scopes to be used in the access token
|
|
|
|
func (c *clientCredentialsClient) RestrictAdditionalAccessTokenScopes() func(scopes []string) []string {
|
|
|
|
return func(scopes []string) []string {
|
|
|
|
return scopes
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsScopeAllowed returns null false as the check is executed during the auth request validation
|
|
|
|
func (c *clientCredentialsClient) IsScopeAllowed(scope string) bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// IDTokenUserinfoClaimsAssertion returns null false as no id_token is issued
|
|
|
|
func (c *clientCredentialsClient) IDTokenUserinfoClaimsAssertion() bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// ClockSkew enable handling clock skew of the token validation. The duration (0-5s) will be added to exp claim and subtracted from iats,
|
|
|
|
// auth_time and nbf of the token to be created because of the client credentials request.
|
|
|
|
// It returns 0 as clock skew is not implemented on machine users.
|
|
|
|
func (c *clientCredentialsClient) ClockSkew() time.Duration {
|
|
|
|
return 0
|
|
|
|
}
|