Need help diving deeper into invalid_client error

Hello,

I’m trying to setup client credentials flow using private key jwt authentication. Here’s the request id of the failed request 5b03b637226edad5aa89. I’ll try to provide more context on my setup and the request properties.

I followed this guide to configure private jwt auth for my application.

  • created private rsa key in pem format
  • derived public key from private key
  • switched app auth mode to private key jwt and uploaded public key and retrieved keyid

Then I followed this guide to build the assertion. This is where I’m wondering if my configuration failed. I’ll post the code below for reference.

Code to sign assertion (includes private key info for full visibility). Runnable link Go Playground - The Go Programming Language

package main

import (
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"log"
	"time"

	"github.com/golang-jwt/jwt/v5"
	"github.com/google/uuid"
)

func main() {
	claims := jwt.MapClaims{
		"aud": "https://dev-hjb4npw6t0kq7cve.us.auth0.com/api/v2/",
		"iss": "RwXtHvCrgUgMsu55RZGHHAVh4q3EV9Tj",
		"sub": "RwXtHvCrgUgMsu55RZGHHAVh4q3EV9Tj",
		"jti": uuid.New().String(),
		"exp": time.Now().Unix() + 3600,
	}

	token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)

	pk := `-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDt72mj++q+8Qgl
cI7A9x5Ig/A3oDJADlKed/4s/45KgY+OiRu5rBHBsVha4UL7sDNkIxRA9RomrB+4
62gzSJoVvrxv1pjzCOnf/v1xpYeWDMrZDMtPmz3ARP8nsrJyuhnrUCMlqGqIDZsw
DTrWBKHegZ/bwXE6PDtiCGo25Ifv26J5nH2rKoRGG2n23mIU7NOw/T1upYh6BAdf
PP8AIk1EDa68rs6/D54sH2p+RoYT5nXmoOYSEDqS9ZVwhtH1ZgH0JbdPUvGa5Msx
mAF+wrtpoNc0uslkmqcaXFg7SaaECr/cTI4VkAbnLlDurKh2lutsWUVeuG8hs+1U
qbwkRjOhAgMBAAECggEAWKHvi0uDVZ959mBofQ9Q85haeo4tSdlMA4YVY/PKsIx0
ypbeghrL/ogurqPtDM7FRg8fM1daZ3+EzaC7jycDZwhHL64UCayPWoalKJmN2WJo
idQPRFdC+IV7OvGBMeWLVDHk9dk33fibj5nOCf1twvIsd3Mm15eqU0lpL4uP69QA
W2F1QM438tbkUOHA4P2rC9UMg+LPpixpsbzjJtUQYKII81NqgDhBMqS6HyPu3UNR
nhlxqamUygj36DiueJg1uMOfkDNYmgTOfEHAbrmtumbbYcv2iwKOY2ftuL3L8rGe
3u1JuwBpP0UEf9Ip9oRz4F0TEppBHqULtMGVn/MycQKBgQD6xn6qe+cDgwo/2B2q
TOEQHGqisc5tPuRk2x0KSxKHgCp6pDWVfOTQ48sM9uKoIflw8dyvGtyzMxFdf3bZ
EzVnNcuN4wRDsJLnIAnCTm3ySdmu3Oc9aIA9OAoYnSkC4PTXnSYG0IuA3Z351gVr
snvGkzK4bODbvMR31B1eX/441QKBgQDy5G9j/GvrX+6Zr3ONZhlVL8yUP1PK2AHY
SbCFIFq0b0UYwRaKKdd8CVGUFwz6Zw4SuwoI0I6Yy9QiHir86Ekjjrhd1cSmv9iv
mEpiH7WZoZdXu+HYsRVwYBxRCeudGVfZXxmvtj8Qxfo2I8sM0N+25EboDWV9F1Xt
NwFjcdR1nQKBgQDkmN7MT+f1NpW4PyxDmpMq2YyHRGg1A7i1C1v8jxST+kw+wcOq
wk/e71egOMnClD3LLF6MLIN0wx+YSGIxXSY4MDye2YuAnMw87eKXgmENFS7fDLWq
L8F+jL//UfwZiLEIf1MzsRUJ4ntoTCVi2C+mg3Yf0fhjtj6uMOiYqA6ZBQKBgHOi
/8kpZrOzg2PVnPzHi+37iLlXgOwEdLRlOM3YfnaCtvG3E6UJkJZz1UvcwYgBQjj2
pStXJ1xlRgRMOokXiaKNEwdRe4MruNy4c0lN8WZIAUZ6o2ZHwH40s6Z9Q5gSdbl+
RjtaV6IDr0DRP51FYAyxl3Ro1FV+o6EA2/9dNRodAoGAQ24DIUfP//BM/EV25FMf
9PJ3dYi7pKXv7asbgDmWWPw4rTsvI43sDP87P5RPeWTrhETy36kvWRhKXFbXdVtR
2BSWROEhA+pq+w8euueJTsVV1cW+qL2akpDfKQktaIDHxpVNNkSQPinS5ixunrEe
I/zPwQuLxPbYD32CKXTGzeA=
-----END PRIVATE KEY-----`

	block, _ := pem.Decode([]byte(pk))
	privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		log.Fatal("Error decoding private key", err)
	}

	tokenString, err := token.SignedString(privateKey)
	if err != nil {
		log.Fatal("Error signing token")
	}

	fmt.Println(tokenString)
}

