1
0
mirror of https://github.com/oauth2-proxy/oauth2-proxy.git synced 2025-02-01 13:18:05 +02:00

Merge remote-tracking branch 'upstream/master' into helm-example

# Conflicts:
#	CHANGELOG.md
This commit is contained in:
Evgeni Gordeev 2020-06-25 15:24:00 -05:00
commit 054979978f
7 changed files with 120 additions and 73 deletions

View File

@ -56,6 +56,7 @@
## Changes since v5.1.1 ## Changes since v5.1.1
- [#615](https://github.com/oauth2-proxy/oauth2-proxy/pull/615) Kubernetes example based on Kind cluster and Nginx ingress (@EvgeniGordeev) - [#615](https://github.com/oauth2-proxy/oauth2-proxy/pull/615) Kubernetes example based on Kind cluster and Nginx ingress (@EvgeniGordeev)
- [#596](https://github.com/oauth2-proxy/oauth2-proxy/pull/596) Validate Bearer IDTokens in headers with correct provider/extra JWT Verifier (@NickMeves)
- [#620](https://github.com/oauth2-proxy/oauth2-proxy/pull/620) Add HealthCheck middleware (@JoelSpeed) - [#620](https://github.com/oauth2-proxy/oauth2-proxy/pull/620) Add HealthCheck middleware (@JoelSpeed)
- [#597](https://github.com/oauth2-proxy/oauth2-proxy/pull/597) Don't log invalid redirect if redirect is empty (@JoelSpeed) - [#597](https://github.com/oauth2-proxy/oauth2-proxy/pull/597) Don't log invalid redirect if redirect is empty (@JoelSpeed)
- [#604](https://github.com/oauth2-proxy/oauth2-proxy/pull/604) Add Keycloak local testing environment (@EvgeniGordeev) - [#604](https://github.com/oauth2-proxy/oauth2-proxy/pull/604) Add Keycloak local testing environment (@EvgeniGordeev)

View File

@ -88,35 +88,36 @@ type OAuthProxy struct {
AuthOnlyPath string AuthOnlyPath string
UserInfoPath string UserInfoPath string
redirectURL *url.URL // the url to receive requests at redirectURL *url.URL // the url to receive requests at
whitelistDomains []string whitelistDomains []string
provider providers.Provider provider providers.Provider
providerNameOverride string providerNameOverride string
sessionStore sessionsapi.SessionStore sessionStore sessionsapi.SessionStore
ProxyPrefix string ProxyPrefix string
SignInMessage string SignInMessage string
HtpasswdFile *HtpasswdFile HtpasswdFile *HtpasswdFile
DisplayHtpasswdForm bool DisplayHtpasswdForm bool
serveMux http.Handler serveMux http.Handler
SetXAuthRequest bool SetXAuthRequest bool
PassBasicAuth bool PassBasicAuth bool
SetBasicAuth bool SetBasicAuth bool
SkipProviderButton bool SkipProviderButton bool
PassUserHeaders bool PassUserHeaders bool
BasicAuthPassword string BasicAuthPassword string
PassAccessToken bool PassAccessToken bool
SetAuthorization bool SetAuthorization bool
PassAuthorization bool PassAuthorization bool
PreferEmailToUser bool PreferEmailToUser bool
skipAuthRegex []string skipAuthRegex []string
skipAuthPreflight bool skipAuthPreflight bool
skipJwtBearerTokens bool skipJwtBearerTokens bool
jwtBearerVerifiers []*oidc.IDTokenVerifier mainJwtBearerVerifier *oidc.IDTokenVerifier
compiledRegex []*regexp.Regexp extraJwtBearerVerifiers []*oidc.IDTokenVerifier
templates *template.Template compiledRegex []*regexp.Regexp
realClientIPParser ipapi.RealClientIPParser templates *template.Template
Banner string realClientIPParser ipapi.RealClientIPParser
Footer string Banner string
Footer string
} }
// UpstreamProxy represents an upstream server to proxy to // UpstreamProxy represents an upstream server to proxy to
@ -317,32 +318,33 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) *OAuthPro
AuthOnlyPath: fmt.Sprintf("%s/auth", opts.ProxyPrefix), AuthOnlyPath: fmt.Sprintf("%s/auth", opts.ProxyPrefix),
UserInfoPath: fmt.Sprintf("%s/userinfo", opts.ProxyPrefix), UserInfoPath: fmt.Sprintf("%s/userinfo", opts.ProxyPrefix),
ProxyPrefix: opts.ProxyPrefix, ProxyPrefix: opts.ProxyPrefix,
provider: opts.GetProvider(), provider: opts.GetProvider(),
providerNameOverride: opts.ProviderName, providerNameOverride: opts.ProviderName,
sessionStore: opts.GetSessionStore(), sessionStore: opts.GetSessionStore(),
serveMux: serveMux, serveMux: serveMux,
redirectURL: redirectURL, redirectURL: redirectURL,
whitelistDomains: opts.WhitelistDomains, whitelistDomains: opts.WhitelistDomains,
skipAuthRegex: opts.SkipAuthRegex, skipAuthRegex: opts.SkipAuthRegex,
skipAuthPreflight: opts.SkipAuthPreflight, skipAuthPreflight: opts.SkipAuthPreflight,
skipJwtBearerTokens: opts.SkipJwtBearerTokens, skipJwtBearerTokens: opts.SkipJwtBearerTokens,
jwtBearerVerifiers: opts.GetJWTBearerVerifiers(), mainJwtBearerVerifier: opts.GetOIDCVerifier(),
compiledRegex: opts.GetCompiledRegex(), extraJwtBearerVerifiers: opts.GetJWTBearerVerifiers(),
realClientIPParser: opts.GetRealClientIPParser(), compiledRegex: opts.GetCompiledRegex(),
SetXAuthRequest: opts.SetXAuthRequest, realClientIPParser: opts.GetRealClientIPParser(),
PassBasicAuth: opts.PassBasicAuth, SetXAuthRequest: opts.SetXAuthRequest,
SetBasicAuth: opts.SetBasicAuth, PassBasicAuth: opts.PassBasicAuth,
PassUserHeaders: opts.PassUserHeaders, SetBasicAuth: opts.SetBasicAuth,
BasicAuthPassword: opts.BasicAuthPassword, PassUserHeaders: opts.PassUserHeaders,
PassAccessToken: opts.PassAccessToken, BasicAuthPassword: opts.BasicAuthPassword,
SetAuthorization: opts.SetAuthorization, PassAccessToken: opts.PassAccessToken,
PassAuthorization: opts.PassAuthorization, SetAuthorization: opts.SetAuthorization,
PreferEmailToUser: opts.PreferEmailToUser, PassAuthorization: opts.PassAuthorization,
SkipProviderButton: opts.SkipProviderButton, PreferEmailToUser: opts.PreferEmailToUser,
templates: loadTemplates(opts.CustomTemplatesDir), SkipProviderButton: opts.SkipProviderButton,
Banner: opts.Banner, templates: loadTemplates(opts.CustomTemplatesDir),
Footer: opts.Footer, Banner: opts.Banner,
Footer: opts.Footer,
} }
} }
@ -1139,15 +1141,24 @@ func (p *OAuthProxy) GetJwtSession(req *http.Request) (*sessionsapi.SessionState
return nil, err return nil, err
} }
for _, verifier := range p.jwtBearerVerifiers { // If we are using an oidc provider, go ahead and try that provider first with its Verifier
bearerToken, err := verifier.Verify(req.Context(), rawBearerToken) // and Bearer Token -> Session converter
if p.mainJwtBearerVerifier != nil {
bearerToken, err := p.mainJwtBearerVerifier.Verify(req.Context(), rawBearerToken)
if err == nil {
return p.provider.CreateSessionStateFromBearerToken(req.Context(), rawBearerToken, bearerToken)
}
}
// Otherwise, attempt to verify against the extra JWT issuers and use a more generic
// Bearer Token -> Session converter
for _, verifier := range p.extraJwtBearerVerifiers {
bearerToken, err := verifier.Verify(req.Context(), rawBearerToken)
if err != nil { if err != nil {
logger.Printf("failed to verify bearer token: %v", err)
continue continue
} }
return p.provider.CreateSessionStateFromBearerToken(req.Context(), rawBearerToken, bearerToken) return (*providers.ProviderData)(nil).CreateSessionStateFromBearerToken(req.Context(), rawBearerToken, bearerToken)
} }
return nil, fmt.Errorf("unable to verify jwt token %s", req.Header.Get("Authorization")) return nil, fmt.Errorf("unable to verify jwt token %s", req.Header.Get("Authorization"))
} }

