1
0
mirror of https://github.com/oauth2-proxy/oauth2-proxy.git synced 2025-06-17 00:17:40 +02:00

SessionState refactoring; improve token renewal and cookie refresh

* New SessionState to consolidate email, access token and refresh token
* split ServeHttp into individual methods
* log on session renewal
* log on access token refresh
* refactor cookie encription/decription and session state serialization
This commit is contained in:
Jehiah Czebotar
2015-06-23 07:23:39 -04:00
parent b9ae5dc8d7
commit d49c3e167f
21 changed files with 883 additions and 597 deletions

View File

@ -7,9 +7,11 @@ import (
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"strings"
"time"
)
type GoogleProvider struct {
@ -43,25 +45,19 @@ func NewGoogleProvider(p *ProviderData) *GoogleProvider {
return &GoogleProvider{ProviderData: p}
}
func (s *GoogleProvider) GetEmailAddress(body []byte, access_token string) (string, error) {
var response struct {
IdToken string `json:"id_token"`
}
if err := json.Unmarshal(body, &response); err != nil {
return "", err
}
func emailFromIdToken(idToken string) (string, error) {
// id_token is a base64 encode ID token payload
// https://developers.google.com/accounts/docs/OAuth2Login#obtainuserinfo
jwt := strings.Split(response.IdToken, ".")
jwt := strings.Split(idToken, ".")
b, err := jwtDecodeSegment(jwt[1])
if err != nil {
return "", err
}
var email struct {
Email string `json:"email"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
}
err = json.Unmarshal(b, &email)
if err != nil {
@ -70,6 +66,9 @@ func (s *GoogleProvider) GetEmailAddress(body []byte, access_token string) (stri
if email.Email == "" {
return "", errors.New("missing email")
}
if !email.EmailVerified {
return "", fmt.Errorf("email %s not listed as verified", email.Email)
}
return email.Email, nil
}
@ -81,11 +80,7 @@ func jwtDecodeSegment(seg string) ([]byte, error) {
return base64.URLEncoding.DecodeString(seg)
}
func (p *GoogleProvider) ValidateToken(access_token string) bool {
return validateToken(p, access_token, nil)
}
func (p *GoogleProvider) Redeem(redirectUrl, code string) (body []byte, token string, err error) {
func (p *GoogleProvider) Redeem(redirectUrl, code string) (s *SessionState, err error) {
if code == "" {
err = errors.New("missing code")
return
@ -108,6 +103,7 @@ func (p *GoogleProvider) Redeem(redirectUrl, code string) (body []byte, token st
if err != nil {
return
}
var body []byte
body, err = ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
@ -122,17 +118,44 @@ func (p *GoogleProvider) Redeem(redirectUrl, code string) (body []byte, token st
var jsonResponse struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ExpiresIn int64 `json:"expires_in"`
IdToken string `json:"id_token"`
}
err = json.Unmarshal(body, &jsonResponse)
if err != nil {
return
}
token, err = p.redeemRefreshToken(jsonResponse.RefreshToken)
var email string
email, err = emailFromIdToken(jsonResponse.IdToken)
if err != nil {
return
}
s = &SessionState{
AccessToken: jsonResponse.AccessToken,
ExpiresOn: time.Now().Add(time.Duration(jsonResponse.ExpiresIn) * time.Second).Truncate(time.Second),
RefreshToken: jsonResponse.RefreshToken,
Email: email,
}
return
}
func (p *GoogleProvider) redeemRefreshToken(refreshToken string) (token string, err error) {
func (p *GoogleProvider) RefreshSessionIfNeeded(s *SessionState) (bool, error) {
if s == nil || s.ExpiresOn.After(time.Now()) || s.RefreshToken == "" {
return false, nil
}
newToken, duration, err := p.redeemRefreshToken(s.RefreshToken)
if err != nil {
return false, err
}
origExpiration := s.ExpiresOn
s.AccessToken = newToken
s.ExpiresOn = time.Now().Add(duration).Truncate(time.Second)
log.Printf("refreshed access token %s (expired on %s)", s, origExpiration)
return true, nil
}
func (p *GoogleProvider) redeemRefreshToken(refreshToken string) (token string, expires time.Duration, err error) {
// https://developers.google.com/identity/protocols/OAuth2WebServer#refresh
params := url.Values{}
params.Add("client_id", p.ClientID)
@ -162,12 +185,15 @@ func (p *GoogleProvider) redeemRefreshToken(refreshToken string) (token string,
return
}
var jsonResponse struct {
var data struct {
AccessToken string `json:"access_token"`
ExpiresIn int64 `json:"expires_in"`
}
err = json.Unmarshal(body, &jsonResponse)
err = json.Unmarshal(body, &data)
if err != nil {
return
}
return jsonResponse.AccessToken, nil
token = data.AccessToken
expires = time.Duration(data.ExpiresIn) * time.Second
return
}