github.com/Axway/agent-sdk@v1.1.101/pkg/authz/oauth/clientmetadatabuilder.go (about)

     1  package oauth
     2  
     3  import (
     4  	"crypto/x509"
     5  	"encoding/base64"
     6  	"encoding/json"
     7  	"encoding/pem"
     8  	"fmt"
     9  
    10  	"github.com/Axway/agent-sdk/pkg/config"
    11  	"github.com/Axway/agent-sdk/pkg/util"
    12  	jwkcert "github.com/lestrrat-go/jwx/v2/cert"
    13  	"github.com/lestrrat-go/jwx/v2/jwk"
    14  )
    15  
    16  var grantTypeWithRedirects = map[string]bool{GrantTypeAuthorizationCode: true, GrantTypeImplicit: true}
    17  
    18  // ClientBuilder - Builder for IdP client representation
    19  type ClientBuilder interface {
    20  	SetClientName(string) ClientBuilder
    21  
    22  	SetScopes([]string) ClientBuilder
    23  	SetGrantTypes([]string) ClientBuilder
    24  	SetResponseType([]string) ClientBuilder
    25  	SetTokenEndpointAuthMethod(tokenAuthMethod string) ClientBuilder
    26  
    27  	SetRedirectURIs([]string) ClientBuilder
    28  	SetLogoURI(string) ClientBuilder
    29  
    30  	SetJWKSURI(string) ClientBuilder
    31  	SetJWKS([]byte) ClientBuilder
    32  
    33  	SetCertificateMetadata(certificateMetaddata string) ClientBuilder
    34  	SetTLSClientAuthSanDNS(tlsClientAuthSanDNS string) ClientBuilder
    35  	SetTLSClientAuthSanEmail(tlsClientAuthSanEmail string) ClientBuilder
    36  	SetTLSClientAuthSanIP(tlsClientAuthSanIP string) ClientBuilder
    37  	SetTLSClientAuthSanURI(tlsClientAuthSanURI string) ClientBuilder
    38  	SetExtraProperties(map[string]string) ClientBuilder
    39  
    40  	Build() (ClientMetadata, error)
    41  }
    42  
    43  type clientBuilder struct {
    44  	jwks                  []byte
    45  	jwksURI               string
    46  	idpClientMetadata     *clientMetadata
    47  	certificateMetadata   string
    48  	tlsClientAuthSanDNS   string
    49  	tlsClientAuthSanEmail string
    50  	tlsClientAuthSanIP    string
    51  	tlsClientAuthSanURI   string
    52  }
    53  
    54  // NewClientMetadataBuilder -  create a new instance of builder to construct client metadata
    55  func NewClientMetadataBuilder() ClientBuilder {
    56  	return &clientBuilder{
    57  		idpClientMetadata: &clientMetadata{},
    58  	}
    59  }
    60  
    61  func (b *clientBuilder) SetClientName(name string) ClientBuilder {
    62  	b.idpClientMetadata.ClientName = name
    63  	return b
    64  }
    65  
    66  func (b *clientBuilder) SetScopes(scopes []string) ClientBuilder {
    67  	b.idpClientMetadata.Scope = Scopes(scopes)
    68  	return b
    69  }
    70  
    71  func (b *clientBuilder) SetGrantTypes(grantTypes []string) ClientBuilder {
    72  	b.idpClientMetadata.GrantTypes = grantTypes
    73  	return b
    74  }
    75  
    76  func (b *clientBuilder) SetResponseType(responseTypes []string) ClientBuilder {
    77  	b.idpClientMetadata.ResponseTypes = responseTypes
    78  	return b
    79  }
    80  
    81  func (b *clientBuilder) SetTokenEndpointAuthMethod(tokenAuthMethod string) ClientBuilder {
    82  	b.idpClientMetadata.TokenEndpointAuthMethod = tokenAuthMethod
    83  	return b
    84  }
    85  
    86  func (b *clientBuilder) SetRedirectURIs(redirectURIs []string) ClientBuilder {
    87  	b.idpClientMetadata.RedirectURIs = redirectURIs
    88  	return b
    89  }
    90  
    91  func (b *clientBuilder) SetLogoURI(logoURI string) ClientBuilder {
    92  	b.idpClientMetadata.LogoURI = logoURI
    93  	return b
    94  }
    95  
    96  func (b *clientBuilder) SetJWKSURI(jwksURI string) ClientBuilder {
    97  	b.jwksURI = jwksURI
    98  	return b
    99  }
   100  
   101  func (b *clientBuilder) SetJWKS(jwks []byte) ClientBuilder {
   102  	b.jwks = jwks
   103  	return b
   104  }
   105  
   106  func (b *clientBuilder) SetCertificateMetadata(certificateMetadata string) ClientBuilder {
   107  	b.certificateMetadata = certificateMetadata
   108  	return b
   109  }
   110  
   111  func (b *clientBuilder) SetTLSClientAuthSanDNS(tlsClientAuthSanDNS string) ClientBuilder {
   112  	b.tlsClientAuthSanDNS = tlsClientAuthSanDNS
   113  	return b
   114  }
   115  
   116  func (b *clientBuilder) SetTLSClientAuthSanEmail(tlsClientAuthSanEmail string) ClientBuilder {
   117  	b.tlsClientAuthSanEmail = tlsClientAuthSanEmail
   118  	return b
   119  }
   120  
   121  func (b *clientBuilder) SetTLSClientAuthSanIP(tlsClientAuthSanIP string) ClientBuilder {
   122  	b.tlsClientAuthSanIP = tlsClientAuthSanIP
   123  	return b
   124  }
   125  
   126  func (b *clientBuilder) SetTLSClientAuthSanURI(tlsClientAuthSanURI string) ClientBuilder {
   127  	b.tlsClientAuthSanURI = tlsClientAuthSanURI
   128  	return b
   129  }
   130  
   131  func (b *clientBuilder) SetExtraProperties(extraProperties map[string]string) ClientBuilder {
   132  	b.idpClientMetadata.extraProperties = extraProperties
   133  	return b
   134  }
   135  
   136  func (b *clientBuilder) decodePublicKeyJWKS() (jwk.Key, error) {
   137  	p, err := util.ParsePublicKey(b.jwks)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	key, err := jwk.FromRaw(p)
   143  	if err != nil {
   144  		return nil, fmt.Errorf("failed to parse public key: %s", err)
   145  	}
   146  	kid, _ := util.ComputeKIDFromDER(b.jwks)
   147  	key.Set(jwk.KeyIDKey, kid)
   148  	key.Set(jwk.KeyUsageKey, jwk.ForSignature)
   149  
   150  	return key, nil
   151  }
   152  
   153  func (b *clientBuilder) decodeCertificateJWKS() (string, jwk.Key, error) {
   154  	pemBlock, _ := pem.Decode(b.jwks)
   155  	if pemBlock == nil {
   156  		return "", nil, fmt.Errorf("failed to decode certificate")
   157  	}
   158  
   159  	cert, err := x509.ParseCertificate(pemBlock.Bytes)
   160  	if err != nil {
   161  		return "", nil, fmt.Errorf("failed to parse certificate: %s", err)
   162  	}
   163  
   164  	subjectDN := cert.Subject.String()
   165  	key, err := jwk.FromRaw(cert.PublicKey)
   166  	if err != nil {
   167  		return "", nil, fmt.Errorf("failed to parse client certificate: %s", err)
   168  	}
   169  
   170  	c := &jwkcert.Chain{}
   171  	c.Add([]byte(base64.StdEncoding.EncodeToString(pemBlock.Bytes)))
   172  	key.Set(jwk.X509CertChainKey, c)
   173  	key.Set(jwk.KeyUsageKey, jwk.ForSignature)
   174  
   175  	return subjectDN, key, nil
   176  }
   177  
   178  func (b *clientBuilder) setClientMetadataJWKS(key jwk.Key) error {
   179  	jwksBuf, err := json.Marshal(key)
   180  	if err != nil {
   181  		return err
   182  	}
   183  	b.idpClientMetadata.Jwks = map[string]interface{}{
   184  		"keys": []json.RawMessage{json.RawMessage(jwksBuf)},
   185  	}
   186  
   187  	return nil
   188  }
   189  
   190  func (b *clientBuilder) setPrivateKeyJWTProperties() error {
   191  	if len(b.jwks) == 0 && len(b.jwksURI) == 0 {
   192  		return fmt.Errorf("public key is required for private_key_jwt token authentication method")
   193  	}
   194  	if len(b.jwks) != 0 {
   195  		key, err := b.decodePublicKeyJWKS()
   196  		if err != nil {
   197  			return err
   198  		}
   199  		b.setClientMetadataJWKS(key)
   200  	}
   201  	b.idpClientMetadata.JwksURI = b.jwksURI
   202  	return nil
   203  }
   204  
   205  func (b *clientBuilder) setTLSClientAuthProperties() error {
   206  	if len(b.jwks) == 0 && len(b.jwksURI) == 0 {
   207  		return fmt.Errorf("client certificate is required for tls_client_auth/self_signed_tls_client_auth token authentication method")
   208  	}
   209  	if len(b.jwks) != 0 {
   210  		subjectDN, jwks, err := b.decodeCertificateJWKS()
   211  		if err != nil {
   212  			return err
   213  		}
   214  		b.setClientMetadataJWKS(jwks)
   215  
   216  		switch b.certificateMetadata {
   217  		case TLSClientAuthSanDNS:
   218  			if b.tlsClientAuthSanDNS == "" {
   219  				return fmt.Errorf("no value provided for tls_client_auth_san_dns")
   220  			}
   221  			b.idpClientMetadata.TLSClientAuthSanDNS = b.tlsClientAuthSanDNS
   222  		case TLSClientAuthSanEmail:
   223  			if b.tlsClientAuthSanEmail == "" {
   224  				return fmt.Errorf("no value provided for tls_client_auth_san_email")
   225  			}
   226  			b.idpClientMetadata.TLSClientAuthSanEmail = b.tlsClientAuthSanEmail
   227  		case TLSClientAuthSanIP:
   228  			if b.tlsClientAuthSanIP == "" {
   229  				return fmt.Errorf("no value provided for tls_client_auth_san_ip")
   230  			}
   231  			b.idpClientMetadata.TLSClientAuthSanIP = b.tlsClientAuthSanIP
   232  		case TLSClientAuthSanURI:
   233  			if b.tlsClientAuthSanURI == "" {
   234  				return fmt.Errorf("no value provided for tls_client_auth_san_uri")
   235  			}
   236  			b.idpClientMetadata.TLSClientAuthSanURI = b.tlsClientAuthSanURI
   237  		default:
   238  			b.idpClientMetadata.TLSClientAuthSubjectDN = subjectDN
   239  		}
   240  	}
   241  	b.idpClientMetadata.JwksURI = b.jwksURI
   242  	return nil
   243  }
   244  
   245  func (b *clientBuilder) Build() (ClientMetadata, error) {
   246  	responseTypes := make(map[string]string)
   247  	for _, grantType := range b.idpClientMetadata.GrantTypes {
   248  		if _, ok := grantTypeWithRedirects[grantType]; ok && len(b.idpClientMetadata.RedirectURIs) == 0 {
   249  			return nil, fmt.Errorf("invalid client metadata redirect uri should be set for %s grant type", grantType)
   250  		}
   251  		switch grantType {
   252  		case GrantTypeAuthorizationCode:
   253  			responseTypes[AuthResponseCode] = AuthResponseCode
   254  		case GrantTypeImplicit:
   255  			responseTypes[AuthResponseToken] = AuthResponseToken
   256  		}
   257  	}
   258  	b.idpClientMetadata.ResponseTypes = make([]string, 0)
   259  	if len(responseTypes) > 0 {
   260  		for responseTypes := range responseTypes {
   261  			b.idpClientMetadata.ResponseTypes = append(b.idpClientMetadata.ResponseTypes, responseTypes)
   262  		}
   263  	}
   264  
   265  	switch b.idpClientMetadata.GetTokenEndpointAuthMethod() {
   266  	case config.PrivateKeyJWT:
   267  		err := b.setPrivateKeyJWTProperties()
   268  		if err != nil {
   269  			return nil, err
   270  		}
   271  	case config.TLSClientAuth:
   272  		fallthrough
   273  	case config.SelfSignedTLSClientAuth:
   274  		err := b.setTLSClientAuthProperties()
   275  		if err != nil {
   276  			return nil, err
   277  		}
   278  	}
   279  	return b.idpClientMetadata, nil
   280  }