AuthorizationCodeGrant exchanges an authorization code + PKCE code_verifier for an access token. The redirectURI must match the one used in the authorize URL.
( code, codeVerifier, redirectURI string, )
| 114 | // AuthorizationCodeGrant exchanges an authorization code + PKCE code_verifier |
| 115 | // for an access token. The redirectURI must match the one used in the authorize URL. |
| 116 | func (c *Client) AuthorizationCodeGrant( |
| 117 | code, codeVerifier, redirectURI string, |
| 118 | ) (*OAuthTokenResponse, error) { |
| 119 | form := url.Values{ |
| 120 | "grant_type": {"authorization_code"}, |
| 121 | "client_id": {c.ClientID}, |
| 122 | "code": {code}, |
| 123 | "code_verifier": {codeVerifier}, |
| 124 | "redirect_uri": {redirectURI}, |
| 125 | } |
| 126 | |
| 127 | req, err := http.NewRequest( |
| 128 | http.MethodPost, |
| 129 | c.DashboardURL+"/2/oauth/token", |
| 130 | strings.NewReader(form.Encode()), |
| 131 | ) |
| 132 | if err != nil { |
| 133 | return nil, err |
| 134 | } |
| 135 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| 136 | req.Header.Set("Accept", "application/json") |
| 137 | |
| 138 | resp, err := c.client.Do(req) |
| 139 | if err != nil { |
| 140 | return nil, fmt.Errorf("token request failed: %w", err) |
| 141 | } |
| 142 | defer resp.Body.Close() |
| 143 | |
| 144 | if resp.StatusCode != http.StatusOK { |
| 145 | return nil, parseOAuthError(resp, "authorization code exchange") |
| 146 | } |
| 147 | |
| 148 | var tokenResp OAuthTokenResponse |
| 149 | if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil { |
| 150 | return nil, fmt.Errorf("failed to parse token response: %w", err) |
| 151 | } |
| 152 | |
| 153 | return &tokenResp, nil |
| 154 | } |
| 155 | |
| 156 | // RefreshToken uses a refresh token to obtain a new access token. |
| 157 | func (c *Client) RefreshToken(refreshToken string) (*OAuthTokenResponse, error) { |