Created
February 3, 2015 18:05
-
-
Save jfcote87/3c4550e4dedb563e9f9a to your computer and use it in GitHub Desktop.
Example of oauth2 bug: RefreshToken not updated in TokenSource
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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