func TestJWTAccessTokenSourceFromJSON(t *testing.T) { // Generate a key we can use in the test data. privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatal(err) } // Encode the key and substitute into our example JSON. enc := pem.EncodeToMemory(&pem.Block{ Type: "PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey), }) enc, err = json.Marshal(string(enc)) if err != nil { t.Fatalf("json.Marshal: %v", err) } jsonKey := bytes.Replace(jwtJSONKey, []byte(`"super secret key"`), enc, 1) ts, err := JWTAccessTokenSourceFromJSON(jsonKey, "audience") if err != nil { t.Fatalf("JWTAccessTokenSourceFromJSON: %v\nJSON: %s", err, string(jsonKey)) } tok, err := ts.Token() if err != nil { t.Fatalf("Token: %v", err) } if got, want := tok.TokenType, "Bearer"; got != want { t.Errorf("TokenType = %q, want %q", got, want) } if got := tok.Expiry; tok.Expiry.Before(time.Now()) { t.Errorf("Expiry = %v, should not be expired", got) } err = jws.Verify(tok.AccessToken, &privateKey.PublicKey) if err != nil { t.Errorf("jws.Verify on AccessToken: %v", err) } claim, err := jws.Decode(tok.AccessToken) if err != nil { t.Fatalf("jws.Decode on AccessToken: %v", err) } if got, want := claim.Iss, "*****@*****.**"; got != want { t.Errorf("Iss = %q, want %q", got, want) } if got, want := claim.Sub, "*****@*****.**"; got != want { t.Errorf("Sub = %q, want %q", got, want) } if got, want := claim.Aud, "audience"; got != want { t.Errorf("Aud = %q, want %q", got, want) } // Finally, check the header private key. parts := strings.Split(tok.AccessToken, ".") hdrJSON, err := base64.RawURLEncoding.DecodeString(parts[0]) if err != nil { t.Fatalf("base64 DecodeString: %v\nString: %q", err, parts[0]) } var hdr jws.Header if err := json.Unmarshal([]byte(hdrJSON), &hdr); err != nil { t.Fatalf("json.Unmarshal: %v (%q)", err) } if got, want := hdr.KeyID, "268f54e43a1af97cfc71731688434f45aca15c8b"; got != want { t.Errorf("Header KeyID = %q, want %q", got, want) } }
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 } if t := js.conf.Expires; t > 0 { claimSet.Exp = time.Now().Add(t).Unix() } 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 }
func (js jwtSource) Token() (*Token, error) { pk, err := internal.ParseKey(js.conf.PrivateKey) if err != nil { return nil, err } hc, err := contextClient(js.ctx) if err != nil { return nil, err } 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) } b := make(map[string]interface{}) if err := json.Unmarshal(body, &b); err != nil { return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) } token := &Token{} token.AccessToken, _ = b["access_token"].(string) token.TokenType, _ = b["token_type"].(string) token.raw = b if e, ok := b["expires_in"].(int); ok { token.Expiry = time.Now().Add(time.Duration(e) * time.Second) } if idtoken, ok := b["id_token"].(string); ok { // decode returned id token to get expiry claimSet, err := jws.Decode(idtoken) if err != nil { return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) } token.Expiry = time.Unix(claimSet.Exp, 0) return token, nil } return token, nil }