// fetchToken retrieves a new access token and updates the existing token // with the newly fetched credentials. func (c *JWTConfig) FetchToken(existing *Token) (token *Token, err error) { if existing == nil { existing = &Token{} } claimSet := &jws.ClaimSet{ Iss: c.opts.Email, Scope: strings.Join(c.opts.Scopes, " "), Aud: c.aud.String(), } if existing.Subject != "" { claimSet.Sub = existing.Subject // prn is the old name of sub. Keep setting it // to be compatible with legacy OAuth 2.0 providers. claimSet.Prn = existing.Subject } payload, err := jws.Encode(defaultHeader, claimSet, c.signature) if err != nil { return } v := url.Values{} v.Set("grant_type", defaultGrantType) v.Set("assertion", payload) // Make a request with assertion to get a new token. resp, err := http.DefaultClient.PostForm(c.aud.String(), v) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != 200 { // TODO(jbd): Provide more context about the response. return nil, errors.New("Cannot fetch token, response: " + resp.Status) } b := &tokenRespBody{} err = json.NewDecoder(resp.Body).Decode(b) if err != nil { return nil, err } token = &Token{ AccessToken: b.AccessToken, TokenType: b.TokenType, Subject: existing.Subject, } if b.IdToken != "" { // decode returned id token to get expiry claimSet := &jws.ClaimSet{} claimSet, err = jws.Decode(b.IdToken) if err != nil { return } token.Expiry = time.Unix(claimSet.Exp, 0) return } token.Expiry = time.Now().Add(time.Duration(b.ExpiresIn) * time.Second) return }
func (js jwtSource) Token() (*oauth2.Token, error) { pk, err := internal.ParseKey(js.conf.PrivateKey) if err != nil { return nil, err } hc := oauth2.NewClient(js.ctx, nil) claimSet := &jws.ClaimSet{ Iss: js.conf.Email, Scope: strings.Join(js.conf.Scopes, " "), Aud: js.conf.TokenURL, } if subject := js.conf.Subject; subject != "" { claimSet.Sub = subject // prn is the old name of sub. Keep setting it // to be compatible with legacy OAuth 2.0 providers. claimSet.Prn = subject } payload, err := jws.Encode(defaultHeader, claimSet, pk) if err != nil { return nil, err } v := url.Values{} v.Set("grant_type", defaultGrantType) v.Set("assertion", payload) resp, err := hc.PostForm(js.conf.TokenURL, v) if err != nil { return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) } defer resp.Body.Close() body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20)) if err != nil { return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) } if c := resp.StatusCode; c < 200 || c > 299 { return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", resp.Status, body) } // tokenRes is the JSON response body. var tokenRes struct { AccessToken string `json:"access_token"` TokenType string `json:"token_type"` IDToken string `json:"id_token"` ExpiresIn int64 `json:"expires_in"` // relative seconds from now } if err := json.Unmarshal(body, &tokenRes); err != nil { return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) } token := &oauth2.Token{ AccessToken: tokenRes.AccessToken, TokenType: tokenRes.TokenType, } raw := make(map[string]interface{}) json.Unmarshal(body, &raw) // no error checks for optional fields token = token.WithExtra(raw) if secs := tokenRes.ExpiresIn; secs > 0 { token.Expiry = time.Now().Add(time.Duration(secs) * time.Second) } if v := tokenRes.IDToken; v != "" { // decode returned id token to get expiry claimSet, err := jws.Decode(v) if err != nil { return nil, fmt.Errorf("oauth2: error decoding JWT token: %v", err) } token.Expiry = time.Unix(claimSet.Exp, 0) } return token, nil }