Understanding Proof Key for Code Exchange in SMART on FHIR Integrations
If you’ve been building SMART on FHIR applications for a while, you’ve probably reached the point where the overall integration process feels familiar. Register the application, configure OAuth, request the required scopes, implement the launch flow, exchange the authorization code for an access token, and you’re ready to start working with FHIR resources.
We recently revisited one of our existing SMART on FHIR applications while preparing it for another EHR integration. Since the application had originally been developed a few years ago, we decided to review it against the latest implementation guidance and align the authorization flow with current standards. One of the changes we made was adding support for Proof Key for Code Exchange (PKCE).
PKCE wasn’t new to us, but its role in SMART on FHIR has evolved. Current SMART App Launch guidance requires PKCE support for both public and confidential clients, making it relevant even for server-side applications that already have their own client authentication.
Before getting into how it works, there is one small detail that made us smile. Although the acronym stands for Proof Key for Code Exchange, almost everyone pronounces it as “pixy.” If you’re a Harry Potter fan, it’s difficult not to think of the Cornish Pixies from Professor Lockhart’s classroom. Fortunately, that’s where the similarity ends.
Why PKCE exists
PKCE is an extension to the OAuth 2.0 Authorization Code Flow. In the standard flow, the application redirects the user to the authorization server. After authentication, the authorization server returns an authorization code, which the application exchanges for an access token.
The potential problem is that if someone manages to intercept the authorization code before the application exchanges it, they may be able to use it themselves. The code is short-lived and can only be used once, but during that brief period it still needs to be protected.
PKCE adds another piece of information that links the authorization request to the application that started it. This means that having the authorization code alone is no longer enough to obtain an access token.
How PKCE changes the flow
The idea behind PKCE is fairly straightforward. Before redirecting the user to the authorization server, the application generates a random string called the code_verifier. From it, the application creates another value called the code_challenge.

The code_challenge is sent with the initial authorization request, while the original code_verifier stays with the application.
When the user completes authentication, the authorization server returns the authorization code as usual. During the token exchange, however, the application sends both the authorization code and the original code_verifier.
The authorization server checks that the verifier corresponds to the challenge received at the beginning of the flow. If they match, the access token can be issued. If they don’t, the request is rejected.

This is the key idea behind PKCE: an intercepted authorization code is no longer useful on its own because the matching code_verifier is also required.
For SMART on FHIR integrations, the challenge must use the S256 method. The plain method is not permitted under current SMART App Launch guidance.
What changes for the application?
From a developer’s perspective, the changes are relatively small. PKCE doesn’t introduce a completely new authorization flow. It adds two parameters to the authorization request—code_challenge and code_challenge_method—and the code_verifier to the token request.

For confidential applications, PKCE is an additional security mechanism rather than a replacement for existing client authentication. The application still authenticates itself as before; PKCE adds protection specifically to the authorization code exchange.
Why are we seeing PKCE more often?
PKCE itself isn’t new, but its role in modern OAuth implementations has grown over time. It was originally introduced primarily for public clients, such as mobile applications, where securely storing a client secret can be difficult.
SMART on FHIR guidance has evolved as well. Current SMART App Launch guidance requires PKCE support for all clients, including confidential applications. This means that an integration implemented several years ago may need to be revisited even if its OAuth flow has been working reliably.
That’s probably the broader takeaway from this update. Keeping an integration current isn’t always about adding new functionality or supporting new APIs. Security standards and implementation guidance evolve too, and sometimes a relatively small change—like adding PKCE—is enough to bring an existing application in line with today’s requirements.
Learn more about FHIR implementation lifecycle.
August 2026