markbates / markbates/goth

getting a meaningful error out of oauth token exchange

Open
#348 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
6.6k
Forks
631
PR merge metrics
No merged PRs in 30d

Description

I've successfully used this goth library, however, I found that
golang/x/oauth2 eats the server's useful error during Exchange() returning:

oauth2: server response missing access_token

... instead of, for example:

invalid_team_for_non_distributed_app

I used this patch to oauth2 to get the error returned:

diff --git a/vendor/golang.org/x/oauth2/internal/token.go b/vendor/golang.org/x/oauth2/internal/token.go
index 0aa16905..3288de96 100644
--- a/vendor/golang.org/x/oauth2/internal/token.go
+++ b/vendor/golang.org/x/oauth2/internal/token.go
@@ -64,6 +64,7 @@ type tokenJSON struct {
        TokenType    string         `json:"token_type"`
        RefreshToken string         `json:"refresh_token"`
        ExpiresIn    expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
+       Error        string         `json:"error"`
 }
 
 func (e *tokenJSON) expiry() (t time.Time) {
@@ -216,7 +217,11 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string,
                // So just try both ways.
                authStyle = AuthStyleInParams // the second way we'll try
                req, _ = newTokenRequest(tokenURL, clientID, clientSecret, v, authStyle)
-               token, err = doTokenRoundTrip(ctx, req)
+               var err2 error
+               token, err2 = doTokenRoundTrip(ctx, req)
+               if err2 == nil {
+                       err = nil
+               }
        }
        if needsAuthStyleProbe && err == nil {
                setAuthStyle(tokenURL, authStyle)
@@ -245,8 +250,8 @@ func doTokenRoundTrip(ctx context.Context, req *http.Request) (*Token, error) {
                        Body:     body,
                }
        }
+       var errorStr string
        var token *Token
        content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
        switch content {
@@ -266,6 +271,7 @@ func doTokenRoundTrip(ctx context.Context, req *http.Request) (*Token, error) {
                if expires != 0 {
                        token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
                }
+               errorStr = vals.Get("error")
        default:
                var tj tokenJSON
                if err = json.Unmarshal(body, &tj); err != nil {
@@ -279,9 +285,14 @@ func doTokenRoundTrip(ctx context.Context, req *http.Request) (*Token, error) {
                        Raw:          make(map[string]interface{}),
                }
                json.Unmarshal(body, &token.Raw) // no error checks for optional fields
+               errorStr = tj.Error
        }
        if token.AccessToken == "" {
-               return nil, errors.New("oauth2: server response missing access_token")
+               err = errors.New("oauth2: server response missing access_token")
+               if errorStr != "" {
+                       err = errors.New(errorStr)
+               }
+               return nil, err
        }
        return token, nil
 }

Does anyone have a better solution? My challenge now is were and how to maintain this patch.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing vendor/golang.org/x/oauth2/internal/token.go, especially RetrieveToken and doTokenRoundTrip, and trace how goth reaches Exchange(). Compare the vendored behavior with the upstream oauth2 package and determine how server error responses should be exposed. Done means a maintainable approach preserves meaningful token-exchange errors without relying on an unmaintained local patch.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
authentication, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.