Skip to content

Commit

Permalink
fix: audience assertion adheres to RFC-7519, section 4.1.3 (#1237)
Browse files Browse the repository at this point in the history
  • Loading branch information
dadrus authored Mar 12, 2024
1 parent 8d1c7fe commit 560a470
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 63 deletions.
2 changes: 1 addition & 1 deletion docs/content/docs/configuration/types.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Required scopes given to the client.

* *`audience`*: _string array_ (optional)
+
Required entries in the `aud` claim. Both cases, either as whitespace separated string, or a JSON array are considered.
Values to be matched in the `aud` claim. This assertion evaluates to `true` if at least one entry in the given `audience` array is present in the `aud` claim. Both cases, either as whitespace separated string, or a JSON array are considered.

* *`issuers`*: _string array_ (optional)
+
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ assertions:

// assertions settings
require.NoError(t, auth.a.ScopesMatcher.Match([]string{}))
assert.Empty(t, auth.a.TargetAudiences)
assert.Empty(t, auth.a.Audiences)
assert.Len(t, auth.a.TrustedIssuers, 1)
assert.Contains(t, auth.a.TrustedIssuers, "foobar")
assert.Len(t, auth.a.AllowedAlgorithms, 6)
Expand Down Expand Up @@ -255,7 +255,7 @@ cache_ttl: 5s`),

// assertions settings
require.NoError(t, auth.a.ScopesMatcher.Match([]string{}))
assert.Empty(t, auth.a.TargetAudiences)
assert.Empty(t, auth.a.Audiences)
assert.Len(t, auth.a.TrustedIssuers, 1)
assert.Contains(t, auth.a.TrustedIssuers, "foobar")
assert.Len(t, auth.a.AllowedAlgorithms, 6)
Expand Down Expand Up @@ -343,7 +343,7 @@ trust_store: ` + trustStorePath),
// assertions settings
assert.NotNil(t, auth.a.ScopesMatcher)
require.NoError(t, auth.a.ScopesMatcher.Match([]string{"foo"}))
assert.Empty(t, auth.a.TargetAudiences)
assert.Empty(t, auth.a.Audiences)
assert.Len(t, auth.a.TrustedIssuers, 1)
assert.Contains(t, auth.a.TrustedIssuers, "foobar")
assert.Len(t, auth.a.AllowedAlgorithms, 1)
Expand Down Expand Up @@ -412,7 +412,7 @@ cache_ttl: 5s`),

// assertions settings
require.NoError(t, auth.a.ScopesMatcher.Match([]string{}))
assert.Empty(t, auth.a.TargetAudiences)
assert.Empty(t, auth.a.Audiences)
assert.Empty(t, auth.a.TrustedIssuers)
assert.Len(t, auth.a.AllowedAlgorithms, 6)
assert.ElementsMatch(t, auth.a.AllowedAlgorithms, []string{
Expand Down Expand Up @@ -549,7 +549,7 @@ assertions:
assert.NotEqual(t, prototype.a, configured.a)

require.NoError(t, configured.a.ScopesMatcher.Match([]string{}))
assert.Empty(t, configured.a.TargetAudiences)
assert.Empty(t, configured.a.Audiences)
assert.ElementsMatch(t, configured.a.TrustedIssuers, []string{"barfoo"})
assert.ElementsMatch(t, configured.a.AllowedAlgorithms, []string{string(jose.ES512)})

Expand Down Expand Up @@ -591,7 +591,7 @@ cache_ttl: 5s`),
assert.NotEqual(t, prototype.a, configured.a)

require.NoError(t, configured.a.ScopesMatcher.Match([]string{}))
assert.Empty(t, configured.a.TargetAudiences)
assert.Empty(t, configured.a.Audiences)
assert.ElementsMatch(t, configured.a.TrustedIssuers, []string{"barfoo"})
assert.ElementsMatch(t, configured.a.AllowedAlgorithms, []string{string(jose.ES512)})

Expand Down Expand Up @@ -632,7 +632,7 @@ assertions:
assert.NotEqual(t, prototype.a, configured.a)

require.NoError(t, configured.a.ScopesMatcher.Match([]string{}))
assert.Empty(t, configured.a.TargetAudiences)
assert.Empty(t, configured.a.Audiences)
assert.ElementsMatch(t, configured.a.TrustedIssuers, []string{"barfoo"})
assert.ElementsMatch(t, configured.a.AllowedAlgorithms, []string{string(jose.ES512)})

Expand Down Expand Up @@ -705,7 +705,7 @@ assertions:
assert.Equal(t, prototype.ttl, configured.ttl)

