Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/Configuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ Root level key `server`
| `auth.dpopskew` | The amount of time drift allowed between when the client generated a dpop proof and the server time. | `1h` | OPENTDF_SERVER_AUTH |
| `auth.skew` | The amount of time drift allowed between a tokens `exp` claim and the server time. | `1m` | OPENTDF_SERVER_AUTH_SKEW |
| `auth.public_client_id` | [DEPRECATED] The oidc client id. This is leveraged by otdfctl. | | OPENTDF_SERVER_AUTH_PUBLIC_CLIENT_ID |
| `auth.enforceDPoP` | If true, DPoP bindings on Access Tokens are enforced. | `false` | OPENTDF_SERVER_AUTH_ENFORCEDPOP |
| `auth.dpop.enforce` | If true, DPoP bindings on Access Tokens are enforced. | `false` | OPENTDF_SERVER_AUTH_DPOP_ENFORCE |
| `auth.enforceDPoP` | [DEPRECATED] Use `auth.dpop.enforce`. Still honored: DPoP is enforced when either field is true. | `false` | OPENTDF_SERVER_AUTH_ENFORCEDPOP |
| `cryptoProvider` | A list of public/private keypairs and their use. Described [below](#crypto-provider) | empty | |
| `enable_pprof` | Enable golang performance profiling | `false` | OPENTDF_SERVER_ENABLE_PPROF |
| `grpc.reflection` | The configuration for the grpc server. | `true` | OPENTDF_SERVER_GRPC_REFLECTION |
Expand Down Expand Up @@ -585,7 +586,8 @@ opentdf-example.yaml:
server:
auth:
enabled: true
enforceDPoP: false
dpop:
enforce: false
# public_client_id: 'opentdf-public' # DEPRECATED
audience: 'http://localhost:8080'
issuer: http://keycloak:8888/auth/realms/opentdf
Expand Down
3 changes: 2 additions & 1 deletion opentdf-core-mode.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ audit:
server:
auth:
enabled: false
enforceDPoP: false
dpop:
enforce: false
audience: 'http://localhost:8080'
issuer: http://localhost:8888/auth/realms/tdf
cors:
Expand Down
3 changes: 2 additions & 1 deletion opentdf-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ server:
key: ./keys/platform-key.pem
auth:
enabled: true
enforceDPoP: false
dpop:
enforce: false
audience: "http://localhost:8080"
issuer: http://localhost:8888/auth/realms/opentdf
policy:
Expand Down
3 changes: 2 additions & 1 deletion opentdf-ers-mode.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ services:
server:
auth:
enabled: true
enforceDPoP: false
dpop:
enforce: false
audience: "http://localhost:8080"
issuer: http://localhost:8888/auth/realms/opentdf
policy:
Expand Down
3 changes: 2 additions & 1 deletion opentdf-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ services:
server:
auth:
enabled: true
enforceDPoP: false
dpop:
enforce: false
audience: "http://localhost:8080"
issuer: http://keycloak:8888/auth/realms/opentdf
policy:
Expand Down
3 changes: 2 additions & 1 deletion opentdf-kas-mode.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ server:
key: ./keys/platform-key.pem
auth:
enabled: true
enforceDPoP: false
dpop:
enforce: false
audience: "http://localhost:8080"
issuer: http://localhost:8888/auth/realms/opentdf
policy:
Expand Down
2 changes: 1 addition & 1 deletion service/internal/auth/authn.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ func NewAuthenticator(ctx context.Context, cfg Config, logger *logger.Logger, we
}

a := &Authentication{
enforceDPoP: cfg.EnforceDPoP,
enforceDPoP: cfg.dpopEnforced(),
strictDPoPHTU: cfg.DPoP.StrictHTU,
logger: logger,
dpopNonceManager: newDPoPNonceManager(cfg.DPoP.RequireNonce, cfg.DPoP.NonceExpiration),
Expand Down
52 changes: 23 additions & 29 deletions service/internal/auth/authn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,12 @@ func (s *AuthSuite) SetupTest() {
context.Background(),
Config{
AuthNConfig: AuthNConfig{
EnforceDPoP: true,
Issuer: s.server.URL,
Audience: "test",
DPoPSkew: time.Hour,
TokenSkew: time.Minute,
Policy: policyCfg,
Issuer: s.server.URL,
Audience: "test",
DPoPSkew: time.Hour,
TokenSkew: time.Minute,
Policy: policyCfg,
DPoP: DPoPConfig{Enforce: true},
},
PublicRoutes: []string{
"/public",
Expand Down Expand Up @@ -496,10 +496,9 @@ func (s *AuthSuite) Test_MuxHandler_RoleProviderErrorReturnsInternalServerError(
s.Require().NoError(defaults.Set(&policyCfg))
auth, err := NewAuthenticator(s.T().Context(), Config{
AuthNConfig: AuthNConfig{
EnforceDPoP: false,
Issuer: s.server.URL,
Audience: "test",
Policy: policyCfg,
Issuer: s.server.URL,
Audience: "test",
Policy: policyCfg,
},
RoleProvider: staticProvider{err: errors.New("role provider unavailable")},
}, logger.CreateTestLogger(), func(_ string, _ any) error { return nil })
Expand Down Expand Up @@ -532,10 +531,9 @@ func (s *AuthSuite) Test_MuxHandler_UsesAuthorizer() {
s.Require().NoError(defaults.Set(&policyCfg))
auth, err := NewAuthenticator(s.T().Context(), Config{
AuthNConfig: AuthNConfig{
EnforceDPoP: false,
Issuer: s.server.URL,
Audience: "test",
Policy: policyCfg,
Issuer: s.server.URL,
Audience: "test",
Policy: policyCfg,
},
}, logger.CreateTestLogger(), func(_ string, _ any) error { return nil })
s.Require().NoError(err)
Expand Down Expand Up @@ -575,10 +573,9 @@ func (s *AuthSuite) Test_MuxHandler_NilAuthorizerReturnsInternalServerError() {
s.Require().NoError(defaults.Set(&policyCfg))
auth, err := NewAuthenticator(s.T().Context(), Config{
AuthNConfig: AuthNConfig{
EnforceDPoP: false,
Issuer: s.server.URL,
Audience: "test",
Policy: policyCfg,
Issuer: s.server.URL,
Audience: "test",
Policy: policyCfg,
},
}, logger.CreateTestLogger(), func(_ string, _ any) error { return nil })
s.Require().NoError(err)
Expand Down Expand Up @@ -611,10 +608,9 @@ func (s *AuthSuite) Test_MuxHandler_NilAuthorizerDecisionReturnsInternalServerEr
s.Require().NoError(defaults.Set(&policyCfg))
auth, err := NewAuthenticator(s.T().Context(), Config{
AuthNConfig: AuthNConfig{
EnforceDPoP: false,
Issuer: s.server.URL,
Audience: "test",
Policy: policyCfg,
Issuer: s.server.URL,
Audience: "test",
Policy: policyCfg,
},
}, logger.CreateTestLogger(), func(_ string, _ any) error { return nil })
s.Require().NoError(err)
Expand Down Expand Up @@ -655,10 +651,9 @@ func (s *AuthSuite) Test_MuxHandler_V2WithoutResourceContextFailsClosedForDimens
s.Require().NoError(defaults.Set(&policyCfg))
auth, err := NewAuthenticator(s.T().Context(), Config{
AuthNConfig: AuthNConfig{
EnforceDPoP: false,
Issuer: s.server.URL,
Audience: "test",
Policy: policyCfg,
Issuer: s.server.URL,
Audience: "test",
Policy: policyCfg,
},
}, logger.CreateTestLogger(), func(_ string, _ any) error { return nil })
s.Require().NoError(err)
Expand Down Expand Up @@ -1379,9 +1374,8 @@ func makeDPoPToken(t *testing.T, tc dpopTestCase) string {

func (s *AuthSuite) Test_Allowing_Auth_With_No_DPoP() {
authnConfig := AuthNConfig{
EnforceDPoP: false,
Issuer: s.server.URL,
Audience: "test",
Issuer: s.server.URL,
Audience: "test",
}
config := Config{}
config.AuthNConfig = authnConfig
Expand Down
19 changes: 17 additions & 2 deletions service/internal/auth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ type Config struct {

// AuthNConfig is the configuration need for the platform to validate tokens
type AuthNConfig struct { //nolint:revive // AuthNConfig is a valid name
// Deprecated: use DPoP.Enforce (server.auth.dpop.enforce) instead. Still honored
// during the migration window: DPoP is enforced when either field is true.
EnforceDPoP bool `mapstructure:"enforceDPoP" json:"enforceDPoP" default:"false"`
Issuer string `mapstructure:"issuer" json:"issuer"`
Audience string `mapstructure:"audience" json:"audience"`
Expand All @@ -34,7 +36,16 @@ type AuthNConfig struct { //nolint:revive // AuthNConfig is a valid name
DPoP DPoPConfig `mapstructure:"dpop" json:"dpop"`
}

// dpopEnforced reports whether DPoP-bound tokens are required. Enforcement is on
// when either the new DPoP.Enforce field or the deprecated EnforceDPoP field is set.
func (c AuthNConfig) dpopEnforced() bool {
return c.DPoP.Enforce || c.EnforceDPoP // honor deprecated EnforceDPoP during migration
}

type DPoPConfig struct {
// Enforce requires access tokens to be DPoP-bound. Replaces the deprecated
// top-level server.auth.enforceDPoP field.
Enforce bool `mapstructure:"enforce" json:"enforce" default:"false"`
RequireNonce bool `mapstructure:"require_nonce" json:"require_nonce" default:"false"`
NonceExpiration time.Duration `mapstructure:"nonce_expiration" json:"nonce_expiration" default:"5m"`
// StrictHTU requires the htu claim in DPoP JWTs to include the origin
Expand All @@ -59,8 +70,12 @@ func (c AuthNConfig) validateAuthNConfig(logger *logger.Logger) error {
return errors.New("config Auth.Audience is required")
}

if !c.EnforceDPoP {
logger.Warn("config Auth.EnforceDPoP is false. DPoP will not be enforced.")
if c.EnforceDPoP {
logger.Warn("config server.auth.enforceDPoP is deprecated; use server.auth.dpop.enforce instead")
}

if !c.dpopEnforced() {
logger.Warn("DPoP is not enforced; set server.auth.dpop.enforce: true to require DPoP-bound tokens")
}

if err := c.DPoP.Validate(); err != nil {
Expand Down
26 changes: 21 additions & 5 deletions service/internal/auth/dpop_nonce_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,22 @@ func TestDPoPProofError(t *testing.T) {
})
}

func TestDPoPEnforcement_Migration(t *testing.T) {
t.Run("new dpop.enforce field enables enforcement", func(t *testing.T) {
cfg := AuthNConfig{DPoP: DPoPConfig{Enforce: true}}
assert.True(t, cfg.dpopEnforced())
})

t.Run("deprecated enforceDPoP field still enables enforcement", func(t *testing.T) {
cfg := AuthNConfig{EnforceDPoP: true} // verifying the deprecated field is still honored
assert.True(t, cfg.dpopEnforced())
})

t.Run("neither set means not enforced", func(t *testing.T) {
assert.False(t, AuthNConfig{}.dpopEnforced())
})
}

func TestDPoPAlgorithmRestrictions(t *testing.T) {
testCases := []struct {
alg jwa.SignatureAlgorithm
Expand Down Expand Up @@ -167,12 +183,12 @@ func (s *AuthSuite) newAuthDPoP(requireNonce bool) *Authentication {
context.Background(),
Config{
AuthNConfig: AuthNConfig{
EnforceDPoP: true,
Issuer: s.server.URL,
Audience: "test",
DPoPSkew: time.Hour,
TokenSkew: time.Minute,
Issuer: s.server.URL,
Audience: "test",
DPoPSkew: time.Hour,
TokenSkew: time.Minute,
DPoP: DPoPConfig{
Enforce: true,
RequireNonce: requireNonce,
NonceExpiration: 5 * time.Minute,
StrictHTU: false,
Expand Down
4 changes: 2 additions & 2 deletions service/internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ func NewOpenTDFServer(config Config, logger *logger.Logger, cacheManager *cache.
}
logger.Debug("authentication interceptor enabled")
} else {
logger.Warn("disabling authentication. this is deprecated and will be removed. if you are using an IdP without DPoP set `enforceDPoP = false`")
logger.Warn("disabling authentication. this is deprecated and will be removed. if you are using an IdP without DPoP set `server.auth.dpop.enforce = false`")
}

var ipcAuthInts []connect.Interceptor
Expand Down Expand Up @@ -349,7 +349,7 @@ func newHTTPServer(c Config, connectRPC http.Handler, extraHTTP http.Handler, a
if c.Auth.Enabled {
httpHandler = a.MuxHandler(httpHandler)
} else {
l.Error("disabling authentication. this is deprecated and will be removed. if you are using an IdP without DPoP set `enforceDPoP = false`")
l.Error("disabling authentication. this is deprecated and will be removed. if you are using an IdP without DPoP set `server.auth.dpop.enforce = false`")
}

// CORS
Expand Down
2 changes: 1 addition & 1 deletion service/pkg/server/testdata/all-no-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ server:
key: ./keys/platform-key.pem
auth:
enabled: true
enforceDPoP: false
enforceDPoP: false # deprecated alias for dpop.enforce; kept here for backward-compat parse coverage
audience: "http://localhost:8080"
issuer: http://localhost:8888/auth/realms/opentdf
policy:
Expand Down
Loading