git.iko.soy/typeable/oauth2-proxy
on master help sign in
update to new scopes
98fb800Jehiah Czebotar
main.go+1−1
@@ -5,9 +5,9 @@ import (
5 "fmt"
5 "fmt"
6 "log"
6 "log"
7 "net"
7 "net"
8− "os"
9 "net/http"
8 "net/http"
10 "net/url"
9 "net/url"
10+ "os"
11 "strings"
11 "strings"
12)
12)
13
13
oauthproxy.go+35−38
@@ -5,7 +5,6 @@ import (
5 "encoding/base64"
5 "encoding/base64"
6 "errors"
6 "errors"
7 "fmt"
7 "fmt"
8− "github.com/bitly/go-simplejson"
9 "io/ioutil"
8 "io/ioutil"
10 "log"
9 "log"
11 "net/http"
10 "net/http"
@@ -13,6 +12,8 @@ import (
13 "net/url"
12 "net/url"
14 "strings"
13 "strings"
15 "time"
14 "time"
15+
16+ "github.com/bitly/go-simplejson"
16)
17)
17
18
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
54
53
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,
62 }
60 }
63}
61}
@@ -102,9 +100,9 @@ func apiRequest(req *http.Request) (*simplejson.Json, error) {
102 return data, nil
100 return data, nil
103}
101}
104
102
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")
108 }
106 }
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
120 }
117 }
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
126 }
123 }
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
130 }
127 }
131− return access_token, nil
132−}
133
128
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
143 }
132 }
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
148 }
140 }
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()
146+ if err != nil {
147+ return "", "", err
148+ }
149+
150+ return access_token, email, nil
151+}
152+
153+func jwtDecodeSegment(seg string) ([]byte, error) {
154+ if l := len(seg) % 4; l > 0 {
155+ seg += strings.Repeat("=", 4-l)
153 }
156 }
154− return email, nil
157+
158+ return base64.URLEncoding.DecodeString(seg)
155}
159}
156
160
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) {
301 return
305 return
302 }
306 }
303
307
304− token, err := p.redeemCode(req.Form.Get("code"))
308+ _, email, err := p.redeemCode(req.Form.Get("code"))
305− if err != nil {
306− log.Printf("error redeeming code %s", err.Error())
307− p.ErrorPage(rw, 500, "Internal Error", err.Error())
308− return
309− }
310− // validate user
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())
315 return
312 return
316 }
313 }