DPoP Token Use and Verification
The Demonstrating Proof-of-Possession (DPoP) proposed standard provides a way to constrain a bearer token so it can only be used by a particular client. It prevents any other party from using intercepted tokens by binding a token to a public key for which the corresponding private key is held by the client. When requesting an authorization token the client includes its public key, which the authorization server includes in the returned token. When using the authorization token, the client includes proof of possession of the private key in the request to the resource server, which can then verify the binding of the token to the key pair held by the client.
Approov uses the DPoP mechanism internally between the web SDK and the Approov service to protect their token fetch and refresh interactions. Optionally, the security of interactions between the client and the web API can be improved by also adopting this protocol.
The Approov web SDK provides a function, getDPoPToken to generate and sign a DPoP token that can be included in a request alongside any Approov token, normally in the Dpop header. Inclusion of this and checking it in your backend system provides confidence that the browser instance that requested the Approov token is the one that is making the request. By binding the Approov token to the DPoP token and by including the message hash, you enable your web API to check that the message was generated by the same browser instance that requested the Approov token and that the message was not modified in transit. By also including a nonce and checking its uniqueness in your web API, you can further protect against message replay attacks.
Example:
// Import the Approov web SDK
import { Approov, ApproovError, ApproovFetchError, ApproovServiceError,
ApproovSessionError } from '/approov.js'
// Ensure the Approov session is initialized
await Approov.initializeSession({
approovHost: 'web-1.approovr.io',
approovSiteKey: 'your-Approov-site-key',
/* further web protection service specific arguments */
})
// ...
// Helper function to compute the SHA-256 hash of a string
async function sha256Hash(str) {
const uint8Arr = stringToUint8Array(str)
const hashBuffer = await window.crypto.subtle.digest('SHA-256', uint8Arr);
const result = new Uint8Array(hashBuffer)
return result
}
// Helper function to encode a byte array in base64-url format without padding
function uint8ArrayToBase64URL(uint8Arr) {
// Convert byte array to String (without char interpretation)
const str = String.fromCharCode.apply(null, uint8Arr)
// Convert to base64
const base64 = btoa(str)
// Convert to base64url without padding
const result = base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
return result
}
try {
let nonce = new Uint32Array(8);
window.crypto.subtle.getRandomValues(nonce);
let approovToken = await Approov.fetchToken('your-Approov-protected-web-site',
{ /* web protection service request data */ })
let messageSignature = uint8ArrayToBase64URL(sha256Hash(requestBody))
// Generate a DPoP token
let dpopToken = await Approov.getDPoPToken('POST',
'https://' + 'your-Approov-protected-web-site',
uint8ArrayToBase64URL(nonce),
approovToken,
messageSignature)
// Include both the Approov token and the DPoP token in the request to your
// backend API
const response = await fetch('https://' + 'your-Approov-protected-web-site', {
method: 'POST',
headers: { 'Approov-Token': approovToken, 'Dpop': dpop },
body: requestBody
})
} catch (error) {
// Handle Approov session expiry and any other errors
}
For more details, see the SDK reference for getDPoPToken.
In your backend system, in addition to checking the Approov token's validity, you should also check that the DPoP token is valid and verify that the request message and the Approov token are consistent with the information included in the DPoP token:
- Check the integrity of the DPoP token, i.e. that its signature is valid and it has not expired. As the DPoP token is a JWT (JSON Web Token), checking this is very similar to checking an Approov token's validity and most existing libraries that can be used for JWT verification should also be suitable for verifying a DPoP token.
- Check that the method and path of the request are consistent with the
htmandhtuclaims of the DPoP token. - Check that the
cnfclaim of the Approov token matches the public key thumbprint of the public key contained in the DPoP token'sjwkclaim. - Check that the SHA-256 hash of the Approov token matches the
athclaim of the DPoP token. - If the DPoP token contains a nonce, check that it has not been seen before.
- If the DPoP token contains a
msgsclaim, check that it matches the message hash of the request.