Merge pull request #25 from jehiah/google_oauth_migration_25

/v2/userinfo going away, switch to new scopes and id token payload
This commit is contained in:
Pierce Lopez 2014-08-07 17:21:27 -04:00
commit 1c0989ff58
2 changed files with 41 additions and 44 deletions

View File

@ -5,13 +5,13 @@ import (
"fmt"
"log"
"net"
"os"
"net/http"
"net/url"
"os"
"strings"
)
const VERSION = "0.0.1"
const VERSION = "0.1.0"
var (
showVersion = flag.Bool("version", false, "print version string")

View File

@ -5,7 +5,6 @@ import (
"encoding/base64"
"errors"
"fmt"
"github.com/bitly/go-simplejson"
"io/ioutil"
"log"
"net/http"
@ -13,6 +12,8 @@ import (
"net/url"
"strings"
"time"
"github.com/bitly/go-simplejson"
)
const signInPath = "/oauth2/sign_in"
@ -27,7 +28,6 @@ type OauthProxy struct {
redirectUrl *url.URL // the url to receive requests at
oauthRedemptionUrl *url.URL // endpoint to redeem the code
oauthLoginUrl *url.URL // to redirect the user to
oauthUserInfoUrl *url.URL
oauthScope string
clientID string
clientSecret string
@ -39,7 +39,6 @@ type OauthProxy struct {
func NewOauthProxy(proxyUrls []*url.URL, clientID string, clientSecret string, validator func(string) bool) *OauthProxy {
login, _ := url.Parse("https://accounts.google.com/o/oauth2/auth")
redeem, _ := url.Parse("https://accounts.google.com/o/oauth2/token")
info, _ := url.Parse("https://www.googleapis.com/oauth2/v2/userinfo")
serveMux := http.NewServeMux()
for _, u := range proxyUrls {
path := u.Path
@ -54,10 +53,9 @@ func NewOauthProxy(proxyUrls []*url.URL, clientID string, clientSecret string, v
clientID: clientID,
clientSecret: clientSecret,
oauthScope: "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
oauthScope: "profile email",
oauthRedemptionUrl: redeem,
oauthLoginUrl: login,
oauthUserInfoUrl: info,
serveMux: serveMux,
}
}
@ -102,9 +100,9 @@ func apiRequest(req *http.Request) (*simplejson.Json, error) {
return data, nil
}
func (p *OauthProxy) redeemCode(code string) (string, error) {
func (p *OauthProxy) redeemCode(code string) (string, string, error) {
if code == "" {
return "", errors.New("missing code")
return "", "", errors.New("missing code")
}
params := url.Values{}
params.Add("redirect_uri", p.redirectUrl.String())
@ -112,46 +110,52 @@ func (p *OauthProxy) redeemCode(code string) (string, error) {
params.Add("client_secret", p.clientSecret)
params.Add("code", code)
params.Add("grant_type", "authorization_code")
log.Printf("body is %s", params.Encode())
req, err := http.NewRequest("POST", p.oauthRedemptionUrl.String(), bytes.NewBufferString(params.Encode()))
if err != nil {
log.Printf("failed building request %s", err.Error())
return "", err
return "", "", err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
json, err := apiRequest(req)
if err != nil {
log.Printf("failed making request %s", err.Error())
return "", err
log.Printf("failed making request %s", err)
return "", "", err
}
access_token, err := json.Get("access_token").String()
if err != nil {
return "", err
return "", "", err
}
return access_token, nil
idToken, err := json.Get("id_token").String()
if err != nil {
return "", "", err
}
// id_token is a base64 encode ID token payload
// https://developers.google.com/accounts/docs/OAuth2Login#obtainuserinfo
jwt := strings.Split(idToken, ".")
b, err := jwtDecodeSegment(jwt[1])
if err != nil {
return "", "", err
}
data, err := simplejson.NewJson(b)
if err != nil {
return "", "", err
}
email, err := data.Get("email").String()
if err != nil {
return "", "", err
}
return access_token, email, nil
}
func (p *OauthProxy) getUserInfo(token string) (string, error) {
params := url.Values{}
params.Add("access_token", token)
endpoint := fmt.Sprintf("%s?%s", p.oauthUserInfoUrl.String(), params.Encode())
log.Printf("calling %s", endpoint)
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
log.Printf("failed building request %s", err.Error())
return "", err
func jwtDecodeSegment(seg string) ([]byte, error) {
if l := len(seg) % 4; l > 0 {
seg += strings.Repeat("=", 4-l)
}
json, err := apiRequest(req)
if err != nil {
log.Printf("failed making request %s", err.Error())
return "", err
}
email, err := json.Get("email").String()
if err != nil {
log.Printf("failed getting email from response %s", err.Error())
return "", err
}
return email, nil
return base64.URLEncoding.DecodeString(seg)
}
func (p *OauthProxy) ClearCookie(rw http.ResponseWriter, req *http.Request) {
@ -301,16 +305,9 @@ func (p *OauthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
return
}
token, err := p.redeemCode(req.Form.Get("code"))
_, email, err := p.redeemCode(req.Form.Get("code"))
if err != nil {
log.Printf("error redeeming code %s", err.Error())
p.ErrorPage(rw, 500, "Internal Error", err.Error())
return
}
// validate user
email, err := p.getUserInfo(token)
if err != nil {
log.Printf("error redeeming code %s", err.Error())
log.Printf("error redeeming code %s", err)
p.ErrorPage(rw, 500, "Internal Error", err.Error())
return
}