PKCE vs Proxy

PKCE vs Proxy

Background

Logging in to a web service used to mean sending a username and password and
getting a cookie in return. This cookie is then sent with every request so that
the server knows who is making the request. However, when it became more common
for services to interact with each other on a user's behalf, other forms of
authorization systems evolved. One such system is OAuth, which uses
token-based authentication instead of cookies.

OAuth is an Authorization framework that allows three different parties to
interact with a minimum level of trust. This way only one service needs to know
your username and password. The service that doesn’t store this information
can ask the service that does to verify who the user is and what they are allowed
to do. In general, this provides a better, more secure, user experience.
A user can maintain their profile and password information in a single place
and have that referenced automatically when they connect to other services. No
more typing in email addresses to every site and updating it in multiple places
if it changes. This is how Login with Facebook or Login with Google
buttons work.

Unfortunately, there are a couple of inherent security risks with this approach.
One service is not as trustworthy as another. You might want to allow that
budgeting app to read your account balance, but you probably don’t want it to
empty your checking account. Additionally, the first time services
communicate they must go over a public network where anybody can be listening.

In order to protect the credentials and limit what those credentials can do
some initial trust needs to be bootstrapped ahead of time. This
is done by combining two different components to ensure that the
recipient of a temporary public credential is, in fact, the one it is intended
for. If you have heard of two-factor authentication (2FA), it's like that but
for web services.

There are a couple of methods that use this combination of factors to provide
some extra confidence the service getting access is the right one. We’ll take a
look at two of those; Authorization Code Flow and
Proof of Key Code Exchange.

Authorization Code Flow

Protection by Proxy

Authorization code flow is the classic way to do this when a client that has a
server component. This server component protects an OAuth client secret and
proxies the token request from the client to the Authorization Server. In this
flow, a user is redirected from the service hosting the protected resources to
an Authorization Server. The Authorization Server’s job is to authenticate the
user and approve access. After successfully authenticating, usually with a
username and password, the user is redirected back to the service with some
extra URL parameters including an authorization code.

The authorization code is a temporary code needed to retrieve the access token
for permission to interact with the relying service's resources. The relying
service
then exchanges the authorization code combined with their client id and
client secret for an access token and optionally a refresh token. The relying
service
then returns these to the user's client, usually a browser or mobile
application. The client then uses this token to tell the resource server who
the request is for and what permissions they have.

Authorization Code Flow

Proof of Key Code Exchange (PKCE)

Cryptographic Pixie Dust

With the rise and popularity of mobile apps and Single Page Applications (SPA) a new
flow had to evolve to accomodate public clients. These clients cannot properly
protect a client secret since they run in an environment not controlled by the
application owner. These are often either a mobile device or JavaScript in the browser. To
compensate for the lack of confidentiality the flow extends the authorization
code
flow using cryptography to generate a code verifier and a code challenge
for every authorization code exchange. During the request to get an
authorization code the relying service includes the code challenge. Then when
the relying service receives the authorization code it exchanges that in
addition to the code verifier. The authorization server then uses the verifier
to confirm the original code challenge.

PKCE Flow

Why not exchange the password for a token?

Exchanging the password for a token is called the Resource Owner Credential
Flow
. This flow is not recommended unless there is a HIGH level of trust between
the Authorization Server and the client. When the client is JavaScript or a
mobile app the trust just isn’t there. The previous flows allow the for
interaction outside of the initial client for exchanging the password with the
Authorization Server. For example, on a mobile device, the application might
open a browser to begin the exchange and finish at an app-specific URL to
complete it. This way the mobile application never has to handle the username
and password directly.

What does this buy us?

In both Authorization Code and PKCE flows, two factors must be exchanged for valid
credentials. The authorization code, as presented as part of a redirect URL for
consumption, along with some additional information posted in the body of the
access token request. These two channels of communication mitigate a variety of
attacks and misconfigurations where bearer tokens or authorization can be
intercepted in flight.

However, with PKCE there is one more consideration to take into account and that
is where the Authorization Server redirects after authentication. The client
tells the Authorization Server where it wants to receive the code in the URL. If
this is not an exact match, it means there is an opening for a malicious client
to trick the Authorization Server to send the code somewhere else. That same
client can also generate the temporary secret code. While PKCE moves the
goalposts in terms of difficulty, it still relies on this redirect as an anchor
of trust.

While having a fully qualified redirect URL is a best practice,
Authorization Code flow mitigates an open redirect misconfiguration due to the
fact that the server still holds a predetermined secret. Because the secret used
in PKCE is generated at runtime, a malicious actor capitalizing on an open
redirect can still follow to the protocol to get valid credentials. On mobile
clients, this is an app-specific URL such as
app-foo://auth_code_handler#code=XXX. For SPA’s this would be something like
https://app.foo.com#code=XXX.

While PKCE was originally intended for mobile applications the OAuth Best
Practices Working Group

has recently started to recommend it for SPA’s as well.

To add a PKCE flow to your application take a look at https://appauth.io/ for
supported libraries and implementations.