# Not able to get data using access token generate by client credentials using DPoP

**URL:** <https://devforum.okta.com/t/not-able-to-get-data-using-access-token-generate-by-client-credentials-using-dpop/30738>\
**Category:** API\
**Created:** [September 27, 2024, 12:31pm UTC](https://devforum.okta.com/t/not-able-to-get-data-using-access-token-generate-by-client-credentials-using-dpop/30738 "2024-09-27T12:31:27Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![vikalp-khandelwal](https://avatars.discourse-cdn.com/v4/letter/v/2acd7d/32.png) [@vikalp-khandelwal](https://devforum.okta.com/u/vikalp-khandelwal)\
**Post date:** [September 27, 2024, 12:31pm UTC](https://devforum.okta.com/t/not-able-to-get-data-using-access-token-generate-by-client-credentials-using-dpop/30738/1 "2024-09-27T12:31:27Z")

</div>

I am using a service Application in Okta. I have used public-key / private key as client authentication and enabled DPop (Require Demonstrating Proof of Possession (DPoP) header in token requests).

I have generated an RSA key converted it into JWK and uploaded it to my service Application in Okta.

I have generated a DPoP proof and client assertion. Then I hit access token API But got an error  
From the error header, I got the Dpop nonce, Again I generated DPoP proof and Hit access token API. Now I have the access token.

Now I want to hit any API like Get groups But I got this error: Request failed with status code 400  
I have decoded the access token it has all the necessary information.

Can someone tell me How to get an access token in an DPoP enabled service Application by which I can hit any API and get the data?

I am providing the code:

const crypto = require(‘crypto’);  
const sshpk = require(‘sshpk’);  
const jwt = require(‘jsonwebtoken’);  
const axios = require(‘axios’);  
const { v4: uuidv4 } = require(‘uuid’);  
const fs = require(‘fs’);  
const forge = require(‘node-forge’);  
const username = ‘vikalp.khandelwal@robomq.io’  
const tokenURL = ‘[https://dev-97298997.okta.com/oauth2/v1/token](https://dev-97298997.okta.com/oauth2/v1/token)’  
const groupsURL = ‘[https://dev-97298997.okta.com/api/v1/groups](https://dev-97298997.okta.com/api/v1/groups)’

// Step 1: Generate RSA key pair  
const generateRsaKey = async (username) =\> {  
const { privateKey, publicKey } = crypto.generateKeyPairSync(‘rsa’, {  
modulusLength: 2048,  
publicKeyEncoding: {  
type: ‘pkcs1’,  
format: ‘pem’,  
},  
privateKeyEncoding: {  
type: ‘pkcs1’,  
format: ‘pem’,  
},  
});

```
// SSH Key Manipulation
const publickey = sshpk.parseKey(publicKey, 'pem');
const pubKey = publickey.toString('ssh');
let usernameRSA = username.replace(/[&\/\\#,+()$~%.'":*?<>{}]/g, '_');
usernameRSA = usernameRSA.substr(0, usernameRSA.lastIndexOf('_')); 
usernameRSA = checkUsername(usernameRSA);
let sshRsa = pubKey.replace('(unnamed)', usernameRSA);
let privatekey = privateKey.replace(/\n/g, " ");

const keyPairs = {
    user_publickey: sshRsa,
    user_privatekey: privatekey,
    user_certificate: generateCertificate(sshRsa, privateKey, username),
};

fs.writeFileSync('pubkey.txt', keyPairs.user_publickey, function (err) {
    if (err) throw err;
});
fs.writeFileSync('pvtKey.txt', keyPairs.user_privatekey, function (err) {
    if (err) throw err;
});
fs.writeFileSync('certificate.txt', keyPairs.user_certificate, function (err) {
    if (err) throw err;
});

```

}

function checkUsername(usernameRSA) {  
if (usernameRSA.length \> 32) {  
const reglastintial = (/[A-Za-z0-9\_]+\_[A-Za-z0-9]/)  
const domainname = usernameRSA.substr(usernameRSA.lastIndexOf(‘@’), usernameRSA.length - 1)  
usernameRSA = usernameRSA.substr(0, usernameRSA.lastIndexOf(‘@’))  
let intialusername = usernameRSA

```
  usernameRSA = ((reglastintial).test(usernameRSA) ? usernameRSA.match(reglastintial)[0] : usernameRSA)

  let last_intital = null
  if ((usernameRSA + domainname).length > 32) {
    if ((/_[A-Za-z0-9]+$/).test(intialusername)) {
      last_intital = intialusername.match(/_[A-Za-z0-9]+$/)[0]
    }
    usernameRSA = ((/[A-Za-z0-9]/).test(intialusername.charAt(0)) ? intialusername.match(/[A-Za-z0-9]/)[0] : intialusername.match(/_[A-Za-z0-9]/)[0])
    if (last_intital != null) {
      usernameRSA += last_intital
    }
  }

  if ((usernameRSA + domainname).length > 32) {
    usernameRSA = ((reglastintial).test(usernameRSA) ? usernameRSA.match(reglastintial)[0] : usernameRSA)
  }
  usernameRSA = usernameRSA + domainname
  usernameRSA = usernameRSA.replace(/[&\/\\#,+()$~%.'":*?<>{}@]/g, '_')
}
else {
  usernameRSA = usernameRSA.replace(/[&\/\\#,+()$~%.'":*?<>{}@]/g, '_')
}
return usernameRSA

```

}

const getAsymmetricPrivateKey = async (privateKey) =\> {  
try {  
const regex = /-----BEGIN RSA PRIVATE KEY-----(.\*?)-----END RSA PRIVATE KEY-----/s;  
let privatePEMKey = privateKey.replace(regex, (\_, content) =\> {  
const replacedContent = content.split(’ ‘).join(’\n’);  
return `-----BEGIN RSA PRIVATE KEY-----${replacedContent}-----END RSA PRIVATE KEY-----`;  
});  
return crypto.createPrivateKey({ key: privatePEMKey, format: ‘pem’ });  
} catch (error) {  
throw error;  
}  
};

const generateCertificate = (publicKey, privateKey, username) =\> {  
const key = sshpk.parseKey(publicKey, ‘ssh’);  
publicKey = key.toString(‘pem’);

```
let keys = {
    publicKey: forge.pki.publicKeyFromPem(publicKey),
    privateKey: forge.pki.privateKeyFromPem(privateKey),
};
  
let cert = forge.pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = '01';
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 20);

let attrs = [{
    name: 'commonName',
    value: username,
}];

cert.setSubject(attrs);
cert.setIssuer(attrs);
cert.sign(keys.privateKey);
return forge.pki.certificateToPem(cert);

```

};

const getFingerprint = async (cert) =\> {  
try {  
const certificate = forge.pki.certificateFromPem(cert);  
const md = forge.md.sha1.create();  
md.update(forge.asn1.toDer(forge.pki.certificateToAsn1(certificate)).getBytes());  
const thumbprint = md.digest().toHex();  
const binaryFingerprint = Buffer.from(thumbprint, ‘hex’);  
return binaryFingerprint.toString(‘base64’);  
} catch (error) {  
console.error(error);  
throw error;  
}  
};

// Step 3: Convert Public Key to JWK format  
const convertJWK = async (pubKey, certFingerprint) =\> {  
const sshKey = sshpk.parseKey(pubKey, ‘ssh’);  
if (sshKey.parts.length \>= 2) {  
const exponent = sshKey.parts[0].data.toString(‘base64’);  
const modulus = sshKey.parts[1].data.toString(‘base64’);  
const jwk = {  
kty: ‘RSA’,  
e: exponent,  
n: modulus,  
alg: ‘RS256’,  
use: ‘sig’,  
kid: certFingerprint  
};

```
    console.log(JSON.stringify(jwk, null, 2));
    fs.writeFileSync('jwk.txt', JSON.stringify(jwk, null, 2), function (err) {
        if (err) throw err;
    });
}

```

};

// Step 4 & 7: Regenerate DPoP proof with nonce and retry  
const generateDpop = async (jwk, pvtKey, url, method, nonce) =\> {  
const header = {  
alg: ‘RS256’,  
typ: ‘dpop+jwt’,  
jwk: JSON.parse(jwk) // Ensure this is parsed correctly  
};  
const proofPayload = {  
jti: uuidv4(),  
htu: url, // URI of the resource (token endpoint or API endpoint)  
htm: method, // HTTP method  
iat: Math.floor(Date.now() / 1000),  
exp: Math.floor(Date.now() / 1000) + 300, // Expire in 5 minutes  
};

```
// Include nonce if present
if (nonce) proofPayload.nonce = nonce;
else proofPayload.nonce = uuidv4()

const dpopProof = jwt.sign(proofPayload, pvtKey, {
    algorithm: 'RS256',
    header: header,
});
// Log for debugging
console.log('Generated DPoP Proof:', dpopProof)
return dpopProof;

```

};

// Step 5: Get Client assertion  
const generateClientAssertion = async (pKey, certFingerprint) =\> {  
const payload = {  
iss: ‘0oajycc0vu4uIxLvT5d7’,  
sub: ‘0oajycc0vu4uIxLvT5d7’,  
aud: tokenURL,  
iat: Math.floor(Date.now() / 1000),  
exp: Math.floor(Date.now() / 1000) + (60 \* 60),  
};

```
// JWT Header
const header = {
    alg: 'RS256',
    typ: 'JWT',
    kid: certFingerprint
}

// Signing client assertion JWT
return jwt.sign(payload, pKey, { algorithm: 'RS256', header })

```

}

// Step 6 & 8: Get Access Token  
const getAccessToken = async (clientAssertion, dpopProof) =\> {  
try {  
const response = await axios({  
method: ‘POST’,  
url: tokenURL,  
headers: {  
‘Accept’: ‘application/json’,  
‘Content-Type’: ‘application/x-www-form-urlencoded’,  
‘DPoP’: dpopProof // Include DPoP proof in headers  
},  
data: new URLSearchParams({  
‘grant\_type’: ‘client\_credentials’,  
‘client\_id’: ‘0oajycc0vu4uIxLvT5d7’,  
‘client\_assertion\_type’: ‘urn:ietf:params:oauth:client-assertion-type:jwt-bearer’,  
‘client\_assertion’: clientAssertion,  
‘scope’: ‘okta.users.manage okta.groups.manage okta.schemas.read okta.groups.read’ ,  
}).toString(),  
});  
console.log(‘access\_token\>\>’, response.data.access\_token)  
return response.data.access\_token;  
} catch (error) {  
if (error.response && error.response.headers[‘dpop-nonce’]) {  
return error.response.headers[‘dpop-nonce’]; // Return nonce for retry  
} else {  
throw new Error(`Error fetching access token: ${error.message}`);  
}  
}  
};

// Step 9: Convert base64 hash  
const convertHash = (access\_token) =\> {  
const boundAccessToken = access\_token  
const hash = crypto.createHash(‘sha256’).update(boundAccessToken).digest(‘base64’);  
return hash;  
}

// Step 10: Use DPoP-bound token to access groups  
const generateModifiedDpop = async (jwk, pvtKey, url, method, hash) =\> {  
var claims = {  
htm: method,  
htu: url,  
ath: hash  
}  
dpopJWKForResource = jwt.sign(claims, pvtKey,  
{  
algorithm: ‘RS256’,  
header:  
{  
typ: ‘dpop+jwt’,  
jwk: jwk  
}  
}  
);  
console.log(‘dpopJWKForResource\>\>\>’, dpopJWKForResource);  
return dpopJWKForResource;  
}

// Step 11: Get Groups  
const getGroups = async (dpopProof, accessToken) =\> {  
try {  
const response = await axios.get(groupsURL, {  
headers: {  
Authorization: `DPoP ${accessToken}`,  
DPoP: dpopProof  
}  
});  
console.log(response.data);  
} catch (error) {  
console.error(‘Error fetching groups:’, error.message);  
}  
};

// Final Function to execute the flow  
const executeFlow = async (username) =\> {  
try {  
// Step 1: Generate RSA key pair  
await generateRsaKey(username);

```
    // Step 2: Load keys from files
    const pubKey = fs.readFileSync('pubkey.txt', 'utf8');
    const pvtKey = fs.readFileSync('pvtKey.txt', 'utf8');
    const pKey = await getAsymmetricPrivateKey(pvtKey);

    const certificate = fs.readFileSync('certificate.txt', 'utf8'); // Assume you already generated this certificate
    const certFingerprint = await getFingerprint(certificate);

    // Step 3: Convert public key to JWK and calculate certificate fingerprint
    await convertJWK(pubKey, certFingerprint);
    const jwk = fs.readFileSync('jwk.txt', 'utf8')

    // Step 4: Generate initial DPoP proof
    let dpopProof = await generateDpop(jwk, pKey, tokenURL, 'POST');
    let access_token

    // Step 5: Get Client assertion
    const clientAssertion = await generateClientAssertion(pKey, certFingerprint)

    // Step 6: Get nonce using access Token API
    let nonce = await getAccessToken(clientAssertion, dpopProof) // Handle nonce case
    if (nonce) {
        // Step 7: Regenerate DPoP proof with nonce and retry
        dpopProof = await generateDpop(jwk, pKey, tokenURL, 'POST', nonce);
        // Step 8: Get access Token
        access_token = await getAccessToken(clientAssertion, dpopProof);
        // Step 9: Convert base64 hash
        const hash = convertHash(access_token)
        // Step 10: Use DPoP-bound token to access groups
        const dpopJWKForResource = await generateModifiedDpop(jwk, pKey, groupsURL, 'GET', hash);
        // Step 11: Get Groups
        await getGroups(dpopJWKForResource, access_token);
    }
} catch (error) {
    console.error('Error executing flow:', error.message);
}

```

};

// Trigger the flow  
executeFlow(username); Not able to get data using access token generate by client credentials using DPoP

---

<div class="post-metadata">

**Author:** ![system](https://global.discourse-cdn.com/oktadev/original/1X/589c4c5f80de422d01f47cee7185a7fb8227835c.png) [@system](https://devforum.okta.com/u/system)\
**Post date:** [September 27, 2024, 12:31pm UTC](https://devforum.okta.com/t/not-able-to-get-data-using-access-token-generate-by-client-credentials-using-dpop/30738/2 "2024-09-27T12:31:28Z")

</div>



---

<div class="post-metadata">

**Author:** ![system](https://global.discourse-cdn.com/oktadev/original/1X/589c4c5f80de422d01f47cee7185a7fb8227835c.png) [@system](https://devforum.okta.com/u/system)\
**Post date:** [September 30, 2024, 3:12pm UTC](https://devforum.okta.com/t/not-able-to-get-data-using-access-token-generate-by-client-credentials-using-dpop/30738/3 "2024-09-30T15:12:13Z")

</div>



---

<div class="post-metadata">

**Author:** ![system](https://global.discourse-cdn.com/oktadev/original/1X/589c4c5f80de422d01f47cee7185a7fb8227835c.png) [@system](https://devforum.okta.com/u/system)\
**Post date:** [October 30, 2024, 3:12pm UTC](https://devforum.okta.com/t/not-able-to-get-data-using-access-token-generate-by-client-credentials-using-dpop/30738/4 "2024-10-30T15:12:16Z")

</div>

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
