git.iko.soy/typeable/oauth2-proxy
on master help sign in
immediately redeem refresh token for provider==Google
commit8d50b372e4b6aa362d5052c2d2433dd7fa71a650 authorJehiah Czebotar <jehiah@gmail.com> dateTue 23 Jun 2015, 13:23 (11 years ago) parentae2a1e5
6 files changed+120−15
oauthproxy.go+8−1 options_test.go+2−2 providers/google.go+98−3 providers/google_test.go+2−2 providers/provider_default.go+10−6 validator.go−1
oauthproxy.go+8−1
@@ -246,7 +246,13 @@ func (p *OauthProxy) ProcessCookie(rw http.ResponseWriter, req *http.Request) (e
246246 } else if ok && p.CookieRefresh != time.Duration(0) {
247247 refresh := timestamp.Add(p.CookieRefresh)
248248 if refresh.Before(time.Now()) {
249− ok = p.Validator(email) && p.provider.ValidateToken(access_token)
249+ log.Printf("refreshing %s old session for %s (refresh after %s)", time.Now().Sub(timestamp), email, p.CookieRefresh)
250+ ok = p.Validator(email)
251+ log.Printf("re-validating %s valid:%v", email, ok)
252+ if ok {
253+ ok = p.provider.ValidateToken(access_token)
254+ log.Printf("re-validating access token. valid:%v", ok)
255+ }
250256 if ok {
251257 p.SetCookie(rw, req, value)
252258 }
@@ -432,6 +438,7 @@ func (p *OauthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
432438 http.Redirect(rw, req, redirect, 302)
433439 return
434440 } else {
441+ log.Printf("validating: %s is unauthorized")
435442 p.ErrorPage(rw, 403, "Permission Denied", "Invalid Account")
436443 return
437444 }
options_test.go+2−2
@@ -96,9 +96,9 @@ func TestDefaultProviderApiSettings(t *testing.T) {
9696 o := testOptions()
9797 assert.Equal(t, nil, o.Validate())
9898 p := o.provider.Data()
99− assert.Equal(t, "https://accounts.google.com/o/oauth2/auth",
99+ assert.Equal(t, "https://accounts.google.com/o/oauth2/auth?access_type=offline",
100100 p.LoginUrl.String())
101− assert.Equal(t, "https://accounts.google.com/o/oauth2/token",
101+ assert.Equal(t, "https://www.googleapis.com/oauth2/v3/token",
102102 p.RedeemUrl.String())
103103 assert.Equal(t, "", p.ProfileUrl.String())
104104 assert.Equal(t, "profile email", p.Scope)
providers/google.go+98−3
@@ -1,15 +1,20 @@
11package providers
22
33import (
4+ "bytes"
45 "encoding/base64"
56 "encoding/json"
67 "errors"
8+ "fmt"
9+ "io/ioutil"
10+ "net/http"
711 "net/url"
812 "strings"
913)
1014
1115type GoogleProvider struct {
1216 *ProviderData
17+ RedeemRefreshUrl *url.URL
1318}
1419
1520func NewGoogleProvider(p *ProviderData) *GoogleProvider {
@@ -17,12 +22,15 @@ func NewGoogleProvider(p *ProviderData) *GoogleProvider {
1722 if p.LoginUrl.String() == "" {
1823 p.LoginUrl = &url.URL{Scheme: "https",
1924 Host: "accounts.google.com",
20− Path: "/o/oauth2/auth"}
25+ Path: "/o/oauth2/auth",
26+ // to get a refresh token. see https://developers.google.com/identity/protocols/OAuth2WebServer#offline
27+ RawQuery: "access_type=offline",
28+ }
2129 }
2230 if p.RedeemUrl.String() == "" {
2331 p.RedeemUrl = &url.URL{Scheme: "https",
24− Host: "accounts.google.com",
25− Path: "/o/oauth2/token"}
32+ Host: "www.googleapis.com",
33+ Path: "/oauth2/v3/token"}
2634 }
2735 if p.ValidateUrl.String() == "" {
2836 p.ValidateUrl = &url.URL{Scheme: "https",
@@ -76,3 +84,90 @@ func jwtDecodeSegment(seg string) ([]byte, error) {
7684func (p *GoogleProvider) ValidateToken(access_token string) bool {
7785 return validateToken(p, access_token, nil)
7886}
87+
88+func (p *GoogleProvider) Redeem(redirectUrl, code string) (body []byte, token string, err error) {
89+ if code == "" {
90+ err = errors.New("missing code")
91+ return
92+ }
93+
94+ params := url.Values{}
95+ params.Add("redirect_uri", redirectUrl)
96+ params.Add("client_id", p.ClientID)
97+ params.Add("client_secret", p.ClientSecret)
98+ params.Add("code", code)
99+ params.Add("grant_type", "authorization_code")
100+ var req *http.Request
101+ req, err = http.NewRequest("POST", p.RedeemUrl.String(), bytes.NewBufferString(params.Encode()))
102+ if err != nil {
103+ return
104+ }
105+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
106+
107+ resp, err := http.DefaultClient.Do(req)
108+ if err != nil {
109+ return
110+ }
111+ body, err = ioutil.ReadAll(resp.Body)
112+ resp.Body.Close()
113+ if err != nil {
114+ return
115+ }
116+
117+ if resp.StatusCode != 200 {
118+ err = fmt.Errorf("got %d from %q %s", resp.StatusCode, p.RedeemUrl.String(), body)
119+ return
120+ }
121+
122+ var jsonResponse struct {
123+ AccessToken string `json:"access_token"`
124+ RefreshToken string `json:"refresh_token"`
125+ }
126+ err = json.Unmarshal(body, &jsonResponse)
127+ if err != nil {
128+ return
129+ }
130+
131+ token, err = p.redeemRefreshToken(jsonResponse.RefreshToken)
132+ return
133+}
134+
135+func (p *GoogleProvider) redeemRefreshToken(refreshToken string) (token string, err error) {
136+ // https://developers.google.com/identity/protocols/OAuth2WebServer#refresh
137+ params := url.Values{}
138+ params.Add("client_id", p.ClientID)
139+ params.Add("client_secret", p.ClientSecret)
140+ params.Add("refresh_token", refreshToken)
141+ params.Add("grant_type", "refresh_token")
142+ var req *http.Request
143+ req, err = http.NewRequest("POST", p.RedeemUrl.String(), bytes.NewBufferString(params.Encode()))
144+ if err != nil {
145+ return
146+ }
147+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
148+
149+ resp, err := http.DefaultClient.Do(req)
150+ if err != nil {
151+ return
152+ }
153+ var body []byte
154+ body, err = ioutil.ReadAll(resp.Body)
155+ resp.Body.Close()
156+ if err != nil {
157+ return
158+ }
159+
160+ if resp.StatusCode != 200 {
161+ err = fmt.Errorf("got %d from %q %s", resp.StatusCode, p.RedeemUrl.String(), body)
162+ return
163+ }
164+
165+ var jsonResponse struct {
166+ AccessToken string `json:"access_token"`
167+ }
168+ err = json.Unmarshal(body, &jsonResponse)
169+ if err != nil {
170+ return
171+ }
172+ return jsonResponse.AccessToken, nil
173+}
providers/google_test.go+2−2
@@ -23,9 +23,9 @@ func TestGoogleProviderDefaults(t *testing.T) {
2323 p := newGoogleProvider()
2424 assert.NotEqual(t, nil, p)
2525 assert.Equal(t, "Google", p.Data().ProviderName)
26− assert.Equal(t, "https://accounts.google.com/o/oauth2/auth",
26+ assert.Equal(t, "https://accounts.google.com/o/oauth2/auth?access_type=offline",
2727 p.Data().LoginUrl.String())
28− assert.Equal(t, "https://accounts.google.com/o/oauth2/token",
28+ assert.Equal(t, "https://www.googleapis.com/oauth2/v3/token",
2929 p.Data().RedeemUrl.String())
3030 assert.Equal(t, "https://www.googleapis.com/oauth2/v1/tokeninfo",
3131 p.Data().ValidateUrl.String())
providers/provider_default.go+10−6
@@ -56,15 +56,19 @@ func (p *ProviderData) Redeem(redirectUrl, code string) (body []byte, token stri
5656 return body, v.Get("access_token"), err
5757}
5858
59+// GetLoginURL with typical oauth parameters
5960func (p *ProviderData) GetLoginURL(redirectURI, finalRedirect string) string {
60− params := url.Values{}
61− params.Add("redirect_uri", redirectURI)
62− params.Add("approval_prompt", "force")
61+ var a url.URL
62+ a = *p.LoginUrl
63+ params, _ := url.ParseQuery(a.RawQuery)
64+ params.Set("redirect_uri", redirectURI)
65+ params.Set("approval_prompt", "force")
6366 params.Add("scope", p.Scope)
64− params.Add("client_id", p.ClientID)
65− params.Add("response_type", "code")
67+ params.Set("client_id", p.ClientID)
68+ params.Set("response_type", "code")
6669 if strings.HasPrefix(finalRedirect, "/") {
6770 params.Add("state", finalRedirect)
6871 }
69− return fmt.Sprintf("%s?%s", p.LoginUrl, params.Encode())
72+ a.RawQuery = params.Encode()
73+ return a.String()
7074}
validator.go−1
@@ -83,7 +83,6 @@ func newValidatorImpl(domains []string, usersFile string,
8383 if allowAll {
8484 valid = true
8585 }
86− log.Printf("validating: is %s valid? %v", email, valid)
8786 return valid
8887 }
8988 return validator