View File

@ -1578,7 +1578,7 @@ func TestGetJwtSession(t *testing.T) {
// Bearer // Bearer
expires := time.Unix(1912151821, 0) expires := time.Unix(1912151821, 0)
session, _ := test.proxy.GetJwtSession(test.req) session, _ := test.proxy.GetJwtSession(test.req)
assert.Equal(t, session.User, "john@example.com") assert.Equal(t, session.User, "1234567890")
assert.Equal(t, session.Email, "john@example.com") assert.Equal(t, session.Email, "john@example.com")
assert.Equal(t, session.ExpiresOn, &expires) assert.Equal(t, session.ExpiresOn, &expires)
assert.Equal(t, session.IDToken, goodJwt) assert.Equal(t, session.IDToken, goodJwt)
@ -1590,12 +1590,12 @@ func TestGetJwtSession(t *testing.T) {
// Check PassAuthorization, should overwrite Basic header // Check PassAuthorization, should overwrite Basic header
assert.Equal(t, test.req.Header.Get("Authorization"), authHeader) assert.Equal(t, test.req.Header.Get("Authorization"), authHeader)
assert.Equal(t, test.req.Header.Get("X-Forwarded-User"), "john@example.com") assert.Equal(t, test.req.Header.Get("X-Forwarded-User"), "1234567890")
assert.Equal(t, test.req.Header.Get("X-Forwarded-Email"), "john@example.com") assert.Equal(t, test.req.Header.Get("X-Forwarded-Email"), "john@example.com")
// SetAuthorization and SetXAuthRequest // SetAuthorization and SetXAuthRequest
assert.Equal(t, test.rw.Header().Get("Authorization"), authHeader) assert.Equal(t, test.rw.Header().Get("Authorization"), authHeader)
assert.Equal(t, test.rw.Header().Get("X-Auth-Request-User"), "john@example.com") assert.Equal(t, test.rw.Header().Get("X-Auth-Request-User"), "1234567890")
assert.Equal(t, test.rw.Header().Get("X-Auth-Request-Email"), "john@example.com") assert.Equal(t, test.rw.Header().Get("X-Auth-Request-Email"), "john@example.com")
} }

