如何存储访问令牌以重复使用?

I'm using go's oauth2 package to make requests to Instagram on behalf of a user. The only part I have left to figure out is how to store the access/refresh token and then how do I use it again with oauth2? Here's the code I have so far, all it does is grab the access token and make one request to the API. After that I don't know what to do.

package main

import "net/http"
import "io/ioutil"
import "fmt"
import "html/template"
import "golang.org/x/oauth2"

var ClientID = YOUR_CLIENT_ID

var ClientSecret = YOUR_CLIENT_SECRET
var RedirectURI = "http://localhost:8080/redirect"

var authURL = "https://api.instagram.com/oauth/authorize"

var tokenURL = "https://api.instagram.com/oauth/access_token"

var templ = template.Must(template.New("index.html").ParseFiles("index.html"))

var igConf *oauth2.Config

func redirect(res http.ResponseWriter, req *http.Request) {

    code := req.FormValue("code")

    if len(code) != 0 {
        tok, err := igConf.Exchange(oauth2.NoContext, code)
        if err != nil {
            fmt.Println(err)
            http.NotFound(res, req)
            return
        }

        if tok.Valid() {
            client := igConf.Client(oauth2.NoContext, tok)

            request, err := http.NewRequest("GET", "https://api.instagram.com/v1/users/self/?access_token="+tok.AccessToken, nil)
            if err != nil {
                fmt.Println(err)
                http.NotFound(res, req)
                return
            }

            resp, err := client.Do(request)
            if err != nil {
                fmt.Println(err)
                http.NotFound(res, req)
                return
            }
            defer resp.Body.Close()

            body, err := ioutil.ReadAll(resp.Body)
            if err != nil {
                fmt.Println(err)
                http.NotFound(res, req)
                return
            }

            res.Write(body)
        }

        http.NotFound(res, req)
    }

}

func homePage(res http.ResponseWriter, req *http.Request) {
    url := igConf.AuthCodeURL("", oauth2.AccessTypeOffline)
    fmt.Println(url)
    err := templ.Execute(res, url)
    if err != nil {
        fmt.Println(err)
    }
}

func main() {
    igConf = &oauth2.Config{
        ClientID:     ClientID,
        ClientSecret: ClientSecret,
        Endpoint: oauth2.Endpoint{
            AuthURL:  authURL,
            TokenURL: tokenURL,
        },
        RedirectURL: RedirectURI,
        Scopes:      []string{"public_content", "comments"},
    }

    http.HandleFunc("/redirect", redirect)
    http.HandleFunc("/", homePage)
    http.ListenAndServe(":8080", nil)
}

You could store the access token in a cookie, by inserting the following code immediately before the res.Write(body) line in the redirect() function:

res.SetCookie(&Cookie{
   Name: "access_token",
   Value: tok.AccessToken,
   Expires: time.Now().Add(time.Hour * 24), // expires in 24 hours
}

In some other handler, you would read this token back again like this:

accessCookie, err := req.Cookie("access_token")
if err != nil {
   res.WriteHeader(http.StatusUnauthorized)
   fmt.Fprintln(res, "no access token provided")
   return
}
accessToken := accessCookie.Value
// make your requests to Instagram here

Why would you store an Access Token?

Both Access and Refresh tokens are Bearer tokens and should be handled as defined in RFC 6750.

You are often better off simply re-doing the authorization flow when they come back and click the login button again.

If you store the token you need to worry about securing data in the token and these tokens give access to some fairly privileged information about your users.

I would suggest, where you can, move to OpenID Connect and where you are using JWT which are encrypted and signed.

-jim

hi i wrote a module to token storage and management with time delay for expiration time take a look to this module i think it can help you, i call this storage from my middleware to manage the tokens

 # storage.go
 package token_store

 import (
     "sync"
     "time"
 )

 type item struct {
    token      string
    lastAccess int64
 }

 // storage structure to managing the store tokens
 type Storage struct {
     storeMap map[string]*item
     lock     sync.Mutex
 }

 func NewStore(len, expireTime int64) (s *Storage) {
     // initial the storage
     s = &Store{storeMap: make(map[string]*item, len)}
     go func() {
         // each minute check that token has expired or not
         for now := range time.Tick(time.Minute) {
             // lock the cache for updating the values
             s.lock.Lock()
             for key, value := range s.storeMap {
                 /*
                     if expiration time exceeded then start to removing
                     the token from cache
                 */
                 if now.Unix()-value.lastAccess > expireTime {
                    delete(s.storeMap, key)
                 }
             }
             s.lock.Unlock()
         }
     }()
     return
 }

 func (s *Storage) Len() int {
    // return the len of storage map
    return len(s.storeMap)
 }

func (s *Storage) Put(key, value string) {
    /*
        value will be the authentication token
        key can be user-name or anay things that you
        want to access token within it
    */
    s.lock.Lock()
    if it, ok := s.storeMap[key]; !ok { // if token is not exist
        it = &item{value, time.Now().Unix()}
        s.storeMap[key] = it
    } else { // if token exist then update the last access time
        it.lastAccess = time.Now().Unix()
    }
    s.lock.Unlock()

 }

 func (s *Storage) Get(key string) (value string) {
     /*
         check out hte item
     */
     s.lock.Lock()
     if item, ok := s.storeMap[key]; ok { // check if item exist then pass the value
          value = item.token
          item.lastAccess = time.Now().Unix()
     }
     s.lock.Unlock()
     return
 }

i hope this post be helpfull for you.