
User17247358700509788794 (Customer) asked a question.
Followed steps in the docs but it's a mix of https://developer.okta.com/docs/guides/implement-oauth-for-okta-serviceapp/main/ and https://developer.okta.com/docs/guides/dpop/nonoktaresourceserver/main/. I get the access_token but calling Okta API /api/v1/users returns HTTP 400.
- Create App Integration > select API Services
- Edit Client Credentials > select Public key / Private key > Add key > copy private JWK key > Save
- In General Settings by default Require Demonstrating Proof of Possession(DPoP) header in token requests is True
- Okta API Scopes Tab > Grant okta.users.read
- Admin Roles Tab > Assign Read-only Administrator > Save changes
- Convert private JWK key to public PEM and private PEM (I use https://8gwifi.org/jwkconvertfunctions.jsp)
- Create and sign the client_credentials JWT using jwt.io
Payload:
{
"aud": "https://dev-<account#>.okta.com/oauth2/v1/token",
"iss": "<app_client_id>",
"sub": "<app_client_id>",
"exp": "<epoch in future>"
}
- Create and sign the DPoP JWT using jwt.io
Header:
{
"typ": "dpop+jwt",
"alg": "RS256",
"jwk": {
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "<private key kid>",
"alg": "RS256",
"n": "<private key n>"
}
}
Payload:
{
"htm": "POST",
"htu": "https://dev-<account#>.okta.com/oauth2/v1/token",
"iat": <epoch now>
}
- Call POST on https://dev-<account#>.okta.com/oauth2/v1/token
curl --location 'https://dev-<account#>.okta.com/oauth2/v1/token' \
--header 'DPoP: <DPoP JWT>' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'scope=okta.users.read' \
--data-urlencode 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
--data-urlencode 'client_assertion=<client_credentials JWT>'
- Receive HTTP 400 and use response header 'dpop-nonce' to create new DPoP JWT
{
"error": "use_dpop_nonce",
"error_description": "Authorization server requires nonce in DPoP proof."
}
- Create and sign the DPoP with nonce JWT using jwt.io by using the same header but adding nonce and jti in payload
{
"htm": "POST",
"htu": "https://dev-<account#>.okta.com/oauth2/v1/token",
"iat": <epoch now>,
"nonce": "<response header dpop-nonce from HTTP 400>",
"jti": "<any random character>"
}
- Call the same POST on https://dev-<account#>.okta.com/oauth2/v1/token but change DPoP header with the new DPoP with nonce JWT
- Receive a HTTP 200
{
"token_type": "DPoP",
"expires_in": 3600,
"access_token": "<access_token with DPoP>",
"scope": "okta.users.read"
}
- Call GET on https://dev-<account#>.okta.com/oauth2/v1/users
curl --location 'https://dev-<account#>.okta.com/api/v1/users' \
--header 'DPoP: <DPoP JWT>' \
--header 'Authorization: DPoP <access_token with DPoP>'
- HTTP 400 Bad Request with a blank body
How do I fix this and which step is it failing?

Hey Paul. Thank you for your response. I posted at the same time at devforum.okta.com and I just received a solution. I'll add the link here for others to find https://devforum.okta.com/t/client-credentials-oauth-for-okta-with-dpop-enabled/30239/1. Cheers.