@@ -7,6 +7,7 @@ package cel
77import (
88 "bytes"
99 "context"
10+ "crypto"
1011 "crypto/rsa"
1112 "crypto/x509"
1213 "encoding/base64"
@@ -24,6 +25,8 @@ import (
2425 "github.com/golang-jwt/jwt/v5"
2526 "golang.org/x/oauth2"
2627 "golang.org/x/oauth2/clientcredentials"
28+
29+ "github.com/elastic/beats/v7/x-pack/filebeat/input/internal/dpop"
2730)
2831
2932// oktaTokenSource is a custom implementation of the oauth2.TokenSource interface.
@@ -37,7 +40,7 @@ type oktaTokenSource struct {
3740}
3841
3942// fetchOktaOauthClient fetches an OAuth2 client using the Okta JWK credentials.
40- func (o * oAuth2Config ) fetchOktaOauthClient (ctx context.Context , _ * http. Client ) (* http.Client , error ) {
43+ func (o * oAuth2Config ) fetchOktaOauthClient (ctx context.Context ) (* http.Client , error ) {
4144 conf := & oauth2.Config {
4245 ClientID : o .ClientID ,
4346 Scopes : o .Scopes ,
@@ -46,11 +49,37 @@ func (o *oAuth2Config) fetchOktaOauthClient(ctx context.Context, _ *http.Client)
4649 },
4750 }
4851
52+ oauthCtx := ctx
53+ var (
54+ claim dpop.ClaimerFunc
55+ key crypto.Signer
56+ method jwt.SigningMethod
57+ )
58+ if o .DPoPKeyPEM != "" {
59+ claim = func () * jwt.RegisteredClaims {
60+ now := time .Now ()
61+ return & jwt.RegisteredClaims {
62+ Audience : []string {conf .Endpoint .TokenURL },
63+ Issuer : conf .ClientID ,
64+ Subject : conf .ClientID ,
65+ IssuedAt : jwt .NewNumericDate (now ),
66+ ExpiresAt : jwt .NewNumericDate (now .Add (time .Hour )),
67+ }
68+ }
69+ var err error
70+ key , err = pemPKCS8PrivateKey ([]byte (o .DPoPKeyPEM ))
71+ if err != nil {
72+ return nil , fmt .Errorf ("failed to decode dpop signer: %w" , err )
73+ }
74+ cli , err := dpop .NewTokenClient (claim , key , jwt .SigningMethodRS256 , nil )
75+ oauthCtx = context .WithValue (oauthCtx , oauth2 .HTTPClient , cli )
76+ }
77+
4978 var (
5079 oktaJWT string
5180 err error
5281 )
53- if len ( o .OktaJWKPEM ) != 0 {
82+ if o .OktaJWKPEM != "" {
5483 oktaJWT , err = generateOktaJWTPEM (o .OktaJWKPEM , conf )
5584 if err != nil {
5685 return nil , fmt .Errorf ("oauth2 client: error generating Okta JWT PEM: %w" , err )
@@ -62,7 +91,7 @@ func (o *oAuth2Config) fetchOktaOauthClient(ctx context.Context, _ *http.Client)
6291 }
6392 }
6493
65- token , err := exchangeForBearerToken (ctx , oktaJWT , conf )
94+ token , err := exchangeForBearerToken (oauthCtx , oktaJWT , conf )
6695 if err != nil {
6796 return nil , fmt .Errorf ("oauth2 client: error exchanging Okta JWT for bearer token: %w" , err )
6897 }
@@ -76,7 +105,9 @@ func (o *oAuth2Config) fetchOktaOauthClient(ctx context.Context, _ *http.Client)
76105 // reuse the tokenSource to refresh the token (automatically calls
77106 // the custom Token() method when token is no longer valid).
78107 client := oauth2 .NewClient (ctx , oauth2 .ReuseTokenSource (token , tokenSource ))
79-
108+ if claim != nil {
109+ return dpop .NewResourceClient (claim , key , method , tokenSource , client )
110+ }
80111 return client , nil
81112}
82113
@@ -167,20 +198,28 @@ func generateOktaJWTPEM(pemdata string, cnf *oauth2.Config) (string, error) {
167198 return signJWT (cnf , key )
168199}
169200
170- func pemPKCS8PrivateKey (pemdata []byte ) (any , error ) {
201+ func pemPKCS8PrivateKey (pemdata []byte ) (crypto. Signer , error ) {
171202 blk , rest := pem .Decode (pemdata )
172203 if rest := bytes .TrimSpace (rest ); len (rest ) != 0 {
173204 return nil , fmt .Errorf ("PEM text has trailing data: %d bytes" , len (rest ))
174205 }
175206 if blk == nil {
176207 return nil , errors .New ("no PEM data" )
177208 }
178- return x509 .ParsePKCS8PrivateKey (blk .Bytes )
209+ key , err := x509 .ParsePKCS8PrivateKey (blk .Bytes )
210+ if err != nil {
211+ return nil , err
212+ }
213+ signer , ok := key .(crypto.Signer )
214+ if ! ok {
215+ return nil , fmt .Errorf ("key is not a signer: %T" , key )
216+ }
217+ return signer , nil
179218}
180219
181220// signJWT creates a JWT token using required claims and sign it with the
182221// private key.
183- func signJWT (cnf * oauth2.Config , key any ) (string , error ) {
222+ func signJWT (cnf * oauth2.Config , key crypto. Signer ) (string , error ) {
184223 now := time .Now ()
185224 signed , err := jwt .NewWithClaims (jwt .SigningMethodRS256 , jwt.RegisteredClaims {
186225 Audience : []string {cnf .Endpoint .TokenURL },
0 commit comments