assert.Equal(t, prototype.a.TrustedIssuers, configured.a.TrustedIssuers)
assert.Equal(t, prototype.a.TargetAudiences, configured.a.TargetAudiences)
assert.Equal(t, prototype.a.Audiences, configured.a.Audiences)
assert.Equal(t, prototype.a.AllowedAlgorithms, configured.a.AllowedAlgorithms)
assert.Equal(t, prototype.a.ValidityLeeway, configured.a.ValidityLeeway)
assert.NotEqual(t, prototype.a.ScopesMatcher, configured.a.ScopesMatcher)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ assertions:
assert.Contains(t, auth.a.TrustedIssuers, "foobar")
require.NoError(t, auth.a.ScopesMatcher.Match([]string{}))
assert.Equal(t, time.Duration(0), auth.a.ValidityLeeway)
assert.Empty(t, auth.a.TargetAudiences)
assert.Empty(t, auth.a.Audiences)

// assert ttl
assert.Nil(t, auth.ttl)
Expand Down Expand Up @@ -240,7 +240,7 @@ allow_fallback_on_error: true
assert.Contains(t, auth.a.TrustedIssuers, "foobar")
require.NoError(t, auth.a.ScopesMatcher.Match([]string{"foo"}))
assert.Equal(t, time.Duration(0), auth.a.ValidityLeeway)
assert.Empty(t, auth.a.TargetAudiences)
assert.Empty(t, auth.a.Audiences)

// assert ttl
assert.Equal(t, 5*time.Second, *auth.ttl)
Expand Down Expand Up @@ -302,7 +302,7 @@ metadata_endpoint:
assert.Empty(t, auth.a.TrustedIssuers, 1)
require.NoError(t, auth.a.ScopesMatcher.Match([]string{}))
assert.Equal(t, time.Duration(0), auth.a.ValidityLeeway)
assert.Empty(t, auth.a.TargetAudiences)
assert.Empty(t, auth.a.Audiences)

// assert ttl
assert.Nil(t, auth.ttl)
Expand Down Expand Up @@ -430,7 +430,7 @@ assertions:
assert.NotEqual(t, prototype.a, configured.a)

require.NoError(t, configured.a.ScopesMatcher.Match([]string{}))
assert.ElementsMatch(t, configured.a.TargetAudiences, []string{"baz"})
assert.ElementsMatch(t, configured.a.Audiences, []string{"baz"})
assert.ElementsMatch(t, configured.a.TrustedIssuers, []string{"barfoo"})
assert.ElementsMatch(t, configured.a.AllowedAlgorithms, []string{string(jose.ES512)})

