...

Source file src/github.com/bradleyfalzon/ghinstallation/v2/appsTransport.go

Documentation: github.com/bradleyfalzon/ghinstallation/v2

     1  package ghinstallation
     2  
     3  import (
     4  	"crypto/rsa"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"strconv"
     9  	"time"
    10  
    11  	jwt "github.com/golang-jwt/jwt/v4"
    12  )
    13  
    14  // AppsTransport provides a http.RoundTripper by wrapping an existing
    15  // http.RoundTripper and provides GitHub Apps authentication as a
    16  // GitHub App.
    17  //
    18  // Client can also be overwritten, and is useful to change to one which
    19  // provides retry logic if you do experience retryable errors.
    20  //
    21  // See https://developer.github.com/apps/building-integrations/setting-up-and-registering-github-apps/about-authentication-options-for-github-apps/
    22  type AppsTransport struct {
    23  	BaseURL string            // BaseURL is the scheme and host for GitHub API, defaults to https://api.github.com
    24  	Client  Client            // Client to use to refresh tokens, defaults to http.Client with provided transport
    25  	tr      http.RoundTripper // tr is the underlying roundtripper being wrapped
    26  	key     *rsa.PrivateKey   // key is the GitHub App's private key
    27  	appID   int64             // appID is the GitHub App's ID
    28  }
    29  
    30  // NewAppsTransportKeyFromFile returns a AppsTransport using a private key from file.
    31  func NewAppsTransportKeyFromFile(tr http.RoundTripper, appID int64, privateKeyFile string) (*AppsTransport, error) {
    32  	privateKey, err := ioutil.ReadFile(privateKeyFile)
    33  	if err != nil {
    34  		return nil, fmt.Errorf("could not read private key: %s", err)
    35  	}
    36  	return NewAppsTransport(tr, appID, privateKey)
    37  }
    38  
    39  // NewAppsTransport returns a AppsTransport using private key. The key is parsed
    40  // and if any errors occur the error is non-nil.
    41  //
    42  // The provided tr http.RoundTripper should be shared between multiple
    43  // installations to ensure reuse of underlying TCP connections.
    44  //
    45  // The returned Transport's RoundTrip method is safe to be used concurrently.
    46  func NewAppsTransport(tr http.RoundTripper, appID int64, privateKey []byte) (*AppsTransport, error) {
    47  	key, err := jwt.ParseRSAPrivateKeyFromPEM(privateKey)
    48  	if err != nil {
    49  		return nil, fmt.Errorf("could not parse private key: %s", err)
    50  	}
    51  	return NewAppsTransportFromPrivateKey(tr, appID, key), nil
    52  }
    53  
    54  // NewAppsTransportFromPrivateKey returns an AppsTransport using a crypto/rsa.(*PrivateKey).
    55  func NewAppsTransportFromPrivateKey(tr http.RoundTripper, appID int64, key *rsa.PrivateKey) *AppsTransport {
    56  	return &AppsTransport{
    57  		BaseURL: apiBaseURL,
    58  		Client:  &http.Client{Transport: tr},
    59  		tr:      tr,
    60  		key:     key,
    61  		appID:   appID,
    62  	}
    63  }
    64  
    65  // RoundTrip implements http.RoundTripper interface.
    66  func (t *AppsTransport) RoundTrip(req *http.Request) (*http.Response, error) {
    67  	// GitHub rejects expiry and issue timestamps that are not an integer,
    68  	// while the jwt-go library serializes to fractional timestamps.
    69  	// Truncate them before passing to jwt-go.
    70  	iss := time.Now().Add(-30 * time.Second).Truncate(time.Second)
    71  	exp := iss.Add(2 * time.Minute)
    72  	claims := &jwt.StandardClaims{
    73  		IssuedAt:  iss.Unix(),
    74  		ExpiresAt: exp.Unix(),
    75  		Issuer:    strconv.FormatInt(t.appID, 10),
    76  	}
    77  	bearer := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
    78  
    79  	ss, err := bearer.SignedString(t.key)
    80  	if err != nil {
    81  		return nil, fmt.Errorf("could not sign jwt: %s", err)
    82  	}
    83  
    84  	req.Header.Set("Authorization", "Bearer "+ss)
    85  	req.Header.Add("Accept", acceptHeader)
    86  
    87  	resp, err := t.tr.RoundTrip(req)
    88  	return resp, err
    89  }
    90  

View as plain text