Token must be safe for concurrent use by multiple go routines Very similar to the RetrieveToken implementation by the oauth2 package. https://github.com/golang/oauth2/blob/master/internal/token.go#L212 Oauth2 package keeps this code unexported or in an /internal package, so we have to copy the imple
()
| 175 | // Oauth2 package keeps this code unexported or in an /internal package, |
| 176 | // so we have to copy the implementation :( |
| 177 | func (src *jwtTokenSource) Token() (*oauth2.Token, error) { |
| 178 | if src.refreshToken == "" { |
| 179 | return nil, xerrors.New("oauth2: token expired and refresh token is not set") |
| 180 | } |
| 181 | cli := http.DefaultClient |
| 182 | if v, ok := src.ctx.Value(oauth2.HTTPClient).(*http.Client); ok { |
| 183 | // This client should be the instrumented client already. So no need to |
| 184 | // handle this manually. |
| 185 | cli = v |
| 186 | } |
| 187 | |
| 188 | token, err := src.cfg.jwtToken() |
| 189 | if err != nil { |
| 190 | return nil, xerrors.Errorf("failed jwt assertion: %w", err) |
| 191 | } |
| 192 | |
| 193 | v := url.Values{ |
| 194 | "client_assertion": {token}, |
| 195 | "client_assertion_type": {"urn:ietf:params:oauth:client-assertion-type:jwt-bearer"}, |
| 196 | "client_id": {src.cfg.clientID}, |
| 197 | "grant_type": {"refresh_token"}, |
| 198 | "scope": {strings.Join(src.cfg.scopes, " ")}, |
| 199 | "refresh_token": {src.refreshToken}, |
| 200 | } |
| 201 | // Using params based auth |
| 202 | req, err := http.NewRequest("POST", src.cfg.tokenURL, strings.NewReader(v.Encode())) |
| 203 | if err != nil { |
| 204 | return nil, xerrors.Errorf("oauth2: make token refresh request: %w", err) |
| 205 | } |
| 206 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| 207 | req = req.WithContext(src.ctx) |
| 208 | resp, err := cli.Do(req) |
| 209 | if err != nil { |
| 210 | return nil, xerrors.Errorf("oauth2: cannot get token: %w", err) |
| 211 | } |
| 212 | |
| 213 | defer resp.Body.Close() |
| 214 | body, err := io.ReadAll(resp.Body) |
| 215 | if err != nil { |
| 216 | return nil, xerrors.Errorf("oauth2: cannot fetch token reading response body: %w", err) |
| 217 | } |
| 218 | |
| 219 | var tokenRes struct { |
| 220 | AccessToken string `json:"access_token"` |
| 221 | TokenType string `json:"token_type,omitempty"` |
| 222 | RefreshToken string `json:"refresh_token,omitempty"` |
| 223 | |
| 224 | // Extra fields returned by the refresh that are needed |
| 225 | IDToken string `json:"id_token"` |
| 226 | ExpiresIn json.Number `json:"expires_in"` // relative seconds from now, use Number since Azure AD might return string |
| 227 | |
| 228 | // error fields |
| 229 | // https://datatracker.ietf.org/doc/html/rfc6749#section-5.2 |
| 230 | ErrorCode string `json:"error"` |
| 231 | ErrorDescription string `json:"error_description"` |
| 232 | ErrorURI string `json:"error_uri"` |
| 233 | } |
| 234 |