50
50
)
51
51
52
52
const (
53
- VERSION = "{ {{packageVersion} }}"
53
+ VERSION = "{ {{packageVersion} }}"
54
54
AccessTokenCacheKey = "OKTA_ACCESS_TOKEN"
55
55
DpopAccessTokenNonce = "DPOP_OKTA_ACCESS_TOKEN_NONCE"
56
56
DpopAccessTokenPrivateKey = "DPOP_OKTA_ACCESS_TOKEN_PRIVATE_KEY"
@@ -59,9 +59,9 @@ const (
59
59
// APIClient manages communication with the { {appName} } API v{ {version} }
60
60
// In most cases there should be only one, shared, APIClient.
61
61
type APIClient struct {
62
- cfg *Configuration
63
- common service // Reuse a single struct instead of allocating one for each service on the heap.
64
- cache Cache
62
+ cfg *Configuration
63
+ common service // Reuse a single struct instead of allocating one for each service on the heap.
64
+ cache Cache
65
65
tokenCache *goCache.Cache
66
66
freshcache bool
67
67
@@ -196,7 +196,7 @@ func (a *PrivateKeyAuth) Authorize(method, URL string) error {
196
196
return err
197
197
}
198
198
199
- accessToken, nonce, privateKey, err := getAccessTokenForPrivateKey(a.httpClient, a.orgURL, clientAssertion, a.userAgent, a.scopes, a.maxRetries, a.maxBackoff)
199
+ accessToken, nonce, privateKey, err := getAccessTokenForPrivateKey(a.httpClient, a.orgURL, clientAssertion, a.userAgent, a.scopes, a.maxRetries, a.maxBackoff, a.clientId, a.privateKeySigner )
200
200
if err != nil {
201
201
return err
202
202
}
@@ -287,7 +287,7 @@ func (a *JWTAuth) Authorize(method, URL string) error {
287
287
}
288
288
}
289
289
} else {
290
- accessToken, nonce, privateKey, err := getAccessTokenForPrivateKey(a.httpClient, a.orgURL, a.clientAssertion, a.userAgent, a.scopes, a.maxRetries, a.maxBackoff)
290
+ accessToken, nonce, privateKey, err := getAccessTokenForPrivateKey(a.httpClient, a.orgURL, a.clientAssertion, a.userAgent, a.scopes, a.maxRetries, a.maxBackoff, " " , nil )
291
291
if err != nil {
292
292
return err
293
293
}
@@ -408,7 +408,7 @@ func (a *JWKAuth) Authorize(method, URL string) error {
408
408
return err
409
409
}
410
410
411
- accessToken, nonce, dpopPrivateKey, err := getAccessTokenForPrivateKey(a.httpClient, a.orgURL, clientAssertion, a.userAgent, a.scopes, a.maxRetries, a.maxBackoff)
411
+ accessToken, nonce, dpopPrivateKey, err := getAccessTokenForPrivateKey(a.httpClient, a.orgURL, clientAssertion, a.userAgent, a.scopes, a.maxRetries, a.maxBackoff, "", nil )
412
412
if err != nil {
413
413
return err
414
414
}
@@ -446,16 +446,16 @@ func convertJWKToPrivateKey(jwks, encryptionType string) (string, error) {
446
446
pair := it.Pair()
447
447
key := pair.Value.(jwk.Key)
448
448
var rawkey interface{} // This is the raw key, like *rsa.PrivateKey or *ecdsa.PrivateKey
449
- err := key.Raw(&rawkey);
449
+ err := key.Raw(&rawkey)
450
450
if err != nil {
451
- return " " ,err
451
+ return " " , err
452
452
}
453
453
454
454
switch encryptionType {
455
455
case " RSA" :
456
456
rsaPrivateKey, ok := rawkey.(*rsa.PrivateKey)
457
457
if ! ok {
458
- return " " ,fmt.Errorf(" expected rsa key, got %T" , rawkey)
458
+ return " " , fmt.Errorf(" expected rsa key, got %T" , rawkey)
459
459
}
460
460
return string(privateKeyToBytes(rsaPrivateKey)), nil
461
461
default:
@@ -514,26 +514,25 @@ func createClientAssertion(orgURL, clientID string, privateKeySinger jose.Signer
514
514
Expiry: jwt.NewNumericDate(time.Now().Add(time.Hour * time.Duration(1))),
515
515
Issuer: clientID,
516
516
Audience: orgURL + " /oauth2/v1/token" ,
517
+ ID: uuid.New().String(),
517
518
}
518
519
jwtBuilder := jwt.Signed(privateKeySinger).Claims(claims)
519
520
return jwtBuilder.CompactSerialize()
520
521
}
521
522
522
- func getAccessTokenForPrivateKey(httpClient *http.Client, orgURL, clientAssertion, userAgent string, scopes []string, maxRetries int32, maxBackoff int64) (*RequestAccessToken, string, *rsa.PrivateKey, error) {
523
- var tokenRequestBuff io.ReadWriter
523
+ func getAccessTokenForPrivateKey(httpClient *http.Client, orgURL, clientAssertion, userAgent string, scopes []string, maxRetries int32, maxBackoff int64, clientID string, signer jose.Signer) (*RequestAccessToken, string, *rsa.PrivateKey, error) {
524
524
query := url.Values{}
525
525
tokenRequestURL := orgURL + "/oauth2/v1/token"
526
526
527
527
query.Add("grant_type", "client_credentials")
528
528
query.Add("scope", strings.Join(scopes, " "))
529
529
query.Add("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
530
530
query.Add("client_assertion", clientAssertion)
531
- tokenRequestURL += "?" + query.Encode()
532
- tokenRequest, err := http.NewRequest("POST", tokenRequestURL, tokenRequestBuff )
531
+
532
+ tokenRequest, err := http.NewRequest("POST", tokenRequestURL, strings.NewReader(query.Encode()) )
533
533
if err != nil {
534
534
return nil, " " , nil, err
535
535
}
536
-
537
536
tokenRequest.Header.Add("Accept", "application/json")
538
537
tokenRequest.Header.Add("Content-Type", "application/x-www-form-urlencoded")
539
538
tokenRequest.Header.Add("User-Agent", userAgent)
@@ -552,14 +551,20 @@ func getAccessTokenForPrivateKey(httpClient *http.Client, orgURL, clientAssertio
552
551
if err != nil {
553
552
return nil, " " , nil, err
554
553
}
554
+
555
555
respBody, err := io.ReadAll(tokenResponse.Body)
556
556
origResp := io.NopCloser(bytes.NewBuffer(respBody))
557
557
tokenResponse.Body = origResp
558
558
var accessToken *RequestAccessToken
559
559
560
+ newClientAssertion, err := createClientAssertion(orgURL, clientID, signer)
561
+ if err != nil {
562
+ return nil, " " , nil, err
563
+ }
564
+
560
565
if tokenResponse.StatusCode >= 300 {
561
566
if strings.Contains(string(respBody), " invalid_dpop_proof" ) {
562
- return getAccessTokenForDpopPrivateKey(tokenRequest, httpClient, orgURL, " " , maxRetries, maxBackoff)
567
+ return getAccessTokenForDpopPrivateKey(tokenRequest, httpClient, orgURL, " " , maxRetries, maxBackoff, newClientAssertion, strings.Join(scopes, " " ), clientID, signer )
563
568
} else {
564
569
return nil, " " , nil, err
565
570
}
@@ -572,7 +577,7 @@ func getAccessTokenForPrivateKey(httpClient *http.Client, orgURL, clientAssertio
572
577
return accessToken, "", nil, nil
573
578
}
574
579
575
- func getAccessTokenForDpopPrivateKey(tokenRequest *http.Request, httpClient *http.Client, orgURL, nonce string, maxRetries int32, maxBackoff int64) (*RequestAccessToken, string, *rsa.PrivateKey, error) {
580
+ func getAccessTokenForDpopPrivateKey(tokenRequest *http.Request, httpClient *http.Client, orgURL, nonce string, maxRetries int32, maxBackoff int64, clientAssertion string, scopes string, clientID string, signer jose.Signer ) (*RequestAccessToken, string, *rsa.PrivateKey, error) {
576
581
privateKey, err := generatePrivateKey(2048)
577
582
if err != nil {
578
583
return nil, " " , nil, err
@@ -581,7 +586,19 @@ func getAccessTokenForDpopPrivateKey(tokenRequest *http.Request, httpClient *htt
581
586
if err != nil {
582
587
return nil, " " , nil, err
583
588
}
589
+ newClientAssertion, err := createClientAssertion(orgURL, clientID, signer)
590
+ if err != nil {
591
+ return nil, " " , nil, err
592
+ }
593
+
594
+ query := url.Values{ }
595
+ query.Add("grant_type", "client_credentials")
596
+ query.Add("scope", scopes)
597
+ query.Add("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
598
+ query.Add("client_assertion", newClientAssertion)
599
+ tokenRequest.Body = io.NopCloser(strings.NewReader(query.Encode()))
584
600
tokenRequest.Header.Set("DPoP", dpopJWT)
601
+
585
602
bOff := &oktaBackoff{
586
603
ctx: context.TODO(),
587
604
maxRetries: maxRetries,
@@ -603,9 +620,9 @@ func getAccessTokenForDpopPrivateKey(tokenRequest *http.Request, httpClient *htt
603
620
}
604
621
605
622
if tokenResponse.StatusCode >= 300 {
606
- if strings.Contains(string(respBody), " use_dpop_nonce" ) {
623
+ if strings.Contains(string(respBody), " use_dpop_nonce" ) {
607
624
newNonce := tokenResponse.Header.Get(" Dpop-Nonce" )
608
- return getAccessTokenForDpopPrivateKey(tokenRequest, httpClient, orgURL, newNonce, maxRetries, maxBackoff)
625
+ return getAccessTokenForDpopPrivateKey(tokenRequest, httpClient, orgURL, newNonce, maxRetries, maxBackoff, clientAssertion, scopes, clientID, signer )
609
626
} else {
610
627
return nil, " " , nil, err
611
628
}
@@ -780,9 +797,9 @@ func (c *APIClient) GetConfig() *Configuration {
780
797
}
781
798
782
799
type formFile struct {
783
- fileBytes []byte
784
- fileName string
785
- formFileName string
800
+ fileBytes []byte
801
+ fileName string
802
+ formFileName string
786
803
}
787
804
788
805
// prepareRequest build the request
@@ -836,11 +853,11 @@ func (c *APIClient) prepareRequest(
836
853
w.Boundary()
837
854
part, err := w.CreateFormFile(formFile.formFileName, filepath.Base(formFile.fileName))
838
855
if err != nil {
839
- return nil, err
856
+ return nil, err
840
857
}
841
858
_, err = part.Write(formFile.fileBytes)
842
859
if err != nil {
843
- return nil, err
860
+ return nil, err
844
861
}
845
862
}
846
863
}
@@ -879,7 +896,7 @@ func (c *APIClient) prepareRequest(
879
896
URL.Scheme = c.cfg.Scheme
880
897
}
881
898
882
- var urlWithoutQuery = *URL
899
+ urlWithoutQuery : = *URL
883
900
884
901
// Adding Query Param
885
902
query := URL.Query()
@@ -1103,7 +1120,7 @@ func (c *APIClient) RefreshNext() *APIClient {
1103
1120
return c
1104
1121
}
1105
1122
1106
- func (c *APIClient) do(ctx context.Context, req *http.Request)(*http.Response, error){
1123
+ func (c *APIClient) do(ctx context.Context, req *http.Request) (*http.Response, error) {
1107
1124
cacheKey := CreateCacheKey(req)
1108
1125
if req.Method != http.MethodGet {
1109
1126
c.cache.Delete(cacheKey)
@@ -1343,9 +1360,9 @@ func (e GenericOpenAPIError) Model() interface{} {
1343
1360
1344
1361
// Okta Backoff
1345
1362
type oktaBackoff struct {
1346
- retryCount, maxRetries int32
1347
- backoffDuration time.Duration
1348
- ctx context.Context
1363
+ retryCount, maxRetries int32
1364
+ backoffDuration time.Duration
1365
+ ctx context.Context
1349
1366
}
1350
1367
1351
1368
// NextBackOff returns the duration to wait before retrying the operation,
@@ -1456,7 +1473,7 @@ func generateDpopJWT(privateKey *rsa.PrivateKey, httpMethod, URL, nonce, accessT
1456
1473
return " " , err
1457
1474
}
1458
1475
key := jose.SigningKey{ Algorithm: jose.RS256, Key: privateKey}
1459
- var signerOpts = jose.SignerOptions{ }
1476
+ signerOpts : = jose.SignerOptions{ }
1460
1477
signerOpts.WithType("dpop+jwt")
1461
1478
signerOpts.WithHeader("jwk", set)
1462
1479
rsaSigner, err := jose.NewSigner(key, &signerOpts)
0 commit comments