diff --git a/.github/workflows/sync_upstream.yaml b/.github/workflows/sync_upstream.yaml
new file mode 100644
index 000000000..81130113a
--- /dev/null
+++ b/.github/workflows/sync_upstream.yaml
@@ -0,0 +1,53 @@
+# for reference: https://stackoverflow.com/questions/23793062/can-forks-be-synced-automatically-in-github
+# .github/workflows/example.yml
+
+name: Sync upstream oauth2 repo
+
+on:
+  workflow_dispatch:
+  schedule:
+    - cron:  '0 9 * * 1'
+    # run once a week on monday morning
+    # if it fails
+    #   send slack message (iw-engineering or integration-inovators)
+    
+
+permissions:
+  contents: write
+
+jobs:
+  merge:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - name: Merge upstream
+        run: |
+          git config --global user.name 'upstream-sync-bot'
+          git config --global user.email 'upstream-sync-bot@users.noreply.github.com'
+          git config --global push.followTags true
+
+          git pull 
+
+          git remote add upstream https://github.com/golang/oauth2.git
+          git fetch --tags upstream
+
+          git checkout master
+          git merge --allow-unrelated-histories --no-edit upstream/master
+          git push origin master 
+
+      - name: On Fail, Send Message About Merge to Slack
+        if: ${{ failure() }}
+        uses: archive/github-actions-slack@v2.8.0
+        id: notify-slack
+        env:
+          ACTIONS_STEP_DEBUG: true
+          ACTIONS_RUNNER_DEBUG: true
+        with:
+          slack-bot-user-oauth-access-token: ${{ secrets.SLACK_BOT_TOKEN }}
+          slack-channel: ${{ secrets.SLACK_BOT_INTEGRATION_INNOVATORS_CHANNEL }} #USE CHANNEL ID, NOT CHANNEL NAME, SINCE ID IS USED IN NEW SLACK API's
+          slack-text: |
+            OAuth2 fork upstream sync failed. [action](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
+
+      - name: Send Slack Message Result
+        if: ${{ failure() }}
+        run: echo "${{ steps.notify-slack.outputs.slack-result }}"
diff --git a/clientcredentials/clientcredentials.go b/clientcredentials/clientcredentials.go
index 2459d069f..3bfcf690e 100644
--- a/clientcredentials/clientcredentials.go
+++ b/clientcredentials/clientcredentials.go
@@ -20,6 +20,7 @@ import (
 	"net/url"
 	"strings"
 
+	"github.com/aws/aws-xray-sdk-go/xray"
 	"golang.org/x/oauth2"
 	"golang.org/x/oauth2/internal"
 )
@@ -67,10 +68,31 @@ func (c *Config) Token(ctx context.Context) (*oauth2.Token, error) {
 // is returned. See the oauth2.HTTPClient variable.
 //
 // The returned Client and its Transport should not be modified.
+//
+// Deprecated: Client exists for historical compatibility and should not be
+// used. It is recommended to use ClientWithXRay instead, as it provides much better visibility into the client's behavior.
 func (c *Config) Client(ctx context.Context) *http.Client {
 	return oauth2.NewClient(ctx, c.TokenSource(ctx))
 }
 
+// ClientWithXRay returns an HTTP client using the provided token with an attached XRay client..
+// The token will auto-refresh as necessary.
+//
+// The provided context optionally controls which HTTP client
+// is returned. See the oauth2.HTTPClient variable.
+//
+// The returned Client and its Transport should not be modified.
+func (c *Config) ClientWithXRay(ctx context.Context, hc *http.Client) *http.Client {
+	if hc == nil {
+		hc = http.DefaultClient
+	}
+
+	client := xray.Client(hc)
+	ctx = context.WithValue(ctx, oauth2.HTTPClient, client)
+
+	return oauth2.NewClient(ctx, c.TokenSource(ctx))
+}
+
 // TokenSource returns a TokenSource that returns t until t expires,
 // automatically refreshing it as necessary using the provided context and the
 // client ID and client secret.
diff --git a/go.mod b/go.mod
index d73aa6996..b59e5ce8a 100644
--- a/go.mod
+++ b/go.mod
@@ -4,5 +4,25 @@ go 1.18
 
 require (
 	cloud.google.com/go/compute/metadata v0.3.0
+	github.com/aws/aws-xray-sdk-go v1.8.3
 	github.com/google/go-cmp v0.5.9
+	google.golang.org/appengine v1.6.7
+)
+
+require (
+	cloud.google.com/go/compute v1.23.0 // indirect
+	github.com/andybalholm/brotli v1.0.6 // indirect
+	github.com/aws/aws-sdk-go v1.47.9 // indirect
+	github.com/golang/protobuf v1.5.3 // indirect
+	github.com/jmespath/go-jmespath v0.4.0 // indirect
+	github.com/klauspost/compress v1.17.2 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+	github.com/valyala/bytebufferpool v1.0.0 // indirect
+	github.com/valyala/fasthttp v1.50.0 // indirect
+	golang.org/x/net v0.21.0 // indirect
+	golang.org/x/sys v0.17.0 // indirect
+	golang.org/x/text v0.14.0 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
+	google.golang.org/grpc v1.59.0 // indirect
+	google.golang.org/protobuf v1.31.0 // indirect
 )
diff --git a/go.sum b/go.sum
index 0c9052866..e9f3a338b 100644
--- a/go.sum
+++ b/go.sum
@@ -1,4 +1,64 @@
+cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
+cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
 cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
 cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
+github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM=
+github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
+github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
+github.com/aws/aws-sdk-go v1.47.9 h1:rarTsos0mA16q+huicGx0e560aYRtOucV5z2Mw23JRY=
+github.com/aws/aws-sdk-go v1.47.9/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
+github.com/aws/aws-xray-sdk-go v1.8.3 h1:S8GdgVncBRhzbNnNUgTPwhEqhwt2alES/9rLASyhxjU=
+github.com/aws/aws-xray-sdk-go v1.8.3/go.mod h1:tv8uLMOSCABolrIF8YCcp3ghyswArsan8dfLCA1ZATk=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
+github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
+github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
+github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
+github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
+github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
+golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
+golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA=
+google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
+google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
+google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/internal/token.go b/internal/token.go
index e83ddeef0..04b7db16b 100644
--- a/internal/token.go
+++ b/internal/token.go
@@ -196,7 +196,9 @@ func newTokenRequest(tokenURL, clientID, clientSecret string, v url.Values, auth
 	}
 	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
 	if authStyle == AuthStyleInHeader {
-		req.SetBasicAuth(url.QueryEscape(clientID), url.QueryEscape(clientSecret))
+		// NOTE: This should be query-escaped, however the Aviva OAuth
+		// server does not accept query-escaped credentials.
+		req.SetBasicAuth(clientID, clientSecret)
 	}
 	return req, nil
 }
diff --git a/jwt/jwt.go b/jwt/jwt.go
index b2bf18298..a01f02cce 100644
--- a/jwt/jwt.go
+++ b/jwt/jwt.go
@@ -10,6 +10,7 @@ package jwt
 
 import (
 	"context"
+	"crypto/rsa"
 	"encoding/json"
 	"fmt"
 	"io"
@@ -29,6 +30,8 @@ var (
 	defaultHeader    = &jws.Header{Algorithm: "RS256", Typ: "JWT"}
 )
 
+type Signer = jws.Signer
+
 // Config is the configuration for using JWT to fetch tokens,
 // commonly known as "two-legged OAuth 2.0".
 type Config struct {
@@ -46,6 +49,11 @@ type Config struct {
 	//
 	PrivateKey []byte
 
+	// SignerProvider is a function that is used to create a Signer from the
+	// PrivateKeyID which is then used to sign JWT payloads. This takes
+	// precedence over default signer using the PrivateKey.
+	SignerProvider func(privateKeyID string) (Signer, error)
+
 	// PrivateKeyID contains an optional hint indicating which key is being
 	// used.
 	PrivateKeyID string
@@ -99,10 +107,6 @@ type jwtSource struct {
 }
 
 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,
@@ -124,7 +128,23 @@ func (js jwtSource) Token() (*oauth2.Token, error) {
 	}
 	h := *defaultHeader
 	h.KeyID = js.conf.PrivateKeyID
-	payload, err := jws.Encode(&h, claimSet, pk)
+	var err error
+	payload := ""
+	if js.conf.SignerProvider == nil {
+		var pk *rsa.PrivateKey
+		pk, err = internal.ParseKey(js.conf.PrivateKey)
+		if err != nil {
+			return nil, err
+		}
+		payload, err = jws.Encode(&h, claimSet, pk)
+	} else {
+		var signer jws.Signer
+		signer, err = js.conf.SignerProvider(h.KeyID)
+		if err != nil {
+			return nil, err
+		}
+		payload, err = jws.EncodeWithSigner(&h, claimSet, signer)
+	}
 	if err != nil {
 		return nil, err
 	}
diff --git a/oauth2_test.go b/oauth2_test.go
index 37f0580d7..2a9d09273 100644
--- a/oauth2_test.go
+++ b/oauth2_test.go
@@ -74,7 +74,7 @@ func TestAuthCodeURL_Optional(t *testing.T) {
 
 func TestURLUnsafeClientConfig(t *testing.T) {
 	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		if got, want := r.Header.Get("Authorization"), "Basic Q0xJRU5UX0lEJTNGJTNGOkNMSUVOVF9TRUNSRVQlM0YlM0Y="; got != want {
+		if got, want := r.Header.Get("Authorization"), "Basic Q0xJRU5UX0lEPz86Q0xJRU5UX1NFQ1JFVD8/"; got != want {
 			t.Errorf("Authorization header = %q; want %q", got, want)
 		}