fosite
fosite copied to clipboard
Add support for custom client secret validation
Preflight checklist
- [X] I could not find a solution in the existing issues, docs, nor discussions.
- [X] I agree to follow this project's Code of Conduct.
- [X] I have read and am following this repository's Contribution Guidelines.
- [ ] This issue affects my Ory Cloud project.
- [ ] I have joined the Ory Community Slack.
- [ ] I am signed up to the Ory Security Patch Newsletter.
Describe your problem
I would like to override the following method that validates the client secret:
func (f *Fosite) checkClientSecret(ctx context.Context, client Client, clientSecret []byte) error {
var err error
err = f.Config.GetSecretsHasher(ctx).Compare(ctx, client.GetHashedSecret(), clientSecret)
if err == nil {
return nil
}
cc, ok := client.(ClientWithSecretRotation)
if !ok {
return err
}
for _, hash := range cc.GetRotatedHashes() {
err = f.Config.GetSecretsHasher(ctx).Compare(ctx, hash, clientSecret)
if err == nil {
return nil
}
}
return err
}
My use case scenario would be with two services: A - User facing application that implements fosite B - Backend application, not internet exposed, with strict security policies that is responsible for managing clients (create, update, delete clients and storing the secrets)
- Service A would receive an OAuth request that requires
- Service A verifies if Client ID exists and is not
Public
- If it is private then it calls service B to verify client's secret
The following diagram better illustrates the infrastructure:
Describe your ideal solution
Create a new interface ClientSecretValidationStrategyProvider
that may be custom implemented using the Configurator
:
// ClientSecretValidationStrategy provides a method signature for validating a client secret
type ClientSecretValidationStrategy func(context.Context, Client, []byte) error
// ClientSecretValidationStrategyProvider returns the provider for configuring the client secret validation strategy.
type ClientSecretValidationStrategyProvider interface {
// GetClientSecretValidationStrategy returns the client secret validation strategy.
GetClientSecretValidationStrategy(ctx context.Context) ClientSecretValidationStrategy
}
type Configurator interface {
...
ClientSecretValidationStrategyProvider
...
}
func (f *Fosite) checkClientSecret(ctx context.Context, client Client, clientSecret []byte) error {
if s := f.Config.GetClientSecretValidationStrategy(ctx); s != nil {
return s(ctx, client, clientSecret)
}
return f.DefaultCheckClientSecret(ctx, client, clientSecret)
}
// DefaultCheckClientSecret provides the fosite's default client secret validation strategy.
func (f *Fosite) DefaultCheckClientSecret(ctx context.Context, client Client, clientSecret []byte) error {
var err error
err = f.Config.GetSecretsHasher(ctx).Compare(ctx, client.GetHashedSecret(), clientSecret)
if err == nil {
return nil
}
cc, ok := client.(ClientWithSecretRotation)
if !ok {
return err
}
for _, hash := range cc.GetRotatedHashes() {
err = f.Config.GetSecretsHasher(ctx).Compare(ctx, hash, clientSecret)
if err == nil {
return nil
}
}
return err
}
Workarounds or alternatives
Alternative 1: Overriding all methods that implements checkClientSecrets
:
-
DefaultClientAuthenticationStrategy
-> 170 lines method where I would only need to change the last 3 lines -
NewIntrospectionRequest
-> 68 lines method where I would only need to change 3 lines
This is far from ideal since it would be hard to maintain and keep-up with Fosite updates (even possibly security updates on these two specific methods).
Alternative 2: Implement a custom Hasher overriding the method Compare
Overriding Hasher interface would be more like an hack since it is intended to be used for a implementing hashing algorithms and not different secrets comparison flows.
Version
v0.42.2
Additional Context
No response