Skip to content

Retries Do not recreate DPop Proof #554

@bbernays

Description

@bbernays

Describe the bug?

When using credentials that require Dpop Proof (like private key + client_id) and the Okta SDK retries an API request after a 429, the SDK does not recreate the DPop header. This causes the API request to fail with a generic 400 error. Upon inspection of the debug logs you can see the error:

Www-Authenticate: DPoP algs="RS256 RS384 RS512 ES256 ES384 ES512", authorization_uri="http://<COMPANY>.okta.com/oauth2/v1/authorize", realm="http://<COMPANY>.okta.com", scope="okta.groups.read", error="invalid_dpop_proof", error_description="The DPoP proof JWT has already been used.", resource="/api/v1/groups/<GROUP_ID>/users"

What is expected to happen?

  1. Retries should recalculate the dpop Proof with a new ID field (and probably IssuedAt field)
    ID: uuid.New().String(),
  2. The information about the error should be populated in the error object and not just an empty body and a generic 400

What is the actual behavior?

  1. if DPop is required retries always fail
  2. Error is just a blank error with no details, users have to go into the response headers to get details

Reproduction Steps?

package main

import (
	"fmt"

	"github.com/okta/okta-sdk-golang/v5/okta"
)

func main() {
	config, err := okta.NewConfiguration(
		okta.WithOrgUrl("https://<COMPANY>.okta.com"),
		okta.WithCache(true),
		okta.WithRateLimitMaxRetries(5),
		okta.WithRateLimitPrevent(true),
		okta.WithAuthorizationMode("PrivateKey"),
		okta.WithClientId("<CLIENT_ID>"),
		okta.WithPrivateKey("-----BEGIN PRIVATE KEY-----<REDACTED>-----END PRIVATE KEY-----"),
		okta.WithScopes([]string{"okta.users.read", "okta.groups.read"}),
	)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	}
	client := okta.NewAPIClient(config)
	for {
		groups, resp, err := client.GroupAPI.ListGroups(client.GetConfig().Context).Limit(200).Execute()
		if err != nil {
			fmt.Printf("Www-Authenticate:%s", resp.Header.Get("Www-Authenticate"))
			fmt.Printf("Error Getting Groups: %v\n", err)
			panic(err)
		}
		for _, group := range groups {
			_, resp, err := client.GroupAPI.ListGroupUsers(client.GetConfig().Context, *group.Id).Limit(200).Execute()
			if err != nil {
				fmt.Printf("Www-Authenticate:%s", resp.Header.Get("Www-Authenticate"))
				fmt.Printf("Error Getting Users for Group %s: %v\n", *group.Id, err)
				panic(err)
			}
		}
	}
}
go run tools/retry_reproduce/main.go
Www-Authenticate:DPoP algs="RS256 RS384 RS512 ES256 ES384 ES512", authorization_uri="http://<COMPANY>.okta.com/oauth2/v1/authorize", realm="http://<COMPANY>.okta.com", scope="okta.groups.read", error="invalid_dpop_proof", error_description="The DPoP proof JWT has already been used.", resource="/api/v1/groups"Error Getting Groups: 400 Bad Request
panic: 400 Bad Request

Additional Information?

No response

Golang Version

go version go1.25.4 darwin/arm64

SDK Version

github.com/okta/okta-sdk-golang/v5 v5.0.6

OS version

No response

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions