golang/oauth2

User Principal OAuth2 (Three Legged, Authorization Code) does not include Refresh Token(s)

YvanJAquino opened this issue · 3 comments

Hi there,

I'm seeing some unexpected behavior while interacting with oauth2.Config & oauth2.Token objects.

My client targets a Google Cloud OAuth Client ID (via the Oauth Consent Screens service AKA OAuth Brands). My client uses a web app type credential which exists in the following format:

{
  "web": {
      "client_id": "...",
      "project_id": "...",
      "auth_uri": "https://accounts.google.com/o/oauth2/auth",
      "token_uri": "https://oauth2.googleapis.com/token",
      "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
      "client_secret": "...",
      "redirect_uris": [
          "http://localhost",
          "https://oauth2-authorizer-service"
      ]
  }
}

Creating a config from this always works fine. I then update the redirect URL, generate a randomized state string, and generate an AuthCodeURL, specifying the oauth2.AccessTypeOffline option.

/ Local Fetch token initiates a request from the localhost; the user is responsible for
// providing the state and auth code.
func (tm *TokenManager) LocalFetchToken(ctx context.Context) (err error) {
	// Generate an opaque identifier by hex-digesting random bytes.
	stateBuf := make([]byte, 32)
	if _, err = rand.Reader.Read(stateBuf); err != nil {
		return
	}
	oauth_state := hex.EncodeToString(stateBuf)
	authCodeURL := tm.config.AuthCodeURL(oauth_state, oauth2.AccessTypeOffline)
	fmt.Printf("Please go to the following link in your browser to generate an auth code:\n\t%s\n", authCodeURL)
	var code, state string

	fmt.Print("Authorization code:")
	fmt.Scanln(&code)
	fmt.Print("\nAuthorization state:")
	fmt.Scanln(&state)

	if oauth_state != state {
		return fmt.Errorf("states do not match")
	}

	token, err := tm.config.Exchange(ctx, code)
	if err != nil {
		return
	}
	fmt.Printf("Token Refresh Token: %s\n", token.RefreshToken)
	tm.token = token
	return
}

This is not producing Refresh tokens even with the oauth2.AccessTypeOffline option. In the past, this used to issue Refresh tokens.

According to the documentation on oauth2.AccessTypeOffline documentation:

	// AccessTypeOnline and AccessTypeOffline are options passed
	// to the Options.AuthCodeURL method. They modify the
	// "access_type" field that gets sent in the URL returned by
	// AuthCodeURL.
	//
	// Online is the default if neither is specified. If your
	// application needs to refresh access tokens when the user
	// is not present at the browser, then use offline. This will
	// result in your application obtaining **a refresh token the**
	// **first time your application exchanges an authorization**
	// **code for a user.**
	AccessTypeOnline  AuthCodeOption = SetAuthURLParam("access_type", "online")
	AccessTypeOffline AuthCodeOption = SetAuthURLParam("access_type", "offline")

Regards,

Perhaps I encountered a similar problem, only I have slightly different conditions. I use keycloak and log in using the code, but in response I receive a token without refreshToken
image
however, there are examples where it is clear that refreshToken is provided
image

I created a new client and it now has a button to enable token renewal

It's worth noting that I need to prompt for consent - IE I need to do the ENTIRE flow.