Skip to content

Instantly share code, notes, and snippets.

@jfcote87
Created February 3, 2015 18:05
Show Gist options
  • Select an option

  • Save jfcote87/3c4550e4dedb563e9f9a to your computer and use it in GitHub Desktop.

Select an option

Save jfcote87/3c4550e4dedb563e9f9a to your computer and use it in GitHub Desktop.
Example of oauth2 bug: RefreshToken not updated in TokenSource
package main
import (
"encoding/json"
"fmt"
"golang.org/x/oauth2"
"log"
"net/http"
"net/http/httptest"
"time"
)
// ServerToken is used to simulate a token response
// from an oauth2 Authorization Server
type ServerToken struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
RefreshToken string `json:"refresh_token,omitempty"`
}
const expiresIn = 2 // ensure that token refreshes every time.
func main() {
currentValidRefreshToken := ""
counter := 0
// Handle oauth code exchange and refresh requests
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.RequestURI() {
case "/refresh":
err := r.ParseForm()
if err != nil {
http.Error(w, "FormParse: "+err.Error(), 500)
return
}
reqRefreshToken := r.Form.Get("refresh_token")
if reqRefreshToken != currentValidRefreshToken {
msg := fmt.Sprintf("refresh_token = %s; want %s", reqRefreshToken, currentValidRefreshToken)
http.Error(w, msg, 500)
}
case "/exchange":
// exchange any code for new refresh token
default:
http.Error(w, "Unknown url "+r.URL.RequestURI(), 404)
return
}
counter += 1
currentValidRefreshToken = fmt.Sprintf("REFRESH_TOKEN_%d", counter)
tk := &ServerToken{
AccessToken: fmt.Sprintf("NEW_ACCESS_TOKEN_%d", counter),
TokenType: "Bearer",
ExpiresIn: expiresIn,
RefreshToken: currentValidRefreshToken,
}
payload, err := json.Marshal(tk)
if err != nil {
http.Error(w, "Json decode: "+err.Error(), 500)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(payload)
}))
defer ts.Close()
config := newConf(ts.URL)
// Exchange code for new Token w/ RefreshToken set
tk, err := config.Exchange(oauth2.NoContext, "Anycode")
if err != nil {
log.Fatalf("Exchange faild: %v", err)
return
}
log.Print("Exchange code for token")
log.Printf("access_token: %s refresh_token: %s", tk.AccessToken, tk.RefreshToken)
// create new token source using token
tkSrc := config.TokenSource(oauth2.NoContext, tk)
// expire token to force refresh
time.Sleep(expiresIn * time.Second)
tk, err = tkSrc.Token()
if err != nil {
log.Fatalf("First Token Refresh failed: %v", err)
return
}
log.Print("First token refresh successful.")
log.Printf("access_token: %s refresh_token: %s", tk.AccessToken, tk.RefreshToken)
time.Sleep(expiresIn * time.Second)
tk, err = tkSrc.Token()
if err != nil {
log.Fatalf("2nd Token Refresh failed: %v", err)
return
}
//
log.Print("2nd token refresh successful.")
log.Printf("access_token: %s refresh_token: %s", tk.AccessToken, tk.RefreshToken)
}
func newConf(url string) *oauth2.Config {
return &oauth2.Config{
ClientID: "CLIENT_ID",
ClientSecret: "CLIENT_SECRET",
RedirectURL: "REDIRECT_URL",
Scopes: []string{"user"},
Endpoint: oauth2.Endpoint{
AuthURL: url + "/exchange",
TokenURL: url + "/refresh",
},
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment