Skip to content

Commit

Permalink
APIGOV-24312 - Support for using additional auth when communicating t…
Browse files Browse the repository at this point in the history
…o IdP (#672)

* APIGOV-24312 - Added support for using additional auth when communicating to IdP
- Changes to configure the agents to use client_secret_basic, client_secret_post, client_secret_jwt, private_key_jwt, tls_client_auth and self_signed_tls_client_auth
- Changes to acquire token based for the registered IdP provider on the configured auth types

* APIGOV-24312 - Fix tests

* APIGOV-24312 - IDP auth config validation

* APIGOV-24312 - Code review comments + fix for accessToken IdP auth type
  • Loading branch information
vivekschauhan authored Aug 10, 2023
1 parent 94d6a36 commit 9b23096
Show file tree
Hide file tree
Showing 16 changed files with 528 additions and 183 deletions.
37 changes: 10 additions & 27 deletions pkg/apic/auth/apicauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
package auth

import (
"crypto/rsa"
"fmt"
"io"
"net/http"
Expand All @@ -13,7 +12,6 @@ import (
"github.com/Axway/agent-sdk/pkg/api"
"github.com/Axway/agent-sdk/pkg/authz/oauth"
"github.com/Axway/agent-sdk/pkg/config"
"github.com/Axway/agent-sdk/pkg/util"
"github.com/Axway/agent-sdk/pkg/util/log"
)

Expand Down Expand Up @@ -90,11 +88,10 @@ func NewPlatformTokenGetter(privKey, publicKey, password, url, aud, clientID str
func NewPlatformTokenGetterWithCentralConfig(centralCfg config.CentralConfig) PlatformTokenGetter {
return &platformTokenGetter{
cfg: centralCfg,
keyReader: &keyReader{
privKey: centralCfg.GetAuthConfig().GetPrivateKey(),
publicKey: centralCfg.GetAuthConfig().GetPublicKey(),
password: centralCfg.GetAuthConfig().GetKeyPassword(),
},
keyReader: oauth.NewKeyReader(
centralCfg.GetAuthConfig().GetPrivateKey(),
centralCfg.GetAuthConfig().GetPublicKey(),
centralCfg.GetAuthConfig().GetKeyPassword()),
}
}

Expand All @@ -114,25 +111,10 @@ func staticTokenGetter(token string) funcTokenGetter {
return funcTokenGetter(func() (string, error) { return token, nil })
}

type keyReader struct {
privKey string // path to rsa encoded private key, used to sign platform tokens
publicKey string // path to the rsa encoded public key
password string // path to password for private key
}

func (kr *keyReader) getPrivateKey() (*rsa.PrivateKey, error) {
return util.ReadPrivateKeyFile(kr.privKey, kr.password)
}

// getPublicKey from the path provided
func (kr *keyReader) getPublicKey() ([]byte, error) {
return util.ReadPublicKeyBytes(kr.publicKey)
}

// platformTokenGetter can get an access token from apic platform.
type platformTokenGetter struct {
cfg config.CentralConfig
*keyReader
cfg config.CentralConfig
keyReader oauth.KeyReader
axwayIDClient oauth.AuthClient
}

Expand All @@ -142,12 +124,12 @@ func (ptp *platformTokenGetter) Close() error {
}

func (ptp *platformTokenGetter) initAxwayIDPClient() error {
privateKey, err := ptp.getPrivateKey()
privateKey, err := ptp.keyReader.GetPrivateKey()
if err != nil {
return err
}

publicKey, err := ptp.getPublicKey()
publicKey, err := ptp.keyReader.GetPublicKey()
if err != nil {
return err
}
Expand All @@ -162,9 +144,10 @@ func (ptp *platformTokenGetter) initAxwayIDPClient() error {
oauth.WithServerName("AxwayId"),
oauth.WithKeyPairAuth(
ptp.cfg.GetAuthConfig().GetClientID(),
"",
ptp.cfg.GetAuthConfig().GetAudience(),
privateKey,
publicKey))
publicKey, ""))

return err
}
Expand Down
68 changes: 0 additions & 68 deletions pkg/apic/auth/apicauth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,74 +45,6 @@ func TestChannelTokenGetterCloses(t *testing.T) {
}
}

