0
votes

After a user authorizes Google Calendar, the NodeJS service saves the Code, AccessToken and RefreshToken to a storage.

Trying to use the same token to access the users' calendar using different backend service written in Go. When the AccessToken is valid, the data is accessible, but when AccessToken expires unable to get the config.Exchange() or config.TokenSource() to give a new token which works, even though the token is valid, when trying to access events, get error :

Error 401: Invalid Credentials, authError exit status 1

 tok, err := config.TokenSource(ctx, token).Token() // token is previous valid token
 if err != nil {
    log.Fatalf("Unable to retrieve token from web: %v", err)
 }

Also tried to exchange code for a new token but doesn't help

400 Bad Request Response: { "error": "invalid_grant", "error_description": "Malformed auth code." }

    tok, err := config.Exchange(context.TODO(), in)
if err != nil {
    log.Fatalf("Unable to retrieve token from web: %v", err)
}

Trying to access using calendar.NewService

srv, _ := calendar.NewService(ctx, option.WithTokenSource(config.TokenSource(ctx, tok)))

How to get a token which can access offline without user intervention from another service ?

Update: Storing token to Redis - RedisJSON but still won't get a new AccessToken. This is the complete function where I am passing a valid Token. It works only before AccessToken expiry.

func GetGoogleCalendarEvents(token *oauth2.Token, userid string) *calendar.Events {
tok := &oauth2.Token{}
var config *oauth2.Config
ctx := context.Background()

b, err := ioutil.ReadFile("credentials.json")
if err != nil {
    log.Fatalf("Unable to read client secret file: %v", err)
}

config, err = google.ConfigFromJSON(b, calendar.CalendarScope) //calendar.CalendarScope)
if err != nil {
    log.Fatalf("Unable to parse client secret file to config: %v", err)
}

tok = token

if token.Expiry.Before(time.Now()) {

    tokenSource := config.TokenSource(ctx, token) //oauth2.NoContext

    newToken, err := tokenSource.Token()
    if err != nil {
        log.Fatalln(err)
    }

    if tok.AccessToken != newToken.AccessToken {
        SetAuthCredToCache(userid, tok)
        tok = newToken
        fmt.Println(newToken)
    }
}

fmt.Println(tok.Expiry, tok.Valid(), tok.Type(), tok.RefreshToken, tok.TokenType)

srv, _ := calendar.NewService(ctx, option.WithTokenSource(config.TokenSource(ctx, tok)))

t := time.Now().Format(time.RFC3339)

events, err := srv.Events.List("primary").ShowDeleted(true).
    SingleEvents(true).TimeMin(t).OrderBy("startTime").Do()

if err != nil {
    log.Fatalf("Unable to retrieve next ten of the user's events: %v", err)
}
return events

}

1
Show the code where the token is saved and loaded.Cerise Limón
Remembering the authorization code is pointless: "If an authorization code is used more than once, the authorization server MUST deny the request" tools.ietf.org/html/rfc6749#section-4.1.2Peter
hi @CeriseLimón. I am storing it to Redis as string. Code, AccessToken, RefreshToken.Walker
Save and restore the entire token by serializing to and from JSON. Does the application save the token returned from a refresh?Cerise Limón
Thanks, will try that. No, the application doesn't save, was trying with new token every time to see if it works. But will save the refreshed token now and update.Walker

1 Answers

0
votes

It was an Unmarshalling problem.

When saving token from Node, it serializes expiry time as "expiry_date" and Go Token has json representation as just "expiry" . Unmarshalling the rest of the object to oauth2.Token object and adding expiry time to that object solved the problem.