Skip to content

added post client auth method #289

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

ilgatnau
Copy link

@ilgatnau ilgatnau commented Jun 22, 2025

https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication

As per specs, multiple available Client Authentication methods that are used by Clients to authenticate to the Authorization Server when using the Token Endpoint. During Client Registration, the RP (Client) MAY register a Client Authentication method. If no method is registered, the default method is client_secret_basic.

These Client Authentication methods are:

  • client_secret_basic
  • client_secret_post
  • client_secret_jwt
  • private_key_jwt

This PR implements implementation for client_secret_post (currently only basic possible) plus placeholder for missing other 2 methods.

Signed-off-by: Ivan Gatnau Lopez <[email protected]>
Signed-off-by: Ivan Gatnau Lopez <[email protected]>
Signed-off-by: Ivan Gatnau Lopez <[email protected]>
Copy link
Collaborator

@nacx nacx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

I've left some comments on the PR. Thanks for raising!

In order to have the PR merged and in s state that can be safely maintained a couple things need to be added:

  • Unit tests validating that the POST method works as expected. The current PR only configures changes tests for the BASIC. Make sure there are unit tests for the case where no method is set, and when basic and post are set. The three cases need to be explicitly covered.
  • An end-to-end test that also exercises the post method. We use Keycloak in the e2e tests. Some suite should be updated to use the post mehtod and verify it is working as expected.

Having these two points properly implemented and covered will help us maintain the feature without breaking it accidentally in the future.

Thanks!

Comment on lines 132 to 134
// This section define swhether
// [Client Authentication](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication)
// is Basic (default) or method post.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// This section define swhether
// [Client Authentication](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication)
// is Basic (default) or method post.
// Available [Client Authentication](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication) methods