func TestGetKey(t *testing.T) {
cases := []struct {
description string
kr *keyReader
}{
{
"no password",
&keyReader{
privKey: "testdata/private_key.pem",
},
},
{
"with empty password file",
&keyReader{
privKey: "testdata/private_key.pem",
password: "testdata/password_empty",
},
},
{
"with password",
&keyReader{
privKey: "testdata/private_key_with_pwd.pem",
password: "testdata/password",
},
},
}

for _, testCase := range cases {
if _, err := testCase.kr.getPrivateKey(); err != nil {
t.Errorf("testcase: %s: failed to read rsa key %s", testCase.description, err)
}
}
}

func TestGetPublicKey(t *testing.T) {
cases := []struct {
description string
kr *keyReader
}{
{
"with public key",
&keyReader{
publicKey: "testdata/public_key",
},
},
{
"with private and public key",
&keyReader{
privKey: "testdata/private_key.pem",
publicKey: "testdata/public_key",
},
},
{
"with private, public key, and password",
&keyReader{
privKey: "testdata/private_key_with_pwd.pem",
password: "testdata/password",
publicKey: "testdata/public_key",
},
},
}
for _, testCase := range cases {
if _, err := testCase.kr.getPublicKey(); err != nil {
t.Errorf("testcase: %s: failed to read public key %s", testCase.description, err)
}
}
}

