Loading CHANGELOG.md +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading iap/iap.go +32 −19 Original line number Diff line number Diff line Loading @@ -31,7 +31,7 @@ import ( "sync" "time" jwt "github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v4" ) const ( Loading @@ -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 Loading @@ -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"`. Loading Loading @@ -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 { Loading @@ -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 Loading @@ -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, }, } Loading Loading @@ -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"), Loading Loading
CHANGELOG.md +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
iap/iap.go +32 −19 Original line number Diff line number Diff line Loading @@ -31,7 +31,7 @@ import ( "sync" "time" jwt "github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v4" ) const ( Loading @@ -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 Loading @@ -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"`. Loading Loading @@ -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 { Loading @@ -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 Loading @@ -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, }, } Loading Loading @@ -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"), Loading