git.iko.soy/typeable/oauth2-proxy
on master help sign in
Add support for setting the basic auth password.

For tools that don't like empty passwords, this change allows one to set a shared secret password for all users.

commit7dd5d299e18162e9ec7663699133d36ec2a7844c authorJustin Burnham <justin@jburnham.net> dateFri 24 Jul 2015, 09:17 (11 years ago) parent58d2b88
5 files changed+117−16
README.md+1 main.go+1 oauthproxy.go+13−11 oauthproxy_test.go+96 options.go+6−5
README.md+1
@@ -116,6 +116,7 @@ Usage of oauth2_proxy:
116116 -login-url="": Authentication endpoint
117117 -pass-access-token=false: pass OAuth access_token to upstream via X-Forwarded-Access-Token header
118118 -pass-basic-auth=true: pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream
119+ -basic-auth-password="": the password to set when passing the HTTP Basic Auth header
119120 -pass-host-header=true: pass the request Host Header to upstream
120121 -profile-url="": Profile access endpoint
121122 -provider="google": OAuth provider
main.go+1
@@ -31,6 +31,7 @@ func main() {
3131 flagSet.String("redirect-url", "", "the OAuth Redirect URL. ie: \"https://internalapp.yourcompany.com/oauth2/callback\"")
3232 flagSet.Var(&upstreams, "upstream", "the http url(s) of the upstream endpoint. If multiple, routing is based on path")
3333 flagSet.Bool("pass-basic-auth", true, "pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream")
34+ flagSet.String("basic-auth-password", "", "the password to set when passing the HTTP Basic Auth header")
3435 flagSet.Bool("pass-access-token", false, "pass OAuth access_token to upstream via X-Forwarded-Access-Token header")
3536 flagSet.Bool("pass-host-header", true, "pass the request Host Header to upstream")
3637 flagSet.Var(&skipAuthRegex, "skip-auth-regex", "bypass authentication for requests path's that match (may be given multiple times)")
oauthproxy.go+13−11
@@ -42,6 +42,7 @@ type OauthProxy struct {
4242 DisplayHtpasswdForm bool
4343 serveMux http.Handler
4444 PassBasicAuth bool
45+ BasicAuthPassword string
4546 PassAccessToken bool
4647 CookieCipher *cookie.Cipher
4748 skipAuthRegex []string
@@ -141,16 +142,17 @@ func NewOauthProxy(opts *Options, validator func(string) bool) *OauthProxy {
141142 OauthStartPath: fmt.Sprintf("%s/start", opts.ProxyPrefix),
142143 OauthCallbackPath: fmt.Sprintf("%s/callback", opts.ProxyPrefix),
143144
144− ProxyPrefix: opts.ProxyPrefix,
145− provider: opts.provider,
146− serveMux: serveMux,
147− redirectUrl: redirectUrl,
148− skipAuthRegex: opts.SkipAuthRegex,
149− compiledRegex: opts.CompiledRegex,
150− PassBasicAuth: opts.PassBasicAuth,
151− PassAccessToken: opts.PassAccessToken,
152− CookieCipher: cipher,
153− templates: loadTemplates(opts.CustomTemplatesDir),
145+ ProxyPrefix: opts.ProxyPrefix,
146+ provider: opts.provider,
147+ serveMux: serveMux,
148+ redirectUrl: redirectUrl,
149+ skipAuthRegex: opts.SkipAuthRegex,
150+ compiledRegex: opts.CompiledRegex,
151+ PassBasicAuth: opts.PassBasicAuth,
152+ BasicAuthPassword: opts.BasicAuthPassword,
153+ PassAccessToken: opts.PassAccessToken,
154+ CookieCipher: cipher,
155+ templates: loadTemplates(opts.CustomTemplatesDir),
154156 }
155157}
156158
@@ -518,7 +520,7 @@ func (p *OauthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
518520
519521 // At this point, the user is authenticated. proxy normally
520522 if p.PassBasicAuth {
521− req.SetBasicAuth(session.User, "")
523+ req.SetBasicAuth(session.User, p.BasicAuthPassword)
522524 req.Header["X-Forwarded-User"] = []string{session.User}
523525 if session.Email != "" {
524526 req.Header["X-Forwarded-Email"] = []string{session.Email}
oauthproxy_test.go+96
@@ -1,6 +1,7 @@
11package main
22
33import (
4+ "encoding/base64"
45 "github.com/bitly/oauth2_proxy/providers"
56 "github.com/bmizerany/assert"
67 "io/ioutil"
@@ -88,6 +89,101 @@ func TestRobotsTxt(t *testing.T) {
8889 assert.Equal(t, "User-agent: *\nDisallow: /", rw.Body.String())
8990}
9091
92+func TestBasicAuthPassword(t *testing.T) {
93+ provider_server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
94+ log.Printf("%#v", r)
95+ url := r.URL
96+ payload := ""
97+ switch url.Path {
98+ case "/oauth/token":
99+ payload = `{"access_token": "my_auth_token"}`
100+ default:
101+ payload = r.Header.Get("Authorization")
102+ if payload == "" {
103+ payload = "No Authorization header found."
104+ }
105+ }
106+ w.WriteHeader(200)
107+ w.Write([]byte(payload))
108+ }))
109+ opts := NewOptions()
110+ opts.Upstreams = append(opts.Upstreams, provider_server.URL)
111+ // The CookieSecret must be 32 bytes in order to create the AES
112+ // cipher.
113+ opts.CookieSecret = "xyzzyplughxyzzyplughxyzzyplughxp"
114+ opts.ClientID = "bazquux"
115+ opts.ClientSecret = "foobar"
116+ opts.CookieSecure = false
117+ opts.PassBasicAuth = true
118+ opts.BasicAuthPassword = "This is a secure password"
119+ opts.Validate()
120+
121+ provider_url, _ := url.Parse(provider_server.URL)
122+ const email_address = "michael.bland@gsa.gov"
123+ const user_name = "michael.bland"
124+
125+ opts.provider = &TestProvider{
126+ ProviderData: &providers.ProviderData{
127+ ProviderName: "Test Provider",
128+ LoginUrl: &url.URL{
129+ Scheme: "http",
130+ Host: provider_url.Host,
131+ Path: "/oauth/authorize",
132+ },
133+ RedeemUrl: &url.URL{
134+ Scheme: "http",
135+ Host: provider_url.Host,
136+ Path: "/oauth/token",
137+ },
138+ ProfileUrl: &url.URL{
139+ Scheme: "http",
140+ Host: provider_url.Host,
141+ Path: "/api/v1/profile",
142+ },
143+ Scope: "profile.email",
144+ },
145+ EmailAddress: email_address,
146+ }
147+
148+ proxy := NewOauthProxy(opts, func(email string) bool {
149+ return email == email_address
150+ })
151+
152+ rw := httptest.NewRecorder()
153+ req, _ := http.NewRequest("GET", "/oauth2/callback?code=callback_code",
154+ strings.NewReader(""))
155+ proxy.ServeHTTP(rw, req)
156+ cookie := rw.HeaderMap["Set-Cookie"][0]
157+
158+ cookieName := proxy.CookieName
159+ var value string
160+ key_prefix := cookieName + "="
161+
162+ for _, field := range strings.Split(cookie, "; ") {
163+ value = strings.TrimPrefix(field, key_prefix)
164+ if value != field {
165+ break
166+ } else {
167+ value = ""
168+ }
169+ }
170+
171+ req, _ = http.NewRequest("GET", "/", strings.NewReader(""))
172+ req.AddCookie(&http.Cookie{
173+ Name: cookieName,
174+ Value: value,
175+ Path: "/",
176+ Expires: time.Now().Add(time.Duration(24)),
177+ HttpOnly: true,
178+ })
179+
180+ rw = httptest.NewRecorder()
181+ proxy.ServeHTTP(rw, req)
182+ expectedHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(user_name+":"+opts.BasicAuthPassword))
183+ assert.Equal(t, expectedHeader, rw.Body.String())
184+ provider_server.Close()
185+}
186+
91187type TestProvider struct {
92188 *providers.ProviderData
93189 EmailAddress string
options.go+6−5
@@ -37,11 +37,12 @@ type Options struct {
3737 CookieSecure bool `flag:"cookie-secure" cfg:"cookie_secure"`
3838 CookieHttpOnly bool `flag:"cookie-httponly" cfg:"cookie_httponly"`
3939
40− Upstreams []string `flag:"upstream" cfg:"upstreams"`
41− SkipAuthRegex []string `flag:"skip-auth-regex" cfg:"skip_auth_regex"`
42− PassBasicAuth bool `flag:"pass-basic-auth" cfg:"pass_basic_auth"`
43− PassAccessToken bool `flag:"pass-access-token" cfg:"pass_access_token"`
44− PassHostHeader bool `flag:"pass-host-header" cfg:"pass_host_header"`
40+ Upstreams []string `flag:"upstream" cfg:"upstreams"`
41+ SkipAuthRegex []string `flag:"skip-auth-regex" cfg:"skip_auth_regex"`
42+ PassBasicAuth bool `flag:"pass-basic-auth" cfg:"pass_basic_auth"`
43+ BasicAuthPassword string `flag:"basic-auth-password" cfg:"basic_auth_password"`
44+ PassAccessToken bool `flag:"pass-access-token" cfg:"pass_access_token"`
45+ PassHostHeader bool `flag:"pass-host-header" cfg:"pass_host_header"`
4546
4647 // These options allow for other providers besides Google, with
4748 // potential overrides.