improved audience handling to support client credentials access tokens without aud claims (#1204)

* implementation draft

* add cfg options skip-au-when-missing && client-id-verification-claim; enhance the provider data verification logic for sake of the added options

* refactor configs, added logging and add additional claim verification

* simplify logic by just having one configuration similar to oidc-email-claim

* added internal oidc token verifier, so that aud check behavior can be managed with oauth2-proxy and is compatible with extra-jwt-issuers

* refactored verification to reduce complexity

* refactored verification to reduce complexity

* added docs

* adjust tests to support new OIDCAudienceClaim and OIDCExtraAudiences options

* extend unit tests and ensure that audience is set with the value of aud claim configuration

* revert filemodes and update docs

* update docs

* remove unneccesary logging, refactor audience existence check and added additional unit tests

* fix linting issues after rebase on origin/main

* cleanup: use new imports for migrated libraries after rebase on origin/main

* adapt mock in keycloak_oidc_test.go

* allow specifying multiple audience claims, fixed bug where jwt issuers client id was not the being considered and fixed bug where aud claims with multiple audiences has broken the whole validation

* fixed formatting issue

* do not pass the whole options struct to minimize complexity and dependency to the configuration structure

* added changelog entry

* update docs

Co-authored-by: Sofia Weiler <sofia.weiler@aoe.com>
Co-authored-by: Christian Zenker <christian.zenker@aoe.com>
This commit is contained in:
Kevin Schu
2022-02-15 16:12:22 +00:00
committed by GitHub
co-authored by Sofia Weiler Christian Zenker
parent c5a98c6d03
commit 25371ea4af
20 changed files with 536 additions and 52 deletions
+8
View File
@@ -54,6 +54,8 @@ func NewLegacyOptions() *LegacyOptions {
UserIDClaim: "email",
OIDCEmailClaim: "email",
OIDCGroupsClaim: "groups",
OIDCAudienceClaims: []string{"aud"},
OIDCExtraAudiences: []string{},
InsecureOIDCSkipNonce: true,
},
@@ -500,6 +502,8 @@ type LegacyProvider struct {
OIDCJwksURL string `flag:"oidc-jwks-url" cfg:"oidc_jwks_url"`
OIDCEmailClaim string `flag:"oidc-email-claim" cfg:"oidc_email_claim"`
OIDCGroupsClaim string `flag:"oidc-groups-claim" cfg:"oidc_groups_claim"`
OIDCAudienceClaims []string `flag:"oidc-audience-claim" cfg:"oidc_audience_claims"`
OIDCExtraAudiences []string `flag:"oidc-extra-audience" cfg:"oidc_extra_audiences"`
LoginURL string `flag:"login-url" cfg:"login_url"`
RedeemURL string `flag:"redeem-url" cfg:"redeem_url"`
ProfileURL string `flag:"profile-url" cfg:"profile_url"`
@@ -550,6 +554,8 @@ func legacyProviderFlagSet() *pflag.FlagSet {
flagSet.String("oidc-jwks-url", "", "OpenID Connect JWKS URL (ie: https://www.googleapis.com/oauth2/v3/certs)")
flagSet.String("oidc-groups-claim", providers.OIDCGroupsClaim, "which OIDC claim contains the user groups")
flagSet.String("oidc-email-claim", providers.OIDCEmailClaim, "which OIDC claim contains the user's email")
flagSet.StringSlice("oidc-audience-claim", providers.OIDCAudienceClaims, "which OIDC claims are used as audience to verify against client id")
flagSet.StringSlice("oidc-extra-audience", []string{}, "additional audiences allowed to pass audience verification")
flagSet.String("login-url", "", "Authentication endpoint")
flagSet.String("redeem-url", "", "Token redemption endpoint")
flagSet.String("profile-url", "", "Profile access endpoint")
@@ -644,6 +650,8 @@ func (l *LegacyProvider) convert() (Providers, error) {
UserIDClaim: l.UserIDClaim,
EmailClaim: l.OIDCEmailClaim,
GroupsClaim: l.OIDCGroupsClaim,
AudienceClaims: l.OIDCAudienceClaims,
ExtraAudiences: l.OIDCExtraAudiences,
}
// This part is out of the switch section because azure has a default tenant
+2
View File
@@ -116,6 +116,8 @@ var _ = Describe("Legacy Options", func() {
opts.Providers[0].ClientID = "oauth-proxy"
opts.Providers[0].ID = "google=oauth-proxy"
opts.Providers[0].OIDCConfig.InsecureSkipNonce = true
opts.Providers[0].OIDCConfig.AudienceClaims = []string{"aud"}
opts.Providers[0].OIDCConfig.ExtraAudiences = []string{}
converted, err := legacyOpts.ToOptions()
Expect(err).ToNot(HaveOccurred())
+1
View File
@@ -42,6 +42,7 @@ var _ = Describe("Load", func() {
UserIDClaim: "email",
OIDCEmailClaim: "email",
OIDCGroupsClaim: "groups",
OIDCAudienceClaims: []string{"aud"},
InsecureOIDCSkipNonce: true,
},
+17 -13
View File
@@ -4,8 +4,8 @@ import (
"crypto"
"net/url"
"github.com/coreos/go-oidc/v3/oidc"
ipapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/ip"
internaloidc "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/oidc"
"github.com/oauth2-proxy/oauth2-proxy/v7/providers"
"github.com/spf13/pflag"
)
@@ -70,25 +70,29 @@ type Options struct {
redirectURL *url.URL
provider providers.Provider
signatureData *SignatureData
oidcVerifier *oidc.IDTokenVerifier
jwtBearerVerifiers []*oidc.IDTokenVerifier
oidcVerifier *internaloidc.IDTokenVerifier
jwtBearerVerifiers []*internaloidc.IDTokenVerifier
realClientIPParser ipapi.RealClientIPParser
}
// Options for Getting internal values
func (o *Options) GetRedirectURL() *url.URL { return o.redirectURL }
func (o *Options) GetProvider() providers.Provider { return o.provider }
func (o *Options) GetSignatureData() *SignatureData { return o.signatureData }
func (o *Options) GetOIDCVerifier() *oidc.IDTokenVerifier { return o.oidcVerifier }
func (o *Options) GetJWTBearerVerifiers() []*oidc.IDTokenVerifier { return o.jwtBearerVerifiers }
func (o *Options) GetRedirectURL() *url.URL { return o.redirectURL }
func (o *Options) GetProvider() providers.Provider { return o.provider }
func (o *Options) GetSignatureData() *SignatureData { return o.signatureData }
func (o *Options) GetOIDCVerifier() *internaloidc.IDTokenVerifier { return o.oidcVerifier }
func (o *Options) GetJWTBearerVerifiers() []*internaloidc.IDTokenVerifier {
return o.jwtBearerVerifiers
}
func (o *Options) GetRealClientIPParser() ipapi.RealClientIPParser { return o.realClientIPParser }
// Options for Setting internal values
func (o *Options) SetRedirectURL(s *url.URL) { o.redirectURL = s }
func (o *Options) SetProvider(s providers.Provider) { o.provider = s }
func (o *Options) SetSignatureData(s *SignatureData) { o.signatureData = s }
func (o *Options) SetOIDCVerifier(s *oidc.IDTokenVerifier) { o.oidcVerifier = s }
func (o *Options) SetJWTBearerVerifiers(s []*oidc.IDTokenVerifier) { o.jwtBearerVerifiers = s }
func (o *Options) SetRedirectURL(s *url.URL) { o.redirectURL = s }
func (o *Options) SetProvider(s providers.Provider) { o.provider = s }
func (o *Options) SetSignatureData(s *SignatureData) { o.signatureData = s }
func (o *Options) SetOIDCVerifier(s *internaloidc.IDTokenVerifier) { o.oidcVerifier = s }
func (o *Options) SetJWTBearerVerifiers(s []*internaloidc.IDTokenVerifier) {
o.jwtBearerVerifiers = s
}
func (o *Options) SetRealClientIPParser(s ipapi.RealClientIPParser) { o.realClientIPParser = s }
// NewOptions constructs a new Options with defaulted values
+8
View File
@@ -164,6 +164,12 @@ type OIDCOptions struct {
// UserIDClaim indicates which claim contains the user ID
// default set to 'email'
UserIDClaim string `json:"userIDClaim,omitempty"`
// AudienceClaim allows to define any claim that is verified against the client id
// By default `aud` claim is used for verification.
AudienceClaims []string `json:"audienceClaims,omitempty"`
// ExtraAudiences is a list of additional audiences that are allowed
// to pass verification in addition to the client id.
ExtraAudiences []string `json:"extraAudiences,omitempty"`
}
type LoginGovOptions struct {
@@ -191,6 +197,8 @@ func providerDefaults() Providers {
UserIDClaim: providers.OIDCEmailClaim, // Deprecated: Use OIDCEmailClaim
EmailClaim: providers.OIDCEmailClaim,
GroupsClaim: providers.OIDCGroupsClaim,
AudienceClaims: providers.OIDCAudienceClaims,
ExtraAudiences: []string{},
},
},
}