JWT assertion

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwczovL2Rldi1oamI0bnB3NnQwa3E3Y3ZlLnVzLmF1dGgwLmNvbS9hcGkvdjIvIiwiZXhwIjoxNjk1MDEyNjgxLCJpc3MiOiJSd1h0SHZDcmdVZ01zdTU1UlpHSEhBVmg0cTNFVjlUaiIsImp0aSI6IjdlZjA1OTU3LWEwN2MtNDlhMC1hZmJhLTIwYjNlMGUwMzcwYyIsInN1YiI6IlJ3WHRIdkNyZ1VnTXN1NTVSWkdISEFWaDRxM0VWOVRqIn0.FHw_MtqvDvex5_ZkCWfqtOw_VXVbDofLoPgnfxNOmj8NP2N5oYm-8k_nsax_d-K8vAGAt1SA7dC2Zn3xxFpChzwLCRBcRw9Uxs5S2qT9RfVBJtcIdChBrBH8eXvfgAXsegzlBuCsCyfTkehchHBOwcB6rfZu5z2ewXfwpP31rsNV17a0G3962BTvxuo9bxgHO8P7gQDFwErW1-g1j2V72MIS_ra74-HbEQSasLBszdjC7TlGOl2CiuNgHV_-SmLqd09FNYv4pXaJLkdrAR8An4Rz5EYJY7qXKW5ezek0jQe3-RzEaivBt_Zs1sx9-bEffmqTcCmrLp3RFaGuHpORPw

Once the assertion is built, when I try to request an access token via curl I get "error":"invalid_client","error_description":"Unauthorized"}.

curl -v --location --request POST 'https://dev-hjb4npw6t0kq7cve.us.auth0.com/oauth/token' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
  --data-urlencode 'client_assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwczovL2Rldi1oamI0bnB3NnQwa3E3Y3ZlLnVzLmF1dGgwLmNvbS9hcGkvdjIvIiwiZXhwIjoxNjk1MDEyNjgxLCJpc3MiOiJSd1h0SHZDcmdVZ01zdTU1UlpHSEhBVmg0cTNFVjlUaiIsImp0aSI6IjdlZjA1OTU3LWEwN2MtNDlhMC1hZmJhLTIwYjNlMGUwMzcwYyIsInN1YiI6IlJ3WHRIdkNyZ1VnTXN1NTVSWkdISEFWaDRxM0VWOVRqIn0.FHw_MtqvDvex5_ZkCWfqtOw_VXVbDofLoPgnfxNOmj8NP2N5oYm-8k_nsax_d-K8vAGAt1SA7dC2Zn3xxFpChzwLCRBcRw9Uxs5S2qT9RfVBJtcIdChBrBH8eXvfgAXsegzlBuCsCyfTkehchHBOwcB6rfZu5z2ewXfwpP31rsNV17a0G3962BTvxuo9bxgHO8P7gQDFwErW1-g1j2V72MIS_ra74-HbEQSasLBszdjC7TlGOl2CiuNgHV_-SmLqd09FNYv4pXaJLkdrAR8An4Rz5EYJY7qXKW5ezek0jQe3-RzEaivBt_Zs1sx9-bEffmqTcCmrLp3RFaGuHpORPw' \
  --data-urlencode 'audience=https://dev-hjb4npw6t0kq7cve.us.auth0.com/api/v2/'

The auth0 requestid in the response for diving deeper. 5b03b637226edad5aa89

Open your Auth0 dashboard, and go to monitoring → logs.
There you should see the failed exchange attempt, and in the details you can see, what went wrong.

I guess in your case the exp is too large, Auth0 has the limitation of 5 minutes.

In the CURL request you want to have a token valid for the Auth0 management API (audience set to the management api identifier)?