package googleapi import ( "encoding/json" "fmt" "gogs.mikescher.com/BlackForestBytes/goext/exerr" "gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/timeext" "io" "net/http" "sync" "time" ) type GoogleOAuth interface { AccessToken() (string, error) } type oauth struct { clientID string clientSecret string refreshToken string lock sync.RWMutex accessToken *string expiryDate *time.Time } func NewGoogleOAuth(clientid string, clientsecret, refreshtoken string) GoogleOAuth { return &oauth{ clientID: clientid, clientSecret: clientsecret, refreshToken: refreshtoken, } } func (c *oauth) AccessToken() (string, error) { c.lock.RLock() if c.accessToken != nil && c.expiryDate != nil && (*c.expiryDate).After(time.Now()) { c.lock.RUnlock() return *c.accessToken, nil // still valid } c.lock.RUnlock() httpclient := http.Client{} url := fmt.Sprintf("https://oauth2.googleapis.com/token?client_id=%s&client_secret=%s&grant_type=%s&refresh_token=%s", c.clientID, c.clientSecret, "refresh_token", c.refreshToken) req, err := http.NewRequest(http.MethodPost, url, nil) if err != nil { return "", err } reqStartTime := time.Now() res, err := httpclient.Do(req) type response struct { AccessToken string `json:"access_token"` ExpiresIn int `json:"expires_in"` Scope string `json:"scope"` TokenType string `json:"token_type"` } var r response data, err := io.ReadAll(res.Body) if err != nil { return "", err } err = json.Unmarshal(data, &r) if err != nil { return "", err } if r.ExpiresIn == 0 || r.AccessToken == "" { return "", exerr.New(exerr.TypeGoogleResponse, "google oauth returned no response").Str("body", string(data)).Build() } c.lock.Lock() c.expiryDate = langext.Ptr(reqStartTime.Add(timeext.FromSeconds(r.ExpiresIn - 10))) c.accessToken = langext.Ptr(r.AccessToken) c.lock.Unlock() return r.AccessToken, nil }