Connect to an Identity Provider such as Okta using OAuth 2.0 and validate authentication status at the network's edge, using Fastly Compute to authorize access to your edge or origin hosted applications.
Check out the equivalent Rust implementation 🦀
For quick ways to get started with Fastly Compute, see the Fastly Developer Hub
This is a self-contained JavaScript implementation for the OAuth 2.0 Authorization Code flow with Proof Key for Code Exchange (PKCE), deployed to Fastly Compute.
It includes JSON Web Token (JWT) verification, and access token introspection.
Scroll down to view the flow in more detail.
After you have checked out the code from this repository, you'll need to do some configuration before you can deploy it, so that Fastly knows which identity provider to use and how to authenticate.
Make sure you have installend and configured the Fastly CLI first.
- Obtain a client ID and optional client secret from your chosen OAuth 2.0 Identity Provider (IdP).
- Create an
.env
file and set theCLIENT_ID
,CLIENT_SECRET
, and a randomNONCE_SECRET
environment variables. - Run
fastly compute publish
and follow the interactive configuration prompts to set up a new Fastly Compute service. - Add
https://{your-fastly-compute-domain}/callback
to the list of allowed callback URLs in your IdP's app configuration.
The first time you run fastly compute publish
, respond with y
to the prompt to create a new service.
Accepting the default values for all other prompts will set up Google as your OAuth 2.0 Identity Provider (IdP), to let you get started as quickly as possible. You may choose to provide your own configuration settings instead.
Create new service: [y/N] y
Service name: [compute-js-auth]
✓ Creating service
Domain: [vaguely-pretty-ray.edgecompute.app]
Configure a backend called 'origin'
...
You will be prompted to set up two backends:
idp
: Your authorization server;origin
: Your application or content server.
Configuration data lives in Fastly Config Store named compute_js_auth_config
, with the following keys:
openid_configuration
: The OpenID Configuration (OIDC) metadata from your authorization server, JSON-serialized;jwks
: JWKS metadata from your authorization server (obtained from thejwks_uri
property of the OIDC metadata), JSON-serialized.
💡 After you've obtained each of the JSON metadata above from your authorization server, you'll have to stringify it before using as an input for the Fastly CLI. Check out this jq playground snippet for a quick way to accomplish this.
Create an .env
file (example contents here) and set the following environment variables:
CLIENT_ID
: The OAuth 2.0 client ID (determined by the Identity Provider (IdP)).CLIENT_SECRET
: The OAuth 2.0 client secret (if required by the IdP).NONCE_SECRET
: A secret to verify the OpenID nonce used to mitigate replay attacks. It must be sufficiently random to not be guessable.
💡 Run
dd if=/dev/random bs=32 count=1 | base64
to generate a random, non-guessable secret.
Store configuration secrets in a Fastly Secret Store named compute_js_auth_secrets
, with the keys client_id
, client_secret
, and nonce_secret
respectively.
You must set the USE_SECRET_STORE
constant to true
in src/config.js
⚠️ For this to work, Secret Stores must be enabled on your Fastly account. Secret Stores is a paid feature in Fastly Compute. Contact Fastly Support to opt in to the beta.
💡 To simplify local development and initial deployment, you may add the
[local_server.secret_stores]
and[setup.secret_stores]
sections in your fastly.toml file. Check out fastly.secretstore.example.toml.
You might operate your own identity service, but any OAuth 2.0, OpenID Connect (OIDC) conformant identity provider will work. You will need the following from your IdP:
- A Client ID, and optionally, a Client secret ➡️ Set the
CLIENT_ID
andCLIENT_SECRET
environment variables, or set theclient_id
andclient_secret
keys in the Secret Store. - An OpenID Connect Discovery document ➡️ Set the
openid_configuration
key (JSON-serialized string value) in the Config Store. - A JSON Web key set ➡️ Set the
jwks
key (JSON-serialized string value) in the Config Store. - The hostname of the IdP's authorization server ➡️ Create as a backend called
idp
on your Fastly service
To build and deploy your Fastly Compute service, run:
fastly compute publish
You'll be prompted to enter a series of configuration settings, explained above. When the deployment is finished you'll be given a Fastly-assigned domain such as random-funky-words.edgecompute.app
.
Remember to add https://{your-fastly-compute-domain}/callback
(e.g., https://random-funky-words.edgecompute.app/callback) to the list of allowed callback URLs in your IdP's OAuth app configuration.
This allows the authorization server to send the user back to the Compute@Edge service.
As an example, if you are using Google as your IdP, follow these steps:
- In the Google API Console, use the Credentials API to create a new OAuth client ID. Choose Web application as your application type, give your app a name, and finally make note of the following two outputs:
- The Client ID (eg.
RANDOM_LONG_ID.apps.googleusercontent.com
) is shown next to your application name. - The Client SECRET (eg.
RANDOM_LONG_SECRET
) is shown next to your application name.
- The Client ID (eg.
- Create an
.env
(example) in your Fastly project and paste in theCLIENT_ID
andCLIENT_SECRET
obtained before. Set a randomNONCE_SECRET
, a long, non-guessable random string of your choice. Save the file. - After you've configured and deployed your new Fastly Compute service, find your new OAuth client ID in the Google API Console, and add
https://{random-funky-words}.edgecompute.app/callback
to the list of Authorized redirect URIs💡 Optionally, also add
http://127.0.0.1:7676/callback
as an authorized redirect URI for local development.
Follow the steps above and visit your Fastly-assigned domain. You should be prompted to follow a login flow with your IdP, and then after successfully authenticating, will see content delivered from your own origin.
Here is how the authentication process works:
- The user makes a request for a protected resource, but they have no session cookie.
- At the edge, this service generates:
- A unique and non-guessable
state
parameter, which encodes what the user was trying to do (e.g., load/articles/kittens
). - A cryptographically random string called a
code_verifier
. - A
code_challenge
, derived from thecode_verifier
. - A time-limited token, authenticated using the
nonce_secret
, that encodes thestate
and anonce
(a unique value used to mitigate replay attacks).
- A unique and non-guessable
- The
state
andcode_verifier
are stored in session cookies. - The service builds an authorization URL and redirects the user to the authorization server operated by the IdP.
- The user completes login formalities with the IdP directly.
- The IdP will include an
authorization_code
and astate
(which should match the time-limited token we created earlier) in a post-login callback to the edge. - The edge service authenticates the
state
token returned by the IdP, and verifies that the state cookie matches its subject claim. - Then, it connects directly to the IdP and exchanges the
authorization_code
(which is good for only one use) andcode_verifier
for security tokens:- An
access_token
– a key that represents the authorization to perform specific operations on behalf of the user) - An
id_token
, which contains the user's profile information.
- An
- The end-user is redirected to the original request URL (
/articles/kittens
), along with their security tokens stored in cookies. - When the user makes the redirected request (or subsequent requests accompanied by security tokens), the edge verifies the integrity, validity and claims for both tokens. If the tokens are still good, it proxies the request to your origin.
Run fastly compute serve --watch
(or npm run dev
) to spin up a local development server and watch source files for changes.
If you encounter any bugs or unexpected behavior, or have feature requests or questions, please file an issue.