---
title: "Secure OAuth 2.0 Access Tokens with Proofs of Possession"
description: "Bearer access tokens grant whoever possesses them access, making theft especially risky for public clients such as SPAs and mobile apps. DPoP, an OAuth 2.0 extension, binds a token to a client’s public key and uses signed proofs so authorization and resource servers can verify requests. The tutorial migrates an Angular single-page application from bearer tokens to DPoP while building on the Authorization Code flow, inspecting token exchanges, request headers, replay protection, and calls to Okta’s APIs; React and Vue variants are also noted. It recommends IndexedDB with non-exportable WebCrypto keys, cleanup during sign-in, and browser capability checks, while noting that the preserved setup instructions no longer work exactly as written after Okta’s May 2025 account changes."
---

# Secure OAuth 2.0 Access Tokens with Proofs of Possession

[Okta](https://yomu.fyi/company/okta) · Alisa Duncan · Sep 10, 2024

**Type:** Tutorial

## Summary

Bearer access tokens grant whoever possesses them access, making theft especially risky for public clients such as SPAs and mobile apps. DPoP, an OAuth 2.0 extension, binds a token to a client’s public key and uses signed proofs so authorization and resource servers can verify requests. The tutorial migrates an Angular single-page application from bearer tokens to DPoP while building on the Authorization Code flow, inspecting token exchanges, request headers, replay protection, and calls to Okta’s APIs; React and Vue variants are also noted. It recommends IndexedDB with non-exportable WebCrypto keys, cleanup during sign-in, and browser capability checks, while noting that the preserved setup instructions no longer work exactly as written after Okta’s May 2025 account changes.

## Context

Bearer tokens allow any party that possesses them to access protected resources. The post focuses on public clients, including single-page applications and mobile apps, which cannot use a client secret through a back-channel and therefore face additional token-protection risks.

## Approach / What changed

The tutorial starts from an Angular, React, or Vue project, configures OAuth 2.0 and OpenID Connect with Okta, and compares bearer-token requests with DPoP-protected requests. It enables DPoP in the client, examines proofs and nonces, requests protected resources with DPoP headers, and addresses browser key storage, cleanup, and capability checks.

## Takeaways

- DPoP uses public/private keys and signed proofs to bind an OAuth access token to the client sending the request, making a stolen token less usable by itself.
- A resource server can reduce proof replay by tracking the JWT ID (jti); it can also reject proofs whose issued-at (iat) time exceeds an allowed threshold. JWT ID enforcement is not required by the DPoP specification.
- The Okta SDK stores DPoP key pairs in IndexedDB and uses non-exportable WebCrypto keys. IndexedDB is harder to use, and persistent keys require cleanup because users may not explicitly sign out.

## FAQ

### How does DPoP limit the usefulness of a stolen access token?

DPoP binds the access token to the OAuth client’s key and requires a signed proof for the request. A thief with only the access token is therefore less likely to use it successfully; possession of the keyset would allow impersonation.

### How can DPoP proofs be protected against replay?

A resource server can track each proof’s JWT ID (jti) and reject a previously used proof. It can also check the issued-at (iat) timestamp and reject proofs older than its allowed threshold.

### Where should a browser SPA store DPoP cryptographic keys?

The post recommends IndexedDB rather than local or session storage. WebCrypto can generate non-exportable private keys, while the Okta SDK handles cleanup on explicit sign-out and can clear stored keys before a new sign-in.

**Tags:** [Angular](https://yomu.fyi/topic/angular), [DPoP](https://yomu.fyi/topic/dpop), [OAuth 2.0](https://yomu.fyi/topic/oauth-2-0), [OpenID Connect](https://yomu.fyi/topic/openid-connect)

- Source: [Okta](https://developer.okta.com/blog/2024/09/10/angular-dpop-jwt)
- Source URL: https://developer.okta.com/blog/2024/09/10/angular-dpop-jwt
- Ingested by Yomu: 2026-09-17T14:06:25.165Z

[Read original post](https://developer.okta.com/blog/2024/09/10/angular-dpop-jwt)
