2023-01-31 19:52:47 +00:00
package oidc
import (
2023-12-05 17:01:03 +00:00
"context"
2024-05-16 05:07:56 +00:00
"strings"
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
2024-08-20 06:45:24 +00:00
"github.com/zitadel/zitadel/internal/api/authz"
2024-09-11 09:04:09 +00:00
"github.com/zitadel/zitadel/internal/domain"
2024-04-05 09:35:49 +00:00
"github.com/zitadel/zitadel/internal/telemetry/tracing"
2023-12-08 14:30:55 +00:00
"github.com/zitadel/zitadel/internal/zerrors"
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-08 14:30:55 +00:00
if zerrors . IsNotFound ( err ) {
2024-08-20 06:45:24 +00:00
return nil , oidc . ErrInvalidClient ( ) . WithParent ( err ) . WithReturnParentToClient ( authz . GetFeatures ( ctx ) . DebugOIDCParentError ) . WithDescription ( "client not found" )
2023-12-05 17:01:03 +00:00
}
if err != nil {
return nil , err // defaults to server error
}
2024-04-05 09:35:49 +00:00
if user . Machine == nil || user . Machine . EncodedSecret == "" {
2023-12-08 14:30:55 +00:00
return nil , zerrors . ThrowPreconditionFailed ( nil , "OIDC-pieP8" , "Errors.User.Machine.Secret.NotExisting" )
2023-12-05 17:01:03 +00:00
}
2024-04-05 09:35:49 +00:00
ctx , spanPasswordComparison := tracing . NewNamedSpan ( ctx , "passwap.Verify" )
updated , err := s . hasher . Verify ( user . Machine . EncodedSecret , clientSecret )
spanPasswordComparison . EndWithError ( err )
if err != nil {
2023-12-05 17:01:03 +00:00
s . command . MachineSecretCheckFailed ( ctx , user . ID , user . ResourceOwner )
2023-12-08 14:30:55 +00:00
return nil , zerrors . ThrowInvalidArgument ( err , "OIDC-VoXo6" , "Errors.User.Machine.Secret.Invalid" )
2023-12-05 17:01:03 +00:00
}
2024-04-05 09:35:49 +00:00
s . command . MachineSecretCheckSucceeded ( ctx , user . ID , user . ResourceOwner , updated )
2023-12-05 17:01:03 +00:00
return & clientCredentialsClient {
2024-09-11 09:04:09 +00:00
clientID : user . Username ,
userID : user . ID ,
resourceOwner : user . ResourceOwner ,
tokenType : user . Machine . AccessTokenType ,
2023-12-05 17:01:03 +00:00
} , nil
}
2023-01-31 19:52:47 +00:00
type clientCredentialsClient struct {
2024-09-11 09:04:09 +00:00
clientID string
userID string
resourceOwner string
tokenType domain . OIDCTokenType
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 {
2024-09-11 09:04:09 +00:00
return accessTokenTypeToOIDC ( c . tokenType )
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 {
2024-09-11 09:04:09 +00:00
return c . clientID
2023-01-31 19:52:47 +00:00
}
// 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
}
}
func ( c * clientCredentialsClient ) IsScopeAllowed ( scope string ) bool {
2024-05-16 05:07:56 +00:00
return isScopeAllowed ( scope ) || strings . HasPrefix ( scope , ScopeProjectRolePrefix )
2023-01-31 19:52:47 +00:00
}
// 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
}