inthttp.HeaderAuthorization: []string{inthttp.BasicAuthHeader(o.config.GetClientId(), o.config.GetClientSecret())},
form, error := buildAuthParams(o.config, codeFromReq, stateFromReq)
if error != nil {
log.Error("error building form", error)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
log.Error("error building form", error)
log.Error("error building auth params", error)

// or if the implementation for the specified method is not supported.
func buildAuthHeader(config *oidcv1.OIDCConfig) (http.Header, error) {

headers := http.Header{}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need to allocate something here if it is going to be replaced.

Suggested change
headers := http.Header{}
var headers http.Header

case oidcv1.OIDCConfig_CLIENT_AUTHENTICATION_METHOD_CLIENT_SECRET_JWT:
// Build jwt auth header
// TODO: implement jwt auth header
return nil, errors.New("not implemented")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add more details in the error about what is not implemented so users can understand what is the problem when receiving the response back.

Comment on lines 437 to 440
params := url.Values{}
switch config.GetMethod() {
case oidcv1.OIDCConfig_CLIENT_AUTHENTICATION_METHOD_BASIC:
default:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same two comments as in the previous method.

ilgatnau added 3 commits June 24, 2025 21:23
Signed-off-by: Ivan Gatnau Lopez <[email protected]>
@ilgatnau
Copy link
Author

Thanks!

I've left some comments on the PR. Thanks for raising!

In order to have the PR merged and in s state that can be safely maintained a couple things need to be added:

  • Unit tests validating that the POST method works as expected. The current PR only configures changes tests for the BASIC. Make sure there are unit tests for the case where no method is set, and when basic and post are set. The three cases need to be explicitly covered.
  • An end-to-end test that also exercises the post method. We use Keycloak in the e2e tests. Some suite should be updated to use the post mehtod and verify it is working as expected.

Having these two points properly implemented and covered will help us maintain the feature without breaking it accidentally in the future.

Thanks!

Thank you for your comments @nacx, while I am trying to implement new tests for new client authentication methods I have some doubts how you guys would like to have this done as TestOIDCProcess looks like an uber test difficult to breakdown in more atomic tests.

I have for now duplicated this test as a reusable function I can call with different client authentication methods, would that be acceptable?

See commit ilgatnau@998f12b

Signed-off-by: Ivan Gatnau Lopez <[email protected]>
Copy link
Collaborator

@nacx nacx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good!

I've left mostly style nits.

There is one important validation thing as well.

Once this is addressed, the only thing left would be to exercise the POST authentication in one of the e2e tests (I'd just modify the keycloak suite to use that) and it should be good to go!

Comment on lines +140 to +149


// Clients create a JWT using an HMAC SHA algorithm, such as HMAC SHA-256 (currently not implemented)
CLIENT_AUTHENTICATION_METHOD_CLIENT_SECRET_JWT = 3;


// Clients that have registered a public key sign a JWT using that key (currently not implemented)
CLIENT_AUTHENTICATION_METHOD_PRIVATE_KEY_JWT = 4;


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style nit: remove the unnecessary blank lines

Suggested change
// Clients create a JWT using an HMAC SHA algorithm, such as HMAC SHA-256 (currently not implemented)
CLIENT_AUTHENTICATION_METHOD_CLIENT_SECRET_JWT = 3;
// Clients that have registered a public key sign a JWT using that key (currently not implemented)
CLIENT_AUTHENTICATION_METHOD_PRIVATE_KEY_JWT = 4;
// Clients create a JWT using an HMAC SHA algorithm, such as HMAC SHA-256 (currently not implemented)
CLIENT_AUTHENTICATION_METHOD_CLIENT_SECRET_JWT = 3;
// Clients that have registered a public key sign a JWT using that key (currently not implemented)
CLIENT_AUTHENTICATION_METHOD_PRIVATE_KEY_JWT = 4;

Comment on lines +409 to +422

// Build basic auth header
headers = http.Header{
inthttp.HeaderContentType: []string{inthttp.HeaderContentTypeFormURLEncoded},
inthttp.HeaderAuthorization: []string{inthttp.BasicAuthHeader(config.GetClientId(), config.GetClientSecret())},
}

case oidcv1.OIDCConfig_CLIENT_AUTHENTICATION_METHOD_CLIENT_SECRET_POST:

// Build post auth header
headers = http.Header{
inthttp.HeaderContentType: []string{inthttp.HeaderContentTypeFormURLEncoded},
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style nit: for consistency with other code blocks, remove the unnecessary blank lines here as well:

Suggested change
// Build basic auth header
headers = http.Header{
inthttp.HeaderContentType: []string{inthttp.HeaderContentTypeFormURLEncoded},
inthttp.HeaderAuthorization: []string{inthttp.BasicAuthHeader(config.GetClientId(), config.GetClientSecret())},
}
case oidcv1.OIDCConfig_CLIENT_AUTHENTICATION_METHOD_CLIENT_SECRET_POST:
// Build post auth header
headers = http.Header{
inthttp.HeaderContentType: []string{inthttp.HeaderContentTypeFormURLEncoded},
}
// Build basic auth header
headers = http.Header{
inthttp.HeaderContentType: []string{inthttp.HeaderContentTypeFormURLEncoded},
inthttp.HeaderAuthorization: []string{inthttp.BasicAuthHeader(config.GetClientId(), config.GetClientSecret())},
}
case oidcv1.OIDCConfig_CLIENT_AUTHENTICATION_METHOD_CLIENT_SECRET_POST:
// Build post auth header
headers = http.Header{
inthttp.HeaderContentType: []string{inthttp.HeaderContentTypeFormURLEncoded},
}

var params url.Values
switch config.GetClientAuthenticationMethod() {
case oidcv1.OIDCConfig_CLIENT_AUTHENTICATION_METHOD_BASIC:

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

"client_id": []string{config.GetClientId()},
"client_secret": []string{config.GetClientSecret()},
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

// Build jwt auth params
// TODO: implement jwt auth params
return nil, errors.New("client authentication method client_secret_jwt is not implemented")

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

Comment on lines +476 to +478

default:

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
default:
default:

Comment on lines +453 to +455

return params, nil

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style nit: for consistency with other code blocks, just rely on the method return. Let's just return in the cases or in the method, but let's not mix as it is confusing.

Suggested change
return params, nil

Comment on lines +486 to +487

return params, nil
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return params, nil


// Available [Client Authentication](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication)
// methods
ClientAuthenticationMethod client_authentication_method = 24;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the validation for this field. I think the lack of this validation is causing the current tests/examples to always fall to the default cases, as the values configured there are not defined in the enum.
Adding this validation should make the authservice fail early and not allow undefined values.

Suggested change
ClientAuthenticationMethod client_authentication_method = 24;
ClientAuthenticationMethod client_authentication_method = 24 [(validate.rules).enum.defined_only = true];

@nacx
Copy link
Collaborator

nacx commented Jul 1, 2025

BTW, to let the check test pass, can you run make format to get the code formatted?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants