1
0
mirror of https://github.com/oauth2-proxy/oauth2-proxy.git synced 2025-06-15 00:15:00 +02:00

Integrate new header injectors with OAuth2 Proxy

This commit is contained in:
Joel Speed
2020-07-29 20:10:14 +01:00
parent d26c65ba8d
commit 8059a812cd
7 changed files with 485 additions and 551 deletions

30
main.go
View File

@ -3,17 +3,14 @@ package main
import ( import (
"fmt" "fmt"
"math/rand" "math/rand"
"net"
"os" "os"
"os/signal" "os/signal"
"runtime" "runtime"
"syscall" "syscall"
"time" "time"
"github.com/justinas/alice"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/middleware"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/validation" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/validation"
) )
@ -63,33 +60,8 @@ func main() {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
chain := alice.New()
if opts.ForceHTTPS {
_, httpsPort, err := net.SplitHostPort(opts.HTTPSAddress)
if err != nil {
logger.Fatalf("FATAL: invalid HTTPS address %q: %v", opts.HTTPAddress, err)
}
chain = chain.Append(middleware.NewRedirectToHTTPS(httpsPort))
}
healthCheckPaths := []string{opts.PingPath}
healthCheckUserAgents := []string{opts.PingUserAgent}
if opts.GCPHealthChecks {
healthCheckPaths = append(healthCheckPaths, "/liveness_check", "/readiness_check")
healthCheckUserAgents = append(healthCheckUserAgents, "GoogleHC/1.0")
}
// To silence logging of health checks, register the health check handler before
// the logging handler
if opts.Logging.SilencePing {
chain = chain.Append(middleware.NewHealthCheck(healthCheckPaths, healthCheckUserAgents), LoggingHandler)
} else {
chain = chain.Append(LoggingHandler, middleware.NewHealthCheck(healthCheckPaths, healthCheckUserAgents))
}
s := &Server{ s := &Server{
Handler: chain.Then(oauthproxy), Handler: oauthproxy,
Opts: opts, Opts: opts,
stop: make(chan struct{}, 1), stop: make(chan struct{}, 1),
} }

View File

@ -2,7 +2,6 @@ package main
import ( import (
"context" "context"
b64 "encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -98,7 +97,6 @@ type OAuthProxy struct {
PassAuthorization bool PassAuthorization bool
PreferEmailToUser bool PreferEmailToUser bool
skipAuthPreflight bool skipAuthPreflight bool
skipAuthStripHeaders bool
skipJwtBearerTokens bool skipJwtBearerTokens bool
mainJwtBearerVerifier *oidc.IDTokenVerifier mainJwtBearerVerifier *oidc.IDTokenVerifier
extraJwtBearerVerifiers []*oidc.IDTokenVerifier extraJwtBearerVerifiers []*oidc.IDTokenVerifier
@ -110,6 +108,8 @@ type OAuthProxy struct {
AllowedGroups []string AllowedGroups []string
sessionChain alice.Chain sessionChain alice.Chain
headersChain alice.Chain
preAuthChain alice.Chain
} }
// NewOAuthProxy creates a new instance of OAuthProxy from the options provided // NewOAuthProxy creates a new instance of OAuthProxy from the options provided
@ -169,7 +169,15 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
return nil, err return nil, err
} }
preAuthChain, err := buildPreAuthChain(opts)
if err != nil {
return nil, fmt.Errorf("could not build pre-auth chain: %v", err)
}
sessionChain := buildSessionChain(opts, sessionStore, basicAuthValidator) sessionChain := buildSessionChain(opts, sessionStore, basicAuthValidator)
headersChain, err := buildHeadersChain(opts)
if err != nil {
return nil, fmt.Errorf("could not build headers chain: %v", err)
}
return &OAuthProxy{ return &OAuthProxy{
CookieName: opts.Cookie.Name, CookieName: opts.Cookie.Name,
@ -201,20 +209,10 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
allowedRoutes: allowedRoutes, allowedRoutes: allowedRoutes,
whitelistDomains: opts.WhitelistDomains, whitelistDomains: opts.WhitelistDomains,
skipAuthPreflight: opts.SkipAuthPreflight, skipAuthPreflight: opts.SkipAuthPreflight,
skipAuthStripHeaders: opts.SkipAuthStripHeaders,
skipJwtBearerTokens: opts.SkipJwtBearerTokens, skipJwtBearerTokens: opts.SkipJwtBearerTokens,
mainJwtBearerVerifier: opts.GetOIDCVerifier(), mainJwtBearerVerifier: opts.GetOIDCVerifier(),
extraJwtBearerVerifiers: opts.GetJWTBearerVerifiers(), extraJwtBearerVerifiers: opts.GetJWTBearerVerifiers(),
realClientIPParser: opts.GetRealClientIPParser(), realClientIPParser: opts.GetRealClientIPParser(),
SetXAuthRequest: opts.SetXAuthRequest,
PassBasicAuth: opts.PassBasicAuth,
SetBasicAuth: opts.SetBasicAuth,
PassUserHeaders: opts.PassUserHeaders,
BasicAuthPassword: opts.BasicAuthPassword,
PassAccessToken: opts.PassAccessToken,
SetAuthorization: opts.SetAuthorization,
PassAuthorization: opts.PassAuthorization,
PreferEmailToUser: opts.PreferEmailToUser,
SkipProviderButton: opts.SkipProviderButton, SkipProviderButton: opts.SkipProviderButton,
templates: templates, templates: templates,
trustedIPs: trustedIPs, trustedIPs: trustedIPs,
@ -226,12 +224,46 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
basicAuthValidator: basicAuthValidator, basicAuthValidator: basicAuthValidator,
displayHtpasswdForm: basicAuthValidator != nil, displayHtpasswdForm: basicAuthValidator != nil,
sessionChain: sessionChain, sessionChain: sessionChain,
headersChain: headersChain,
preAuthChain: preAuthChain,
}, nil }, nil
} }
func buildSessionChain(opts *options.Options, sessionStore sessionsapi.SessionStore, validator basic.Validator) alice.Chain { // buildPreAuthChain constructs a chain that should process every request before
// the OAuth2 Proxy authentication logic kicks in.
// For example forcing HTTPS or health checks.
func buildPreAuthChain(opts *options.Options) (alice.Chain, error) {
chain := alice.New(middleware.NewScope()) chain := alice.New(middleware.NewScope())
if opts.ForceHTTPS {
_, httpsPort, err := net.SplitHostPort(opts.HTTPSAddress)
if err != nil {
return alice.Chain{}, fmt.Errorf("invalid HTTPS address %q: %v", opts.HTTPAddress, err)
}
chain = chain.Append(middleware.NewRedirectToHTTPS(httpsPort))
}
healthCheckPaths := []string{opts.PingPath}
healthCheckUserAgents := []string{opts.PingUserAgent}
if opts.GCPHealthChecks {
healthCheckPaths = append(healthCheckPaths, "/liveness_check", "/readiness_check")
healthCheckUserAgents = append(healthCheckUserAgents, "GoogleHC/1.0")
}
// To silence logging of health checks, register the health check handler before
// the logging handler
if opts.Logging.SilencePing {
chain = chain.Append(middleware.NewHealthCheck(healthCheckPaths, healthCheckUserAgents), LoggingHandler)
} else {
chain = chain.Append(LoggingHandler, middleware.NewHealthCheck(healthCheckPaths, healthCheckUserAgents))
}
return chain, nil
}
func buildSessionChain(opts *options.Options, sessionStore sessionsapi.SessionStore, validator basic.Validator) alice.Chain {
chain := alice.New()
if opts.SkipJwtBearerTokens { if opts.SkipJwtBearerTokens {
sessionLoaders := []middlewareapi.TokenToSessionLoader{} sessionLoaders := []middlewareapi.TokenToSessionLoader{}
if opts.GetOIDCVerifier() != nil { if opts.GetOIDCVerifier() != nil {
@ -264,6 +296,20 @@ func buildSessionChain(opts *options.Options, sessionStore sessionsapi.SessionSt
return chain return chain
} }
func buildHeadersChain(opts *options.Options) (alice.Chain, error) {
requestInjector, err := middleware.NewRequestHeaderInjector(opts.InjectRequestHeaders)
if err != nil {
return alice.Chain{}, fmt.Errorf("error constructing request header injector: %v", err)
}
responseInjector, err := middleware.NewResponseHeaderInjector(opts.InjectResponseHeaders)
if err != nil {
return alice.Chain{}, fmt.Errorf("error constructing request header injector: %v", err)
}
return alice.New(requestInjector, responseInjector), nil
}
func buildSignInMessage(opts *options.Options) string { func buildSignInMessage(opts *options.Options) string {
var msg string var msg string
if len(opts.Banner) >= 1 { if len(opts.Banner) >= 1 {
@ -685,6 +731,10 @@ func (p *OAuthProxy) IsTrustedIP(req *http.Request) bool {
} }
func (p *OAuthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { func (p *OAuthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
p.preAuthChain.Then(http.HandlerFunc(p.serveHTTP)).ServeHTTP(rw, req)
}
func (p *OAuthProxy) serveHTTP(rw http.ResponseWriter, req *http.Request) {
if req.URL.Path != p.AuthOnlyPath && strings.HasPrefix(req.URL.Path, p.ProxyPrefix) { if req.URL.Path != p.AuthOnlyPath && strings.HasPrefix(req.URL.Path, p.ProxyPrefix) {
prepareNoCache(rw) prepareNoCache(rw)
} }
@ -884,15 +934,14 @@ func (p *OAuthProxy) AuthenticateOnly(rw http.ResponseWriter, req *http.Request)
// we are authenticated // we are authenticated
p.addHeadersForProxying(rw, req, session) p.addHeadersForProxying(rw, req, session)
rw.WriteHeader(http.StatusAccepted) p.headersChain.Then(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(http.StatusAccepted)
})).ServeHTTP(rw, req)
} }
// SkipAuthProxy proxies allowlisted requests and skips authentication // SkipAuthProxy proxies allowlisted requests and skips authentication
func (p *OAuthProxy) SkipAuthProxy(rw http.ResponseWriter, req *http.Request) { func (p *OAuthProxy) SkipAuthProxy(rw http.ResponseWriter, req *http.Request) {
if p.skipAuthStripHeaders { p.headersChain.Then(p.serveMux).ServeHTTP(rw, req)
p.stripAuthHeaders(req)
}
p.serveMux.ServeHTTP(rw, req)
} }
// Proxy proxies the user request if the user is authenticated else it prompts // Proxy proxies the user request if the user is authenticated else it prompts
@ -903,8 +952,7 @@ func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
case nil: case nil:
// we are authenticated // we are authenticated
p.addHeadersForProxying(rw, req, session) p.addHeadersForProxying(rw, req, session)
p.serveMux.ServeHTTP(rw, req) p.headersChain.Then(p.serveMux).ServeHTTP(rw, req)
case ErrNeedsLogin: case ErrNeedsLogin:
// we need to send the user to a login screen // we need to send the user to a login screen
if isAjax(req) { if isAjax(req) {
@ -961,120 +1009,6 @@ func (p *OAuthProxy) getAuthenticatedSession(rw http.ResponseWriter, req *http.R
// addHeadersForProxying adds the appropriate headers the request / response for proxying // addHeadersForProxying adds the appropriate headers the request / response for proxying
func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Request, session *sessionsapi.SessionState) { func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Request, session *sessionsapi.SessionState) {
if p.PassBasicAuth {
if p.PreferEmailToUser && session.Email != "" {
req.SetBasicAuth(session.Email, p.BasicAuthPassword)
req.Header["X-Forwarded-User"] = []string{session.Email}
req.Header.Del("X-Forwarded-Email")
} else {
req.SetBasicAuth(session.User, p.BasicAuthPassword)
req.Header["X-Forwarded-User"] = []string{session.User}
if session.Email != "" {
req.Header["X-Forwarded-Email"] = []string{session.Email}
} else {
req.Header.Del("X-Forwarded-Email")
}
}
if session.PreferredUsername != "" {
req.Header["X-Forwarded-Preferred-Username"] = []string{session.PreferredUsername}
} else {
req.Header.Del("X-Forwarded-Preferred-Username")
}
}
if p.PassUserHeaders {
if p.PreferEmailToUser && session.Email != "" {
req.Header["X-Forwarded-User"] = []string{session.Email}
req.Header.Del("X-Forwarded-Email")
} else {
req.Header["X-Forwarded-User"] = []string{session.User}
if session.Email != "" {
req.Header["X-Forwarded-Email"] = []string{session.Email}
} else {
req.Header.Del("X-Forwarded-Email")
}
}
if session.PreferredUsername != "" {
req.Header["X-Forwarded-Preferred-Username"] = []string{session.PreferredUsername}
} else {
req.Header.Del("X-Forwarded-Preferred-Username")
}
if len(session.Groups) > 0 {
for _, group := range session.Groups {
req.Header.Add("X-Forwarded-Groups", group)
}
} else {
req.Header.Del("X-Forwarded-Groups")
}
}
if p.SetXAuthRequest {
rw.Header().Set("X-Auth-Request-User", session.User)
if session.Email != "" {
rw.Header().Set("X-Auth-Request-Email", session.Email)
} else {
rw.Header().Del("X-Auth-Request-Email")
}
if session.PreferredUsername != "" {
rw.Header().Set("X-Auth-Request-Preferred-Username", session.PreferredUsername)
} else {
rw.Header().Del("X-Auth-Request-Preferred-Username")
}
if p.PassAccessToken {
if session.AccessToken != "" {
rw.Header().Set("X-Auth-Request-Access-Token", session.AccessToken)
} else {
rw.Header().Del("X-Auth-Request-Access-Token")
}
}
if len(session.Groups) > 0 {
for _, group := range session.Groups {
rw.Header().Add("X-Auth-Request-Groups", group)
}
} else {
rw.Header().Del("X-Auth-Request-Groups")
}
}
if p.PassAccessToken {
if session.AccessToken != "" {
req.Header["X-Forwarded-Access-Token"] = []string{session.AccessToken}
} else {
req.Header.Del("X-Forwarded-Access-Token")
}
}
if p.PassAuthorization {
if session.IDToken != "" {
req.Header["Authorization"] = []string{fmt.Sprintf("Bearer %s", session.IDToken)}
} else {
req.Header.Del("Authorization")
}
}
if p.SetBasicAuth {
switch {
case p.PreferEmailToUser && session.Email != "":
authVal := b64.StdEncoding.EncodeToString([]byte(session.Email + ":" + p.BasicAuthPassword))
rw.Header().Set("Authorization", "Basic "+authVal)
case session.User != "":
authVal := b64.StdEncoding.EncodeToString([]byte(session.User + ":" + p.BasicAuthPassword))
rw.Header().Set("Authorization", "Basic "+authVal)
default:
rw.Header().Del("Authorization")
}
}
if p.SetAuthorization {
if session.IDToken != "" {
rw.Header().Set("Authorization", fmt.Sprintf("Bearer %s", session.IDToken))
} else {
rw.Header().Del("Authorization")
}
}
if session.Email == "" { if session.Email == "" {
rw.Header().Set("GAP-Auth", session.User) rw.Header().Set("GAP-Auth", session.User)
} else { } else {
@ -1082,32 +1016,6 @@ func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Req
} }
} }
// stripAuthHeaders removes Auth headers for allowlisted routes from skipAuthRegex
func (p *OAuthProxy) stripAuthHeaders(req *http.Request) {
if p.PassBasicAuth {
req.Header.Del("X-Forwarded-User")
req.Header.Del("X-Forwarded-Groups")
req.Header.Del("X-Forwarded-Email")
req.Header.Del("X-Forwarded-Preferred-Username")
req.Header.Del("Authorization")
}
if p.PassUserHeaders {
req.Header.Del("X-Forwarded-User")
req.Header.Del("X-Forwarded-Groups")
req.Header.Del("X-Forwarded-Email")
req.Header.Del("X-Forwarded-Preferred-Username")
}
if p.PassAccessToken {
req.Header.Del("X-Forwarded-Access-Token")
}
if p.PassAuthorization {
req.Header.Del("Authorization")
}
}
// isAjax checks if a request is an ajax request // isAjax checks if a request is an ajax request
func isAjax(req *http.Request) bool { func isAjax(req *http.Request) bool {
acceptValues := req.Header.Values("Accept") acceptValues := req.Header.Values("Accept")

View File

@ -495,6 +495,8 @@ func TestBasicAuthPassword(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
})) }))
basicAuthPassword := "This is a secure password"
opts := baseTestOptions() opts := baseTestOptions()
opts.UpstreamServers = options.Upstreams{ opts.UpstreamServers = options.Upstreams{
{ {
@ -505,11 +507,22 @@ func TestBasicAuthPassword(t *testing.T) {
} }
opts.Cookie.Secure = false opts.Cookie.Secure = false
opts.PassBasicAuth = true opts.InjectRequestHeaders = []options.Header{
opts.SetBasicAuth = true {
opts.PassUserHeaders = true Name: "Authorization",
opts.PreferEmailToUser = true Values: []options.HeaderValue{
opts.BasicAuthPassword = "This is a secure password" {
ClaimSource: &options.ClaimSource{
Claim: "email",
BasicAuthPassword: &options.SecretSource{
Value: []byte(base64.StdEncoding.EncodeToString([]byte(basicAuthPassword))),
},
},
},
},
},
}
err := validation.Validate(opts) err := validation.Validate(opts)
assert.NoError(t, err) assert.NoError(t, err)
@ -524,148 +537,44 @@ func TestBasicAuthPassword(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
// Save the required session
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/oauth2/callback?code=callback_code&state=nonce:", strings.NewReader("")) req, _ := http.NewRequest("GET", "/", nil)
req.AddCookie(proxy.MakeCSRFCookie(req, "nonce", proxy.CookieExpire, time.Now())) err = proxy.sessionStore.Save(rw, req, &sessions.SessionState{
proxy.ServeHTTP(rw, req) Email: emailAddress,
if rw.Code >= 400 {
t.Fatalf("expected 3xx got %d", rw.Code)
}
cookie := rw.Header().Values("Set-Cookie")[1]
cookieName := proxy.CookieName
var value string
keyPrefix := cookieName + "="
for _, field := range strings.Split(cookie, "; ") {
value = strings.TrimPrefix(field, keyPrefix)
if value != field {
break
} else {
value = ""
}
}
req, _ = http.NewRequest("GET", "/", strings.NewReader(""))
req.AddCookie(&http.Cookie{
Name: cookieName,
Value: value,
Path: "/",
Expires: time.Now().Add(time.Duration(24)),
HttpOnly: true,
}) })
req.AddCookie(proxy.MakeCSRFCookie(req, "nonce", proxy.CookieExpire, time.Now())) assert.NoError(t, err)
// Extract the cookie value to inject into the test request
cookie := rw.Header().Values("Set-Cookie")[0]
req, _ = http.NewRequest("GET", "/", nil)
req.Header.Set("Cookie", cookie)
rw = httptest.NewRecorder() rw = httptest.NewRecorder()
proxy.ServeHTTP(rw, req) proxy.ServeHTTP(rw, req)
// The username in the basic auth credentials is expected to be equal to the email address from the // The username in the basic auth credentials is expected to be equal to the email address from the
// auth response, so we use the same variable here. // auth response, so we use the same variable here.
expectedHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(emailAddress+":"+opts.BasicAuthPassword)) expectedHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(emailAddress+":"+basicAuthPassword))
assert.Equal(t, expectedHeader, rw.Body.String()) assert.Equal(t, expectedHeader, rw.Body.String())
providerServer.Close() providerServer.Close()
} }
func TestBasicAuthWithEmail(t *testing.T) {
opts := baseTestOptions()
opts.PassBasicAuth = true
opts.PassUserHeaders = false
opts.PreferEmailToUser = false
opts.BasicAuthPassword = "This is a secure password"
err := validation.Validate(opts)
assert.NoError(t, err)
const emailAddress = "john.doe@example.com"
const userName = "9fcab5c9b889a557"
// The username in the basic auth credentials is expected to be equal to the email address from the
expectedEmailHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(emailAddress+":"+opts.BasicAuthPassword))
expectedUserHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(userName+":"+opts.BasicAuthPassword))
created := time.Now()
session := &sessions.SessionState{
User: userName,
Email: emailAddress,
AccessToken: "oauth_token",
CreatedAt: &created,
}
{
rw := httptest.NewRecorder()
req, _ := http.NewRequest("GET", opts.ProxyPrefix+"/testCase0", nil)
proxy, err := NewOAuthProxy(opts, func(email string) bool {
return email == emailAddress
})
if err != nil {
t.Fatal(err)
}
proxy.addHeadersForProxying(rw, req, session)
assert.Equal(t, expectedUserHeader, req.Header["Authorization"][0])
assert.Equal(t, userName, req.Header["X-Forwarded-User"][0])
}
opts.PreferEmailToUser = true
{
rw := httptest.NewRecorder()
req, _ := http.NewRequest("GET", opts.ProxyPrefix+"/testCase1", nil)
proxy, err := NewOAuthProxy(opts, func(email string) bool {
return email == emailAddress
})
if err != nil {
t.Fatal(err)
}
proxy.addHeadersForProxying(rw, req, session)
assert.Equal(t, expectedEmailHeader, req.Header["Authorization"][0])
assert.Equal(t, emailAddress, req.Header["X-Forwarded-User"][0])
}
}
func TestPassUserHeadersWithEmail(t *testing.T) {
opts := baseTestOptions()
err := validation.Validate(opts)
assert.NoError(t, err)
const emailAddress = "john.doe@example.com"
const userName = "9fcab5c9b889a557"
created := time.Now()
session := &sessions.SessionState{
User: userName,
Email: emailAddress,
AccessToken: "oauth_token",
CreatedAt: &created,
}
{
rw := httptest.NewRecorder()
req, _ := http.NewRequest("GET", opts.ProxyPrefix+"/testCase0", nil)
proxy, err := NewOAuthProxy(opts, func(email string) bool {
return email == emailAddress
})
if err != nil {
t.Fatal(err)
}
proxy.addHeadersForProxying(rw, req, session)
assert.Equal(t, userName, req.Header["X-Forwarded-User"][0])
}
opts.PreferEmailToUser = true
{
rw := httptest.NewRecorder()
req, _ := http.NewRequest("GET", opts.ProxyPrefix+"/testCase1", nil)
proxy, err := NewOAuthProxy(opts, func(email string) bool {
return email == emailAddress
})
if err != nil {
t.Fatal(err)
}
proxy.addHeadersForProxying(rw, req, session)
assert.Equal(t, emailAddress, req.Header["X-Forwarded-User"][0])
}
}
func TestPassGroupsHeadersWithGroups(t *testing.T) { func TestPassGroupsHeadersWithGroups(t *testing.T) {
opts := baseTestOptions() opts := baseTestOptions()
opts.InjectRequestHeaders = []options.Header{
{
Name: "X-Forwarded-Groups",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "groups",
},
},
},
},
}
err := validation.Validate(opts) err := validation.Validate(opts)
assert.NoError(t, err) assert.NoError(t, err)
@ -681,161 +590,27 @@ func TestPassGroupsHeadersWithGroups(t *testing.T) {
AccessToken: "oauth_token", AccessToken: "oauth_token",
CreatedAt: &created, CreatedAt: &created,
} }
{
rw := httptest.NewRecorder()
req, _ := http.NewRequest("GET", opts.ProxyPrefix+"/testCase0", nil)
proxy, err := NewOAuthProxy(opts, func(email string) bool {
return email == emailAddress
})
if err != nil {
t.Fatal(err)
}
proxy.addHeadersForProxying(rw, req, session)
assert.Equal(t, groups, req.Header["X-Forwarded-Groups"])
}
}
func TestStripAuthHeaders(t *testing.T) { proxy, err := NewOAuthProxy(opts, func(email string) bool {
testCases := map[string]struct { return email == emailAddress
SkipAuthStripHeaders bool })
PassBasicAuth bool assert.NoError(t, err)
PassUserHeaders bool
PassAccessToken bool
PassAuthorization bool
StrippedHeaders map[string]bool
}{
"Default options": {
SkipAuthStripHeaders: true,
PassBasicAuth: true,
PassUserHeaders: true,
PassAccessToken: false,
PassAuthorization: false,
StrippedHeaders: map[string]bool{
"X-Forwarded-User": true,
"X-Forwared-Groups": true,
"X-Forwarded-Email": true,
"X-Forwarded-Preferred-Username": true,
"X-Forwarded-Access-Token": false,
"Authorization": true,
},
},
"Pass access token": {
SkipAuthStripHeaders: true,
PassBasicAuth: true,
PassUserHeaders: true,
PassAccessToken: true,
PassAuthorization: false,
StrippedHeaders: map[string]bool{
"X-Forwarded-User": true,
"X-Forwared-Groups": true,
"X-Forwarded-Email": true,
"X-Forwarded-Preferred-Username": true,
"X-Forwarded-Access-Token": true,
"Authorization": true,
},
},
"Nothing setting Authorization": {
SkipAuthStripHeaders: true,
PassBasicAuth: false,
PassUserHeaders: true,
PassAccessToken: true,
PassAuthorization: false,
StrippedHeaders: map[string]bool{
"X-Forwarded-User": true,
"X-Forwared-Groups": true,
"X-Forwarded-Email": true,
"X-Forwarded-Preferred-Username": true,
"X-Forwarded-Access-Token": true,
"Authorization": false,
},
},
"Only Authorization header modified": {
SkipAuthStripHeaders: true,
PassBasicAuth: false,
PassUserHeaders: false,
PassAccessToken: false,
PassAuthorization: true,
StrippedHeaders: map[string]bool{
"X-Forwarded-User": false,
"X-Forwared-Groups": false,
"X-Forwarded-Email": false,
"X-Forwarded-Preferred-Username": false,
"X-Forwarded-Access-Token": false,
"Authorization": true,
},
},
"Don't strip any headers (default options)": {
SkipAuthStripHeaders: false,
PassBasicAuth: true,
PassUserHeaders: true,
PassAccessToken: false,
PassAuthorization: false,
StrippedHeaders: map[string]bool{
"X-Forwarded-User": false,
"X-Forwared-Groups": false,
"X-Forwarded-Email": false,
"X-Forwarded-Preferred-Username": false,
"X-Forwarded-Access-Token": false,
"Authorization": false,
},
},
"Don't strip any headers (custom options)": {
SkipAuthStripHeaders: false,
PassBasicAuth: true,
PassUserHeaders: true,
PassAccessToken: true,
PassAuthorization: false,
StrippedHeaders: map[string]bool{
"X-Forwarded-User": false,
"X-Forwared-Groups": false,
"X-Forwarded-Email": false,
"X-Forwarded-Preferred-Username": false,
"X-Forwarded-Access-Token": false,
"Authorization": false,
},
},
}
initialHeaders := map[string]string{ // Save the required session
"X-Forwarded-User": "9fcab5c9b889a557", rw := httptest.NewRecorder()
"X-Forwarded-Email": "john.doe@example.com", req, _ := http.NewRequest("GET", "/", nil)
"X-Forwarded-Groups": "a,b,c", err = proxy.sessionStore.Save(rw, req, session)
"X-Forwarded-Preferred-Username": "john.doe", assert.NoError(t, err)
"X-Forwarded-Access-Token": "AccessToken",
"Authorization": "bearer IDToken",
}
for name, tc := range testCases { // Extract the cookie value to inject into the test request
t.Run(name, func(t *testing.T) { cookie := rw.Header().Values("Set-Cookie")[0]
opts := baseTestOptions()
opts.SkipAuthStripHeaders = tc.SkipAuthStripHeaders
opts.PassBasicAuth = tc.PassBasicAuth
opts.PassUserHeaders = tc.PassUserHeaders
opts.PassAccessToken = tc.PassAccessToken
opts.PassAuthorization = tc.PassAuthorization
err := validation.Validate(opts)
assert.NoError(t, err)
req, _ := http.NewRequest("GET", fmt.Sprintf("%s/testCase", opts.ProxyPrefix), nil) req, _ = http.NewRequest("GET", "/", nil)
for header, val := range initialHeaders { req.Header.Set("Cookie", cookie)
req.Header.Set(header, val) rw = httptest.NewRecorder()
} proxy.ServeHTTP(rw, req)
proxy, err := NewOAuthProxy(opts, func(_ string) bool { return true }) assert.Equal(t, groups, req.Header["X-Forwarded-Groups"])
assert.NoError(t, err)
if proxy.skipAuthStripHeaders {
proxy.stripAuthHeaders(req)
}
for header, stripped := range tc.StrippedHeaders {
if stripped {
assert.Equal(t, req.Header.Get(header), "")
} else {
assert.Equal(t, req.Header.Get(header), initialHeaders[header])
}
}
})
}
} }
type PassAccessTokenTest struct { type PassAccessTokenTest struct {
@ -884,7 +659,21 @@ func NewPassAccessTokenTest(opts PassAccessTokenTestOptions) (*PassAccessTokenTe
} }
patt.opts.Cookie.Secure = false patt.opts.Cookie.Secure = false
patt.opts.PassAccessToken = opts.PassAccessToken if opts.PassAccessToken {
patt.opts.InjectRequestHeaders = []options.Header{
{
Name: "X-Forwarded-Access-Token",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "access_token",
},
},
},
},
}
}
err := validation.Validate(patt.opts) err := validation.Validate(patt.opts)
if err != nil { if err != nil {
return nil, err return nil, err
@ -1442,7 +1231,48 @@ func TestAuthOnlyEndpointSetXAuthRequestHeaders(t *testing.T) {
var pcTest ProcessCookieTest var pcTest ProcessCookieTest
pcTest.opts = baseTestOptions() pcTest.opts = baseTestOptions()
pcTest.opts.SetXAuthRequest = true pcTest.opts.InjectResponseHeaders = []options.Header{
{
Name: "X-Auth-Request-User",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "user",
},
},
},
},
{
Name: "X-Auth-Request-Email",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "email",
},
},
},
},
{
Name: "X-Auth-Request-Groups",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "groups",
},
},
},
},
{
Name: "X-Forwarded-Preferred-Username",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "preferred_username",
},
},
},
},
}
pcTest.opts.AllowedGroups = []string{"oauth_groups"} pcTest.opts.AllowedGroups = []string{"oauth_groups"}
err := validation.Validate(pcTest.opts) err := validation.Validate(pcTest.opts)
assert.NoError(t, err) assert.NoError(t, err)
@ -1480,8 +1310,62 @@ func TestAuthOnlyEndpointSetBasicAuthTrueRequestHeaders(t *testing.T) {
var pcTest ProcessCookieTest var pcTest ProcessCookieTest
pcTest.opts = baseTestOptions() pcTest.opts = baseTestOptions()
pcTest.opts.SetXAuthRequest = true pcTest.opts.InjectResponseHeaders = []options.Header{
pcTest.opts.SetBasicAuth = true {
Name: "X-Auth-Request-User",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "user",
},
},
},
},
{
Name: "X-Auth-Request-Email",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "email",
},
},
},
},
{
Name: "X-Auth-Request-Groups",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "groups",
},
},
},
},
{
Name: "X-Forwarded-Preferred-Username",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "preferred_username",
},
},
},
},
{
Name: "Authorization",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "user",
BasicAuthPassword: &options.SecretSource{
Value: []byte(base64.StdEncoding.EncodeToString([]byte("This is a secure password"))),
},
},
},
},
},
}
err := validation.Validate(pcTest.opts) err := validation.Validate(pcTest.opts)
assert.NoError(t, err) assert.NoError(t, err)
@ -1511,7 +1395,7 @@ func TestAuthOnlyEndpointSetBasicAuthTrueRequestHeaders(t *testing.T) {
assert.Equal(t, http.StatusAccepted, pcTest.rw.Code) assert.Equal(t, http.StatusAccepted, pcTest.rw.Code)
assert.Equal(t, "oauth_user", pcTest.rw.Header().Values("X-Auth-Request-User")[0]) assert.Equal(t, "oauth_user", pcTest.rw.Header().Values("X-Auth-Request-User")[0])
assert.Equal(t, "oauth_user@example.com", pcTest.rw.Header().Values("X-Auth-Request-Email")[0]) assert.Equal(t, "oauth_user@example.com", pcTest.rw.Header().Values("X-Auth-Request-Email")[0])
expectedHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte("oauth_user:"+pcTest.opts.BasicAuthPassword)) expectedHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte("oauth_user:This is a secure password"))
assert.Equal(t, expectedHeader, pcTest.rw.Header().Values("Authorization")[0]) assert.Equal(t, expectedHeader, pcTest.rw.Header().Values("Authorization")[0])
} }
@ -1519,8 +1403,48 @@ func TestAuthOnlyEndpointSetBasicAuthFalseRequestHeaders(t *testing.T) {
var pcTest ProcessCookieTest var pcTest ProcessCookieTest
pcTest.opts = baseTestOptions() pcTest.opts = baseTestOptions()
pcTest.opts.SetXAuthRequest = true pcTest.opts.InjectResponseHeaders = []options.Header{
pcTest.opts.SetBasicAuth = false {
Name: "X-Auth-Request-User",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "user",
},
},
},
},
{
Name: "X-Auth-Request-Email",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "email",
},
},
},
},
{
Name: "X-Auth-Request-Groups",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "groups",
},
},
},
},
{
Name: "X-Forwarded-Preferred-Username",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "preferred_username",
},
},
},
},
}
err := validation.Validate(pcTest.opts) err := validation.Validate(pcTest.opts)
assert.NoError(t, err) assert.NoError(t, err)
@ -1985,9 +1909,74 @@ func TestGetJwtSession(t *testing.T) {
&oidc.Config{ClientID: "https://test.myapp.com", SkipExpiryCheck: true}) &oidc.Config{ClientID: "https://test.myapp.com", SkipExpiryCheck: true})
test, err := NewAuthOnlyEndpointTest(func(opts *options.Options) { test, err := NewAuthOnlyEndpointTest(func(opts *options.Options) {
opts.PassAuthorization = true opts.InjectRequestHeaders = []options.Header{
opts.SetAuthorization = true {
opts.SetXAuthRequest = true Name: "Authorization",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "id_token",
Prefix: "Bearer ",
},
},
},
},
{
Name: "X-Forwarded-User",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "user",
},
},
},
},
{
Name: "X-Forwarded-Email",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "email",
},
},
},
},
}
opts.InjectResponseHeaders = []options.Header{
{
Name: "Authorization",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "id_token",
Prefix: "Bearer ",
},
},
},
},
{
Name: "X-Auth-Request-User",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "user",
},
},
},
},
{
Name: "X-Auth-Request-Email",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "email",
},
},
},
},
}
opts.SkipJwtBearerTokens = true opts.SkipJwtBearerTokens = true
opts.SetJWTBearerVerifiers(append(opts.GetJWTBearerVerifiers(), verifier)) opts.SetJWTBearerVerifiers(append(opts.GetJWTBearerVerifiers(), verifier))
}) })
@ -2004,15 +1993,6 @@ func TestGetJwtSession(t *testing.T) {
"Authorization": {authHeader}, "Authorization": {authHeader},
} }
// Bearer
expires := time.Unix(1912151821, 0)
session, err := test.proxy.getAuthenticatedSession(test.rw, test.req)
assert.NoError(t, err)
assert.Equal(t, session.User, "1234567890")
assert.Equal(t, session.Email, "john@example.com")
assert.Equal(t, session.ExpiresOn, &expires)
assert.Equal(t, session.IDToken, goodJwt)
test.proxy.ServeHTTP(test.rw, test.req) test.proxy.ServeHTTP(test.rw, test.req)
if test.rw.Code >= 400 { if test.rw.Code >= 400 {
t.Fatalf("expected 3xx got %d", test.rw.Code) t.Fatalf("expected 3xx got %d", test.rw.Code)
@ -2140,6 +2120,43 @@ func baseTestOptions() *options.Options {
opts.ClientID = clientID opts.ClientID = clientID
opts.ClientSecret = clientSecret opts.ClientSecret = clientSecret
opts.EmailDomains = []string{"*"} opts.EmailDomains = []string{"*"}
// Default injected headers for legacy configuration
opts.InjectRequestHeaders = []options.Header{
{
Name: "Authorization",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "user",
BasicAuthPassword: &options.SecretSource{
Value: []byte(base64.StdEncoding.EncodeToString([]byte("This is a secure password"))),
},
},
},
},
},
{
Name: "X-Forwarded-User",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "user",
},
},
},
},
{
Name: "X-Forwarded-Email",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "email",
},
},
},
},
}
return opts return opts
} }

View File

@ -73,10 +73,6 @@ func Validate(o *options.Options) error {
"\n use email-domain=* to authorize all email addresses") "\n use email-domain=* to authorize all email addresses")
} }
if o.SetBasicAuth && o.SetAuthorization {
msgs = append(msgs, "mutually exclusive: set-basic-auth and set-authorization-header can not both be true")
}
if o.OIDCIssuerURL != "" { if o.OIDCIssuerURL != "" {
ctx := context.Background() ctx := context.Background()
@ -161,10 +157,6 @@ func Validate(o *options.Options) error {
} }
} }
if o.PreferEmailToUser && !o.PassBasicAuth && !o.PassUserHeaders {
msgs = append(msgs, "PreferEmailToUser should only be used with PassBasicAuth or PassUserHeaders")
}
if o.SkipJwtBearerTokens { if o.SkipJwtBearerTokens {
// Configure extra issuers // Configure extra issuers
if len(o.ExtraJwtIssuers) > 0 { if len(o.ExtraJwtIssuers) > 0 {

View File

@ -162,29 +162,6 @@ func TestDefaultProviderApiSettings(t *testing.T) {
assert.Equal(t, "profile email", p.Scope) assert.Equal(t, "profile email", p.Scope)
} }
func TestPassAccessTokenRequiresSpecificCookieSecretLengths(t *testing.T) {
o := testOptions()
assert.Equal(t, nil, Validate(o))
assert.Equal(t, false, o.PassAccessToken)
o.PassAccessToken = true
o.Cookie.Secret = "cookie of invalid length-"
assert.NotEqual(t, nil, Validate(o))
o.PassAccessToken = false
o.Cookie.Refresh = time.Duration(24) * time.Hour
assert.NotEqual(t, nil, Validate(o))
o.Cookie.Secret = "16 bytes AES-128"
assert.Equal(t, nil, Validate(o))
o.Cookie.Secret = "24 byte secret AES-192--"
assert.Equal(t, nil, Validate(o))
o.Cookie.Secret = "32 byte secret for AES-256------"
assert.Equal(t, nil, Validate(o))
}
func TestCookieRefreshMustBeLessThanCookieExpire(t *testing.T) { func TestCookieRefreshMustBeLessThanCookieExpire(t *testing.T) {
o := testOptions() o := testOptions()
assert.Equal(t, nil, Validate(o)) assert.Equal(t, nil, Validate(o))

View File

@ -16,18 +16,21 @@ func validateSessionCookieMinimal(o *options.Options) []string {
} }
msgs := []string{} msgs := []string{}
if o.PassAuthorization { for _, header := range append(o.InjectRequestHeaders, o.InjectResponseHeaders...) {
msgs = append(msgs, for _, value := range header.Values {
"pass_authorization_header requires oauth tokens in sessions. session_cookie_minimal cannot be set") if value.ClaimSource != nil {
} if value.ClaimSource.Claim == "access_token" {
if o.SetAuthorization { msgs = append(msgs,
msgs = append(msgs, fmt.Sprintf("access_token claim for header %q requires oauth tokens in sessions. session_cookie_minimal cannot be set", header.Name))
"set_authorization_header requires oauth tokens in sessions. session_cookie_minimal cannot be set") }
} if value.ClaimSource.Claim == "id_token" {
if o.PassAccessToken { msgs = append(msgs,
msgs = append(msgs, fmt.Sprintf("id_token claim for header %q requires oauth tokens in sessions. session_cookie_minimal cannot be set", header.Name))
"pass_access_token requires oauth tokens in sessions. session_cookie_minimal cannot be set") }
}
}
} }
if o.Cookie.Refresh != time.Duration(0) { if o.Cookie.Refresh != time.Duration(0) {
msgs = append(msgs, msgs = append(msgs,
"cookie_refresh > 0 requires oauth tokens in sessions. session_cookie_minimal cannot be set") "cookie_refresh > 0 requires oauth tokens in sessions. session_cookie_minimal cannot be set")

View File

@ -13,10 +13,9 @@ import (
var _ = Describe("Sessions", func() { var _ = Describe("Sessions", func() {
const ( const (
passAuthorizationMsg = "pass_authorization_header requires oauth tokens in sessions. session_cookie_minimal cannot be set" idTokenConflictMsg = "id_token claim for header \"X-ID-Token\" requires oauth tokens in sessions. session_cookie_minimal cannot be set"
setAuthorizationMsg = "set_authorization_header requires oauth tokens in sessions. session_cookie_minimal cannot be set" accessTokenConflictMsg = "access_token claim for header \"X-Access-Token\" requires oauth tokens in sessions. session_cookie_minimal cannot be set"
passAccessTokenMsg = "pass_access_token requires oauth tokens in sessions. session_cookie_minimal cannot be set" cookieRefreshMsg = "cookie_refresh > 0 requires oauth tokens in sessions. session_cookie_minimal cannot be set"
cookieRefreshMsg = "cookie_refresh > 0 requires oauth tokens in sessions. session_cookie_minimal cannot be set"
) )
type cookieMinimalTableInput struct { type cookieMinimalTableInput struct {
@ -38,14 +37,25 @@ var _ = Describe("Sessions", func() {
}, },
errStrings: []string{}, errStrings: []string{},
}), }),
Entry("No minimal cookie session & passAuthorization", &cookieMinimalTableInput{ Entry("No minimal cookie session & request header has access_token claim", &cookieMinimalTableInput{
opts: &options.Options{ opts: &options.Options{
Session: options.SessionOptions{ Session: options.SessionOptions{
Cookie: options.CookieStoreOptions{ Cookie: options.CookieStoreOptions{
Minimal: false, Minimal: false,
}, },
}, },
PassAuthorization: true, InjectRequestHeaders: []options.Header{
{
Name: "X-Access-Token",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "access_token",
},
},
},
},
},
}, },
errStrings: []string{}, errStrings: []string{},
}), }),
@ -59,38 +69,71 @@ var _ = Describe("Sessions", func() {
}, },
errStrings: []string{}, errStrings: []string{},
}), }),
Entry("PassAuthorization conflict", &cookieMinimalTableInput{ Entry("Request Header id_token conflict", &cookieMinimalTableInput{
opts: &options.Options{ opts: &options.Options{
Session: options.SessionOptions{ Session: options.SessionOptions{
Cookie: options.CookieStoreOptions{ Cookie: options.CookieStoreOptions{
Minimal: true, Minimal: true,
}, },
}, },
PassAuthorization: true, InjectRequestHeaders: []options.Header{
{
Name: "X-ID-Token",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "id_token",
},
},
},
},
},
}, },
errStrings: []string{passAuthorizationMsg}, errStrings: []string{idTokenConflictMsg},
}), }),
Entry("SetAuthorization conflict", &cookieMinimalTableInput{ Entry("Response Header id_token conflict", &cookieMinimalTableInput{
opts: &options.Options{ opts: &options.Options{
Session: options.SessionOptions{ Session: options.SessionOptions{
Cookie: options.CookieStoreOptions{ Cookie: options.CookieStoreOptions{
Minimal: true, Minimal: true,
}, },
}, },
SetAuthorization: true, InjectResponseHeaders: []options.Header{
{
Name: "X-ID-Token",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "id_token",
},
},
},
},
},
}, },
errStrings: []string{setAuthorizationMsg}, errStrings: []string{idTokenConflictMsg},
}), }),
Entry("PassAccessToken conflict", &cookieMinimalTableInput{ Entry("Request Header access_token conflict", &cookieMinimalTableInput{
opts: &options.Options{ opts: &options.Options{
Session: options.SessionOptions{ Session: options.SessionOptions{
Cookie: options.CookieStoreOptions{ Cookie: options.CookieStoreOptions{
Minimal: true, Minimal: true,
}, },
}, },
PassAccessToken: true, InjectRequestHeaders: []options.Header{
{
Name: "X-Access-Token",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "access_token",
},
},
},
},
},
}, },
errStrings: []string{passAccessTokenMsg}, errStrings: []string{accessTokenConflictMsg},
}), }),
Entry("CookieRefresh conflict", &cookieMinimalTableInput{ Entry("CookieRefresh conflict", &cookieMinimalTableInput{
opts: &options.Options{ opts: &options.Options{
@ -112,10 +155,32 @@ var _ = Describe("Sessions", func() {
Minimal: true, Minimal: true,
}, },
}, },
PassAuthorization: true, InjectResponseHeaders: []options.Header{
PassAccessToken: true, {
Name: "X-ID-Token",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "id_token",
},
},
},
},
},
InjectRequestHeaders: []options.Header{
{
Name: "X-Access-Token",
Values: []options.HeaderValue{
{
ClaimSource: &options.ClaimSource{
Claim: "access_token",
},
},
},
},
},
}, },
errStrings: []string{passAuthorizationMsg, passAccessTokenMsg}, errStrings: []string{idTokenConflictMsg, accessTokenConflictMsg},
}), }),
) )