@@ -5,7 +5,6 @@ import (
5 "encoding/base64"
5 "encoding/base64"
8− "github.com/bitly/go-simplejson"
9 "io/ioutil"
8 "io/ioutil"
11 "net/http"
10 "net/http"
@@ -13,6 +12,8 @@ import (
16+ "github.com/bitly/go-simplejson"
18const signInPath = "/oauth2/sign_in"
19const signInPath = "/oauth2/sign_in"
@@ -27,7 +28,6 @@ type OauthProxy struct {
27 redirectUrl *url.URL // the url to receive requests at
28 redirectUrl *url.URL // the url to receive requests at
28 oauthRedemptionUrl *url.URL // endpoint to redeem the code
29 oauthRedemptionUrl *url.URL // endpoint to redeem the code
29 oauthLoginUrl *url.URL // to redirect the user to
30 oauthLoginUrl *url.URL // to redirect the user to
30− oauthUserInfoUrl *url.URL
31 oauthScope string
31 oauthScope string
32 clientID string
32 clientID string
33 clientSecret string
33 clientSecret string
@@ -39,7 +39,6 @@ type OauthProxy struct {
39func NewOauthProxy(proxyUrls []*url.URL, clientID string, clientSecret string, validator func(string) bool) *OauthProxy {
39func NewOauthProxy(proxyUrls []*url.URL, clientID string, clientSecret string, validator func(string) bool) *OauthProxy {
40 login, _ := url.Parse("https://accounts.google.com/o/oauth2/auth")
40 login, _ := url.Parse("https://accounts.google.com/o/oauth2/auth")
41 redeem, _ := url.Parse("https://accounts.google.com/o/oauth2/token")
41 redeem, _ := url.Parse("https://accounts.google.com/o/oauth2/token")
42− info, _ := url.Parse("https://www.googleapis.com/oauth2/v2/userinfo")
43 serveMux := http.NewServeMux()
42 serveMux := http.NewServeMux()
44 for _, u := range proxyUrls {
43 for _, u := range proxyUrls {
45 path := u.Path
44 path := u.Path
@@ -54,10 +53,9 @@ func NewOauthProxy(proxyUrls []*url.URL, clientID string, clientSecret string, v
55 clientID: clientID,
54 clientID: clientID,
56 clientSecret: clientSecret,
55 clientSecret: clientSecret,
57− oauthScope: "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
56+ oauthScope: "profile email",
58 oauthRedemptionUrl: redeem,
57 oauthRedemptionUrl: redeem,
59 oauthLoginUrl: login,
58 oauthLoginUrl: login,
60− oauthUserInfoUrl: info,
61 serveMux: serveMux,
59 serveMux: serveMux,
@@ -102,9 +100,9 @@ func apiRequest(req *http.Request) (*simplejson.Json, error) {
102 return data, nil
100 return data, nil
105−func (p *OauthProxy) redeemCode(code string) (string, error) {
103+func (p *OauthProxy) redeemCode(code string) (string, string, error) {
106 if code == "" {
104 if code == "" {
107− return "", errors.New("missing code")
105+ return "", "", errors.New("missing code")
109 params := url.Values{}
107 params := url.Values{}
110 params.Add("redirect_uri", p.redirectUrl.String())
108 params.Add("redirect_uri", p.redirectUrl.String())
@@ -112,46 +110,52 @@ func (p *OauthProxy) redeemCode(code string) (string, error) {
112 params.Add("client_secret", p.clientSecret)
110 params.Add("client_secret", p.clientSecret)
113 params.Add("code", code)
111 params.Add("code", code)
114 params.Add("grant_type", "authorization_code")
112 params.Add("grant_type", "authorization_code")
115− log.Printf("body is %s", params.Encode())
116 req, err := http.NewRequest("POST", p.oauthRedemptionUrl.String(), bytes.NewBufferString(params.Encode()))
113 req, err := http.NewRequest("POST", p.oauthRedemptionUrl.String(), bytes.NewBufferString(params.Encode()))
117 if err != nil {
114 if err != nil {
118 log.Printf("failed building request %s", err.Error())
115 log.Printf("failed building request %s", err.Error())
119− return "", err
116+ return "", "", err
121 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
118 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
122 json, err := apiRequest(req)
119 json, err := apiRequest(req)
123 if err != nil {
120 if err != nil {
124− log.Printf("failed making request %s", err.Error())
121+ log.Printf("failed making request %s", err)
125− return "", err
122+ return "", "", err
127 access_token, err := json.Get("access_token").String()
124 access_token, err := json.Get("access_token").String()
128 if err != nil {
125 if err != nil {
129− return "", err
126+ return "", "", err
131− return access_token, nil
134−func (p *OauthProxy) getUserInfo(token string) (string, error) {
129+ idToken, err := json.Get("id_token").String()
135− params := url.Values{}
136− params.Add("access_token", token)
137− endpoint := fmt.Sprintf("%s?%s", p.oauthUserInfoUrl.String(), params.Encode())
138− log.Printf("calling %s", endpoint)
139− req, err := http.NewRequest("GET", endpoint, nil)
140 if err != nil {
130 if err != nil {
141− log.Printf("failed building request %s", err.Error())
142− return "", err
131+ return "", "", err
144− json, err := apiRequest(req)
133+
134+ // id_token is a base64 encode ID token payload
135+ // https://developers.google.com/accounts/docs/OAuth2Login#obtainuserinfo
136+ jwt := strings.Split(idToken, ".")
137+ b, err := jwtDecodeSegment(jwt[1])
145 if err != nil {
138 if err != nil {
146− log.Printf("failed making request %s", err.Error())
147− return "", err
139+ return "", "", err
149− email, err := json.Get("email").String()
141+ data, err := simplejson.NewJson(b)
150 if err != nil {
142 if err != nil {
151− log.Printf("failed getting email from response %s", err.Error())
143+ return "", "", err
152− return "", err
144+ }
145+ email, err := data.Get("email").String()
150+ return access_token, email, nil
153+func jwtDecodeSegment(seg string) ([]byte, error) {
154+ if l := len(seg) % 4; l > 0 {
155+ seg += strings.Repeat("=", 4-l)
154− return email, nil
157+
158+ return base64.URLEncoding.DecodeString(seg)
157func (p *OauthProxy) ClearCookie(rw http.ResponseWriter, req *http.Request) {
161func (p *OauthProxy) ClearCookie(rw http.ResponseWriter, req *http.Request) {
@@ -301,16 +305,9 @@ func (p *OauthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
304− token, err := p.redeemCode(req.Form.Get("code"))
308+ _, email, err := p.redeemCode(req.Form.Get("code"))
306− log.Printf("error redeeming code %s", err.Error())
307− p.ErrorPage(rw, 500, "Internal Error", err.Error())
311− email, err := p.getUserInfo(token)
312 if err != nil {
309 if err != nil {
313− log.Printf("error redeeming code %s", err.Error())
310+ log.Printf("error redeeming code %s", err)
314 p.ErrorPage(rw, 500, "Internal Error", err.Error())
311 p.ErrorPage(rw, 500, "Internal Error", err.Error())