Expand Down Expand Up @@ -502,7 +502,7 @@ cache_ttl: 5s`),
assert.NotEqual(t, prototype.a, configured.a)

require.NoError(t, configured.a.ScopesMatcher.Match([]string{}))
assert.Empty(t, configured.a.TargetAudiences)
assert.Empty(t, configured.a.Audiences)
assert.ElementsMatch(t, configured.a.TrustedIssuers, []string{"barfoo"})
assert.ElementsMatch(t, configured.a.AllowedAlgorithms, []string{string(jose.ES512)})

Expand Down
36 changes: 18 additions & 18 deletions internal/rules/mechanisms/oauth2/claims_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ func TestClaimsValidate(t *testing.T) {
Audience: Audience{"bar"},
},
expectations: Expectation{
TrustedIssuers: []string{"foo"},
TargetAudiences: []string{"foo"},
TrustedIssuers: []string{"foo"},
Audiences: []string{"foo"},
},
assert: func(t *testing.T, err error) {
t.Helper()
Expand All @@ -76,8 +76,8 @@ func TestClaimsValidate(t *testing.T) {
NotBefore: &dateInTheFuture,
},
expectations: Expectation{
TrustedIssuers: []string{"foo"},
TargetAudiences: []string{"bar"},
TrustedIssuers: []string{"foo"},
Audiences: []string{"bar"},
},
assert: func(t *testing.T, err error) {
t.Helper()
Expand All @@ -95,8 +95,8 @@ func TestClaimsValidate(t *testing.T) {
IssuedAt: &dateInTheFuture,
},
expectations: Expectation{
TrustedIssuers: []string{"foo"},
TargetAudiences: []string{"bar"},
TrustedIssuers: []string{"foo"},
Audiences: []string{"bar"},
},
assert: func(t *testing.T, err error) {
t.Helper()
Expand All @@ -115,9 +115,9 @@ func TestClaimsValidate(t *testing.T) {
Scp: Scopes{"foo", "bar"},
},
expectations: Expectation{
TrustedIssuers: []string{"foo"},
TargetAudiences: []string{"bar"},
ScopesMatcher: ExactScopeStrategyMatcher{"bar", "baz"},
TrustedIssuers: []string{"foo"},
Audiences: []string{"bar"},
ScopesMatcher: ExactScopeStrategyMatcher{"bar", "baz"},
},
assert: func(t *testing.T, err error) {
t.Helper()
Expand All @@ -136,9 +136,9 @@ func TestClaimsValidate(t *testing.T) {
Scope: Scopes{"foo", "bar"},
},
expectations: Expectation{
TrustedIssuers: []string{"foo"},
TargetAudiences: []string{"bar"},
ScopesMatcher: ExactScopeStrategyMatcher{"bar", "baz"},
TrustedIssuers: []string{"foo"},
Audiences: []string{"bar"},
ScopesMatcher: ExactScopeStrategyMatcher{"bar", "baz"},
},
assert: func(t *testing.T, err error) {
t.Helper()
Expand All @@ -157,9 +157,9 @@ func TestClaimsValidate(t *testing.T) {
Scope: Scopes{"foo", "bar"},
},
expectations: Expectation{
TrustedIssuers: []string{"foo"},
TargetAudiences: []string{"bar"},
ScopesMatcher: ExactScopeStrategyMatcher{"foo"},
TrustedIssuers: []string{"foo"},
Audiences: []string{"bar"},
ScopesMatcher: ExactScopeStrategyMatcher{"foo"},
},
assert: func(t *testing.T, err error) {
t.Helper()
Expand All @@ -177,9 +177,9 @@ func TestClaimsValidate(t *testing.T) {
Scp: Scopes{"foo", "bar"},
},
expectations: Expectation{
TrustedIssuers: []string{"foo"},
TargetAudiences: []string{"bar"},
ScopesMatcher: ExactScopeStrategyMatcher{"foo"},
TrustedIssuers: []string{"foo"},
Audiences: []string{"bar"},
ScopesMatcher: ExactScopeStrategyMatcher{"foo"},
},
assert: func(t *testing.T, err error) {
t.Helper()
Expand Down
15 changes: 9 additions & 6 deletions internal/rules/mechanisms/oauth2/expectations.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/dadrus/heimdall/internal/x"
"github.com/dadrus/heimdall/internal/x/errorchain"
"github.com/dadrus/heimdall/internal/x/slicex"
)

const defaultLeeway = 10 * time.Second
Expand All @@ -32,7 +33,7 @@ var ErrAssertion = errors.New("assertion error")
type Expectation struct {
TrustedIssuers []string `mapstructure:"issuers"`
ScopesMatcher ScopesMatcher `mapstructure:"scopes"`
TargetAudiences []string `mapstructure:"audience"`
Audiences []string `mapstructure:"audience"`
AllowedAlgorithms []string `mapstructure:"allowed_algorithms"`
ValidityLeeway time.Duration `mapstructure:"validity_leeway"`
}
Expand All @@ -44,7 +45,7 @@ func (e *Expectation) Merge(other *Expectation) Expectation {

e.TrustedIssuers = x.IfThenElse(len(e.TrustedIssuers) != 0, e.TrustedIssuers, other.TrustedIssuers)
e.ScopesMatcher = x.IfThenElse(e.ScopesMatcher != nil, e.ScopesMatcher, other.ScopesMatcher)
e.TargetAudiences = x.IfThenElse(len(e.TargetAudiences) != 0, e.TargetAudiences, other.TargetAudiences)
e.Audiences = x.IfThenElse(len(e.Audiences) != 0, e.Audiences, other.Audiences)
e.AllowedAlgorithms = x.IfThenElse(len(e.AllowedAlgorithms) != 0, e.AllowedAlgorithms, other.AllowedAlgorithms)
e.ValidityLeeway = x.IfThenElse(e.ValidityLeeway != 0, e.ValidityLeeway, other.ValidityLeeway)

Expand All @@ -68,10 +69,12 @@ func (e *Expectation) AssertIssuer(issuer string) error {
}

func (e *Expectation) AssertAudience(audience []string) error {
for _, aud := range e.TargetAudiences {
if !slices.Contains(audience, aud) {
return errorchain.NewWithMessagef(ErrAssertion, "audience %s is not expected", aud)
}
if len(e.Audiences) == 0 {
return nil
}

if !slicex.Intersects(e.Audiences, audience) {
return errorchain.NewWithMessage(ErrAssertion, "no expected audience present")
}

return nil
Expand Down
Loading

0 comments on commit 560a470

Please sign in to comment.