Unverified Commit f4023add authored by Simon Esposito's avatar Simon Esposito Committed by GitHub
Browse files

Handle Google IAP validation token caching when using credential overrides. (#955)

parent dfb4e0c3
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ The format is based on [keep a changelog](http://keepachangelog.com) and this pr
- Fix handling of users attempting to leave groups they're banned from.
- Fix handling of optional parameters in JS runtime token generate function.
- Ensure group count does not update when failing to add a member.
- Handle Google IAP validation token caching when using credential overrides.

## [3.14.0] - 2022-10-14
### Added
+32 −19
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@ import (
	"sync"
	"time"

	jwt "github.com/golang-jwt/jwt/v4"
	"github.com/golang-jwt/jwt/v4"
)

const (
@@ -53,6 +53,16 @@ const (

const accessTokenExpiresGracePeriod = 300 // 5 min grace period

var cachedTokensGoogle = &googleTokenCache{
	tokenMap: make(map[string]*accessTokenGoogle),
}
var cachedTokenHuawei accessTokenHuawei

type googleTokenCache struct {
	sync.RWMutex
	tokenMap map[string]*accessTokenGoogle
}

type ValidationError struct {
	Err        error
	StatusCode int
@@ -71,9 +81,6 @@ var (
	ErrInvalidSignatureHuawei = errors.New("inAppPurchaseData invalid signature")
)

var cachedTokenGoogle accessTokenGoogle
var cachedTokenHuawei accessTokenHuawei

func init() {
	// Hint to the JWT encoder that single-string arrays should be marshaled as strings.
	// This ensures that for example `["foo"]` is marshaled as `"foo"`.
@@ -285,7 +292,6 @@ type accessTokenGoogle struct {
	RefreshToken string    `json:"refresh_token"`
	Scope        string    `json:"scope"`
	fetchedAt    time.Time // Set when token is received
	sync.RWMutex
}

func (at *accessTokenGoogle) Expired() bool {
@@ -297,17 +303,22 @@ func (at *accessTokenGoogle) Expired() bool {
func getGoogleAccessToken(ctx context.Context, httpc *http.Client, email string, privateKey string) (string, error) {
	const authUrl = "https://accounts.google.com/o/oauth2/token"

	cachedTokenGoogle.RLock()
	if cachedTokenGoogle.AccessToken != "" && !cachedTokenGoogle.Expired() {
		cachedTokenGoogle.RUnlock()
		return cachedTokenGoogle.AccessToken, nil
	cachedTokensGoogle.RLock()
	cacheToken, found := cachedTokensGoogle.tokenMap[email]
	if found && cacheToken.AccessToken != "" && !cacheToken.Expired() {
		cachedTokensGoogle.RUnlock()
		return cacheToken.AccessToken, nil
	}
	cachedTokenGoogle.RUnlock()
	cachedTokenGoogle.Lock()
	defer cachedTokenGoogle.Unlock()
	if cachedTokenGoogle.AccessToken != "" && !cachedTokenGoogle.Expired() {
		return cachedTokenGoogle.AccessToken, nil
	cachedTokensGoogle.RUnlock()

	cachedTokensGoogle.Lock()
	cacheToken, found = cachedTokensGoogle.tokenMap[email]
	if found && cacheToken.AccessToken != "" && !cacheToken.Expired() {
		cachedTokensGoogle.Unlock()
		return cacheToken.AccessToken, nil
	}
	defer cachedTokensGoogle.Unlock()

	type GoogleClaims struct {
		Scope string `json:"scope,omitempty"`
		jwt.RegisteredClaims
@@ -318,8 +329,8 @@ func getGoogleAccessToken(ctx context.Context, httpc *http.Client, email string,
		"https://www.googleapis.com/auth/androidpublisher",
		jwt.RegisteredClaims{
			Audience:  jwt.ClaimStrings{authUrl},
			ExpiresAt: &jwt.NumericDate{now.Add(1 * time.Hour)},
			IssuedAt:  &jwt.NumericDate{now},
			ExpiresAt: jwt.NewNumericDate(now.Add(1 * time.Hour)),
			IssuedAt:  jwt.NewNumericDate(now),
			Issuer:    email,
		},
	}
@@ -364,11 +375,13 @@ func getGoogleAccessToken(ctx context.Context, httpc *http.Client, email string,

	switch resp.StatusCode {
	case 200:
		cachedTokenGoogle.fetchedAt = time.Now()
		if err := json.Unmarshal(buf, &cachedTokenGoogle); err != nil {
		newToken := accessTokenGoogle{}
		if err := json.Unmarshal(buf, &newToken); err != nil {
			return "", err
		}
		return cachedTokenGoogle.AccessToken, nil
		newToken.fetchedAt = time.Now()
		cachedTokensGoogle.tokenMap[email] = &newToken
		return newToken.AccessToken, nil
	default:
		return "", &ValidationError{
			Err:        errors.New("non-200 response from Google auth"),