func TestNetAuthenticate(t *testing.T) {
aa := NewWithStatic("12345", "abcde")
if aa.tenantID != "12345" {
Expand Down
68 changes: 54 additions & 14 deletions pkg/authz/oauth/authclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type authClient struct {
}

type authenticator interface {
prepareRequest() (url.Values, error)
prepareRequest() (url.Values, map[string]string, error)
}

type tokenResponse struct {
Expand Down Expand Up @@ -79,25 +79,61 @@ func WithServerName(serverName string) AuthClientOption {
}
}

// WithClientSecretAuth - sets up to use client secret authenticator
func WithClientSecretAuth(clientID, clientSecret, scope string) AuthClientOption {
// WithClientSecretBasicAuth - sets up to use client secret basic authenticator
func WithClientSecretBasicAuth(clientID, clientSecret, scope string) AuthClientOption {
return func(opt *authClientOptions) {
opt.authenticator = &clientSecretAuthenticator{
opt.authenticator = &clientSecretBasicAuthenticator{
clientID,
clientSecret,
scope,
}
}
}

// WithClientSecretPostAuth - sets up to use client secret authenticator
func WithClientSecretPostAuth(clientID, clientSecret, scope string) AuthClientOption {
return func(opt *authClientOptions) {
opt.authenticator = &clientSecretPostAuthenticator{
clientID,
clientSecret,
scope,
}
}
}

// WithClientSecretJwtAuth - sets up to use client secret authenticator
func WithClientSecretJwtAuth(clientID, clientSecret, scope, issuer, aud string) AuthClientOption {
return func(opt *authClientOptions) {
opt.authenticator = &clientSecretJwtAuthenticator{
clientID,
clientSecret,
scope,
issuer,
aud,
}
}
}

// WithKeyPairAuth - sets up to use public/private key pair authenticator
func WithKeyPairAuth(clientID, audience string, privKey *rsa.PrivateKey, publicKey []byte) AuthClientOption {
func WithKeyPairAuth(clientID, issuer, audience string, privKey *rsa.PrivateKey, publicKey []byte, scope string) AuthClientOption {
return func(opt *authClientOptions) {
opt.authenticator = &keyPairAuthenticator{
clientID,
issuer,
audience,
privKey,
publicKey,
scope,
}
}
}

// WithTLSClientAuth - sets up to use tls_client_auth and self_signed_tls_client_auth authenticator
func WithTLSClientAuth(clientID, scope string) AuthClientOption {
return func(opt *authClientOptions) {
opt.authenticator = &tlsClientAuthenticator{
clientID: clientID,
scope: scope,
}
}
}
Expand Down Expand Up @@ -145,12 +181,12 @@ func (c *authClient) fetchNewToken() (string, error) {
}

func (c *authClient) getOAuthTokens() (*tokenResponse, error) {
req, err := c.options.authenticator.prepareRequest()
req, headers, err := c.options.authenticator.prepareRequest()
if err != nil {
return nil, err
}

resp, err := c.postAuthForm(req)
resp, err := c.postAuthForm(req, headers)
if err != nil {
return nil, err
}
Expand All @@ -175,14 +211,18 @@ func (c *authClient) getOAuthTokens() (*tokenResponse, error) {
return &tokens, nil
}

func (c *authClient) postAuthForm(data url.Values) (resp *api.Response, err error) {
func (c *authClient) postAuthForm(data url.Values, headers map[string]string) (resp *api.Response, err error) {
reqHeaders := map[string]string{
hdrContentType: mimeApplicationFormURLEncoded,
}
for name, value := range headers {
reqHeaders[name] = value
}
req := api.Request{
Method: api.POST,
URL: c.tokenURL,
Body: []byte(data.Encode()),
Headers: map[string]string{
hdrContentType: mimeApplicationFormURLEncoded,
},
Method: api.POST,
URL: c.tokenURL,
Body: []byte(data.Encode()),
Headers: reqHeaders,
}
return c.apiClient.Send(req)
}
8 changes: 4 additions & 4 deletions pkg/authz/oauth/authclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func TestGetPlatformTokensHttpError(t *testing.T) {
s.SetTokenResponse("", 0, http.StatusBadRequest)
ac, err := NewAuthClient(s.GetTokenURL(), apiClient,
WithServerName("testServer"),
WithClientSecretAuth("invalid_client", "invalid-secrt", ""))
WithClientSecretPostAuth("invalid_client", "invalid-secrt", ""))
assert.Nil(t, err)
assert.NotNil(t, ac)

Expand All @@ -78,7 +78,7 @@ func TestGetPlatformTokensHttpError(t *testing.T) {
s.SetTokenResponse("", 0, http.StatusBadRequest)
ac, err = NewAuthClient(s.GetTokenURL(), apiClient,
WithServerName("testServer"),
WithKeyPairAuth("invalid_client", "", privateKey, publicKey))
WithKeyPairAuth("invalid_client", "", "", privateKey, publicKey, ""))
assert.Nil(t, err)
assert.NotNil(t, ac)

Expand All @@ -88,7 +88,7 @@ func TestGetPlatformTokensHttpError(t *testing.T) {
s.SetTokenResponse("token", 3*time.Second, http.StatusOK)
ac, err = NewAuthClient(s.GetTokenURL(), apiClient,
WithServerName("testServer"),
WithKeyPairAuth("invalid_client", "", privateKey, publicKey))
WithKeyPairAuth("invalid_client", "", "", privateKey, publicKey, ""))
assert.Nil(t, err)
assert.NotNil(t, ac)

Expand All @@ -106,7 +106,7 @@ func TestGetPlatformTokensTimeout(t *testing.T) {
apiClient := api.NewClientWithTimeout(config.NewTLSConfig(), "", time.Second)
ac, err := NewAuthClient(s.URL, apiClient,
WithServerName("testServer"),
WithClientSecretAuth("invalid_client", "invalid-secrt", ""))
WithClientSecretPostAuth("invalid_client", "invalid-secrt", ""))

assert.Nil(t, err)
assert.NotNil(t, ac)
Expand Down
9 changes: 9 additions & 0 deletions pkg/authz/oauth/authservermetadata.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
package oauth

type MTLSEndPointAlias struct {
TokenEndpoint string `json:"token_endpoint,omitempty"`
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`
RevocationEndpoint string `json:"revocation_endpoint,omitempty"`
}

// AuthorizationServerMetadata - OAuth metadata from IdP
type AuthorizationServerMetadata struct {
Issuer string `json:"issuer,omitempty"`
Expand Down Expand Up @@ -27,4 +34,6 @@ type AuthorizationServerMetadata struct {

RequestParameterSupported bool `json:"request_parameter_supported,omitempty"`
RequestObjectSigningAlgValuesSupported []string `json:"request_object_signing_alg_values_supported,omitempty"`

MTLSEndPointAlias *MTLSEndPointAlias `json:"mtls_endpoint_aliases,omitempty"`
}
28 changes: 28 additions & 0 deletions pkg/authz/oauth/clientsecretbasicauthenticator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package oauth

import (
"encoding/base64"
"net/url"
)

type clientSecretBasicAuthenticator struct {
clientID string
clientSecret string
scope string
}

func (p *clientSecretBasicAuthenticator) prepareRequest() (url.Values, map[string]string, error) {
v := url.Values{
metaGrantType: []string{grantClientCredentials},
}

if p.scope != "" {
v.Add(metaScope, p.scope)
}

token := base64.StdEncoding.EncodeToString([]byte(p.clientID + ":" + p.clientSecret))
headers := map[string]string{
"Authorization": "Basic " + token,
}
return v, headers, nil
}
Loading

0 comments on commit 9b23096

Please sign in to comment.