@@ -22,9 +22,12 @@ const oauthStartPath = "/oauth2/start"
22const oauthCallbackPath = "/oauth2/callback"
22const oauthCallbackPath = "/oauth2/callback"
24type OauthProxy struct {
24type OauthProxy struct {
25− CookieSeed string
25+ CookieSeed string
26− CookieKey string
26+ CookieKey string
29+ CookieExpire time.Duration
27− Validator func(string) bool
30+ Validator func(string) bool
29 redirectUrl *url.URL // the url to receive requests at
32 redirectUrl *url.URL // the url to receive requests at
30 oauthRedemptionUrl *url.URL // endpoint to redeem the code
33 oauthRedemptionUrl *url.URL // endpoint to redeem the code
@@ -35,40 +38,41 @@ type OauthProxy struct {
35 SignInMessage string
38 SignInMessage string
36 HtpasswdFile *HtpasswdFile
39 HtpasswdFile *HtpasswdFile
37 serveMux *http.ServeMux
40 serveMux *http.ServeMux
40−func NewOauthProxy(proxyUrls []*url.URL, clientID string, clientSecret string, validator func(string) bool) *OauthProxy {
44+func NewOauthProxy(opts *Options, validator func(string) bool) *OauthProxy {
41 login, _ := url.Parse("https://accounts.google.com/o/oauth2/auth")
45 login, _ := url.Parse("https://accounts.google.com/o/oauth2/auth")
42 redeem, _ := url.Parse("https://accounts.google.com/o/oauth2/token")
46 redeem, _ := url.Parse("https://accounts.google.com/o/oauth2/token")
43 serveMux := http.NewServeMux()
47 serveMux := http.NewServeMux()
44− for _, u := range proxyUrls {
48+ for _, u := range opts.proxyUrls {
45 path := u.Path
49 path := u.Path
49 u.Path = ""
50 u.Path = ""
50− log.Printf("mapping %s => %s", path, u)
51+ log.Printf("mapping path %q => upstream %q", path, u)
51 serveMux.Handle(path, httputil.NewSingleHostReverseProxy(u))
52 serveMux.Handle(path, httputil.NewSingleHostReverseProxy(u))
53− return &OauthProxy{
54+ redirectUrl := opts.redirectUrl
54− CookieKey: "_oauthproxy",
55+ redirectUrl.Path = oauthCallbackPath
55− CookieSeed: *cookieSecret,
56− Validator: validator,
58+ CookieKey: "_oauthproxy",
59+ CookieSeed: opts.CookieSecret,
60+ CookieDomain: opts.CookieDomain,
61+ CookieHttpsOnly: opts.CookieHttpsOnly,
62+ CookieExpire: opts.CookieExpire,
63+ Validator: validator,
58− clientID: clientID,
65+ clientID: opts.ClientID,
59− clientSecret: clientSecret,
66+ clientSecret: opts.ClientSecret,
60 oauthScope: "profile email",
67 oauthScope: "profile email",
61 oauthRedemptionUrl: redeem,
68 oauthRedemptionUrl: redeem,
62 oauthLoginUrl: login,
69 oauthLoginUrl: login,
63 serveMux: serveMux,
70 serveMux: serveMux,
71+ redirectUrl: redirectUrl,
72+ PassBasicAuth: opts.PassBasicAuth,
67−func (p *OauthProxy) SetRedirectUrl(redirectUrl *url.URL) {
68− redirectUrl.Path = oauthCallbackPath
69− p.redirectUrl = redirectUrl
72func (p *OauthProxy) GetLoginURL(redirectUrl string) string {
76func (p *OauthProxy) GetLoginURL(redirectUrl string) string {
73 params := url.Values{}
77 params := url.Values{}
74 params.Add("redirect_uri", p.redirectUrl.String())
78 params.Add("redirect_uri", p.redirectUrl.String())
@@ -164,8 +168,8 @@ func jwtDecodeSegment(seg string) ([]byte, error) {
165func (p *OauthProxy) ClearCookie(rw http.ResponseWriter, req *http.Request) {
169func (p *OauthProxy) ClearCookie(rw http.ResponseWriter, req *http.Request) {
166 domain := strings.Split(req.Host, ":")[0]
170 domain := strings.Split(req.Host, ":")[0]
167− if *cookieDomain != "" && strings.HasSuffix(domain, *cookieDomain) {
171+ if p.CookieDomain != "" && strings.HasSuffix(domain, p.CookieDomain) {
168− domain = *cookieDomain
172+ domain = p.CookieDomain
170 cookie := &http.Cookie{
174 cookie := &http.Cookie{
171 Name: p.CookieKey,
175 Name: p.CookieKey,
@@ -181,8 +185,8 @@ func (p *OauthProxy) ClearCookie(rw http.ResponseWriter, req *http.Request) {
181func (p *OauthProxy) SetCookie(rw http.ResponseWriter, req *http.Request, val string) {
185func (p *OauthProxy) SetCookie(rw http.ResponseWriter, req *http.Request, val string) {
183 domain := strings.Split(req.Host, ":")[0] // strip the port (if any)
187 domain := strings.Split(req.Host, ":")[0] // strip the port (if any)
184− if *cookieDomain != "" && strings.HasSuffix(domain, *cookieDomain) {
188+ if p.CookieDomain != "" && strings.HasSuffix(domain, p.CookieDomain) {
185− domain = *cookieDomain
189+ domain = p.CookieDomain
187 cookie := &http.Cookie{
191 cookie := &http.Cookie{
188 Name: p.CookieKey,
192 Name: p.CookieKey,
@@ -190,8 +194,8 @@ func (p *OauthProxy) SetCookie(rw http.ResponseWriter, req *http.Request, val st
190 Path: "/",
194 Path: "/",
191 Domain: domain,
195 Domain: domain,
192 HttpOnly: true,
196 HttpOnly: true,
193− Secure: *cookieHttpsOnly,
197+ Secure: p.CookieHttpsOnly,
194− Expires: time.Now().Add(*cookieExpire),
198+ Expires: time.Now().Add(p.CookieExpire),
196 http.SetCookie(rw, cookie)
200 http.SetCookie(rw, cookie)
@@ -267,11 +271,11 @@ func (p *OauthProxy) GetRedirect(req *http.Request) (string, error) {
268func (p *OauthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
272func (p *OauthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
269 // check if this is a redirect back at the end of oauth
273 // check if this is a redirect back at the end of oauth
270− remoteIP := req.Header.Get("X-Real-IP")
274+ remoteAddr := req.RemoteAddr
271− if remoteIP == "" {
275+ if req.Header.Get("X-Real-IP") != "" {
272− remoteIP = req.RemoteAddr
276+ remoteAddr += fmt.Sprintf(" (%q)", req.Header.Get("X-Real-IP"))
274− log.Printf("%s %s %s", remoteIP, req.Method, req.URL.Path)
278+ log.Printf("%s %s %s", remoteAddr, req.Method, req.URL.RequestURI())
276 var ok bool
280 var ok bool
277 var user string
281 var user string
@@ -322,7 +326,7 @@ func (p *OauthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
323 _, email, err := p.redeemCode(req.Form.Get("code"))
327 _, email, err := p.redeemCode(req.Form.Get("code"))
324 if err != nil {
328 if err != nil {
325− log.Printf("error redeeming code %s", err)
329+ log.Printf("%s error redeeming code %s", remoteAddr, err)
326 p.ErrorPage(rw, 500, "Internal Error", err.Error())
330 p.ErrorPage(rw, 500, "Internal Error", err.Error())
@@ -334,7 +338,7 @@ func (p *OauthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
335 // set cookie, or deny
339 // set cookie, or deny
336 if p.Validator(email) {
340 if p.Validator(email) {
337− log.Printf("authenticating %s completed", email)
341+ log.Printf("%s authenticating %s completed", remoteAddr, email)
338 p.SetCookie(rw, req, email)
342 p.SetCookie(rw, req, email)
339 http.Redirect(rw, req, redirect, 302)
343 http.Redirect(rw, req, redirect, 302)
@@ -362,13 +366,13 @@ func (p *OauthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
365− log.Printf("invalid cookie")
369+ log.Printf("%s - invalid cookie session", remoteAddr)
366 p.SignInPage(rw, req, 403)
370 p.SignInPage(rw, req, 403)
370 // At this point, the user is authenticated. proxy normally
374 // At this point, the user is authenticated. proxy normally
371− if *passBasicAuth {
375+ if p.PassBasicAuth {
372 req.SetBasicAuth(user, "")
376 req.SetBasicAuth(user, "")
373 req.Header["X-Forwarded-User"] = []string{user}
377 req.Header["X-Forwarded-User"] = []string{user}
374 req.Header["X-Forwarded-Email"] = []string{email}
378 req.Header["X-Forwarded-Email"] = []string{email}