View File

@ -178,10 +178,6 @@ func Validate(o *options.Options) error {
} }
if o.SkipJwtBearerTokens { if o.SkipJwtBearerTokens {
// If we are using an oidc provider, go ahead and add that provider to the list
if o.GetOIDCVerifier() != nil {
o.SetJWTBearerVerifiers(append(o.GetJWTBearerVerifiers(), o.GetOIDCVerifier()))
}
// Configure extra issuers // Configure extra issuers
if len(o.ExtraJwtIssuers) > 0 { if len(o.ExtraJwtIssuers) > 0 {
var jwtIssuers []jwtIssuer var jwtIssuers []jwtIssuer

View File

@ -124,7 +124,6 @@ func newOIDCServer(body []byte) (*url.URL, *httptest.Server) {
} }
func newSignedTestIDToken(tokenClaims idTokenClaims) (string, error) { func newSignedTestIDToken(tokenClaims idTokenClaims) (string, error) {
key, _ := rsa.GenerateKey(rand.Reader, 2048) key, _ := rsa.GenerateKey(rand.Reader, 2048)
standardClaims := jwt.NewWithClaims(jwt.SigningMethodRS256, tokenClaims) standardClaims := jwt.NewWithClaims(jwt.SigningMethodRS256, tokenClaims)
return standardClaims.SignedString(key) return standardClaims.SignedString(key)

View File

@ -164,14 +164,13 @@ func (p *ProviderData) CreateSessionStateFromBearerToken(ctx context.Context, ra
newSession := &sessions.SessionState{ newSession := &sessions.SessionState{
Email: claims.Email, Email: claims.Email,
User: claims.Email, User: claims.Subject,
PreferredUsername: claims.PreferredUsername, PreferredUsername: claims.PreferredUsername,
AccessToken: rawIDToken,
IDToken: rawIDToken,
RefreshToken: "",
ExpiresOn: &idToken.Expiry,
} }
newSession.AccessToken = rawIDToken
newSession.IDToken = rawIDToken
newSession.RefreshToken = ""
newSession.ExpiresOn = &idToken.Expiry
return newSession, nil return newSession, nil
} }

View File

@ -2,10 +2,15 @@ package providers
import ( import (
"context" "context"
"crypto/rand"
"crypto/rsa"
"net/url" "net/url"
"testing" "testing"
"time" "time"
"github.com/coreos/go-oidc"
"github.com/dgrijalva/jwt-go"
"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -47,3 +52,39 @@ func TestAcrValuesConfigured(t *testing.T) {
result := p.GetLoginURL("https://my.test.app/oauth", "") result := p.GetLoginURL("https://my.test.app/oauth", "")
assert.Contains(t, result, "acr_values=testValue") assert.Contains(t, result, "acr_values=testValue")
} }
func TestCreateSessionStateFromBearerToken(t *testing.T) {
minimalIDToken := jwt.StandardClaims{
Audience: "asdf1234",
ExpiresAt: time.Now().Add(time.Duration(5) * time.Minute).Unix(),
Id: "id-some-id",
IssuedAt: time.Now().Unix(),
Issuer: "https://issuer.example.com",
NotBefore: 0,
Subject: "123456789",
}
// From oidc_test.go
verifier := oidc.NewVerifier(
"https://issuer.example.com",
fakeKeySetStub{},
&oidc.Config{ClientID: "asdf1234"},
)
key, err := rsa.GenerateKey(rand.Reader, 2048)
assert.NoError(t, err)
rawIDToken, err := jwt.NewWithClaims(jwt.SigningMethodRS256, minimalIDToken).SignedString(key)
assert.NoError(t, err)
// Pass to a dummy Verifier to get an oidc.IDToken from the rawIDToken for our actual test below
idToken, err := verifier.Verify(context.Background(), rawIDToken)
assert.NoError(t, err)
session, err := (*ProviderData)(nil).CreateSessionStateFromBearerToken(context.Background(), rawIDToken, idToken)
assert.NoError(t, err)
assert.Equal(t, rawIDToken, session.AccessToken)
assert.Equal(t, rawIDToken, session.IDToken)
assert.Equal(t, "123456789", session.Email)
assert.Equal(t, "123456789", session.User)
assert.Empty(t, session.RefreshToken)
assert.Empty(t, session.PreferredUsername)
}