Loading...
Loading...
Implement and debug OAuth 2.0 DPoP (RFC 9449) refresh token sender-constraining for WebCrypto, Node.js ES6, and browser runtimes integrating with Google's OAuth platform. Use when configuring non-extractable asymmetric key pairs (P-256), generating DPoP Proof JWTs for authorization code exchange and token refresh, or handling 400 use_dpop_nonce challenge retry loops at oauth2.googleapis.com/token. Don't use for unconstrained OAuth 2.0 flows (where refresh tokens are not bound to a client key pair), or for Google Cloud IAM / service account authentication.
npx skill4agent add google/skills dpop-adoptiontoken_type: "Bearer""type": "module"globalThis.cryptorequire('node:crypto')window.cryptoP-256{ name: 'ECDSA', namedCurve: 'P-256' }extractable: falseextractable: true"kty": "EC""crv": "P-256""x"="y"="d"crypto.subtle.signrscrypto.subtle.signInvalid DER sequencejava.security.Signaturecrypto.createSignclient_secretDPoP-Nonceaccess_type=offlinecreateDPoPProofcreateDPoPProoftypalgjwk// Header
{
"typ": "dpop+jwt",
"alg": "ES256",
"jwk": await exportPublicJWK(publicKey)
}"htm""POST""htu"sanitizeHTU(htu)https://oauth2.googleapis.com/token"iat"Math.floor(Date.now() / 1000)"jti"jticreateDPoPProofauthCodejti = await calculateAuthCodeJti(authCode)calculateAuthCodeJtibase64url(sha256(authCode))jtiauthCodegenerateRandomString()crypto.getRandomValues(new Uint8Array(24))"ath"accessTokenbase64url(sha256(accessToken))calculateATH(accessToken)"nonce"nonce// 1. Key generation & JWK export
export async function generateDPoPKeyPair() // -> { publicKey, privateKey } (private key extractable=false)
export async function exportPublicJWK(publicKey) // -> { kty: 'EC', crv: 'P-256', x, y }
// 2. Proof generation & validation
export async function createDPoPProof({ privateKey, publicKey, htm, htu, nonce, accessToken, authCode, jti }) // -> signed JWT string
export async function verifyDPoPProof(dpopProofJwt) // -> { isValid: boolean, header, payload, error }
export function sanitizeHTU(htu) // -> URL stripped of query and hash: const u = new URL(htu); return `${u.origin}${u.pathname}`;
// 3. Cryptographic & encoding utilities
export function base64UrlEncode(buffer) // -> Uint8Array/ArrayBuffer to base64url string without '=' padding
export function base64UrlDecode(str) // -> base64url string to Uint8Array/Buffer
export function stringToBase64Url(str) // -> UTF-8 string to base64url
export function base64UrlToString(str) // -> base64url to UTF-8 string
export function generateRandomString(byteLength = 32) // -> cryptographic random base64url string
export async function calculateATH(accessToken) // -> base64url(sha256(accessToken)) per RFC 9449 Sec 6.1
export async function calculateAuthCodeJti(code) // -> base64url(sha256(code))
export async function generatePKCE() // -> { codeVerifier (>=43 chars), codeChallenge, codeChallengeMethod: 'S256' }oauth2.googleapis.com/tokenDPoP`DPoP: ${proofJwt}`POSTgrant_type=authorization_codegrant_type=refresh_token"token_type": "Bearer"`Authorization: Bearer ${accessToken}`400 Bad Requesterror: "use_dpop_nonce""DPoP-Nonce"400 use_dpop_noncethis.dpopNoncenoncejtiMCPnpx -y @google/mcp-developer-knowledge-serverdeveloper_knowledge:search_documentsdeveloper_knowledge:get_documents