Skip to main content

One post tagged with "pkce"

View All Tags

ยท 3 min read

If a self-hosted OIDC / OAuth2 provider is your thing, then most chances Hydra would be your best choice.

This post explains how to write a single page app (SPA) in js, with no backend that performs authentication via Hydra.

It uses crossid-spa-js, a javascript OAuth2 client (with PKCE) that suites for SPA apps that is able to sign users in and out with advanced features such caching and refresh token.

Setup Hydra

This setup is based on the 5 Minutes Tutorial which:

  • setup Hydra as an OAuth2 provider.
  • setup a server that serves an example of a login page.
  • create a client configured for SPA apps.


Hydra needs to be configured with:

  • admin CORS enabled for the OIDC wellknown endpoint.
  • public CORS enabled for the token endpoint.
  • issue access tokens in JWT format.

Edit contrib/quickstart/5-min/hydra.yml with:

enabled: true
enabled: true
access_token: jwt

Start Hydra:

$ docker-compose -f quickstart.yml \
-f quickstart-postgres.yml \
up --build

Starting hydra_postgresd_1
Starting hydra_hydra_1

Create a client

The client needs to be created as follows:

$ docker-compose -f quickstart.yml exec hydra \
hydra clients create \
--endpoint \
--id myspa \
--grant-types authorization_code,refresh_token \
--response-types code,id_token \
--token-endpoint-auth-method none \
--audience \
--scope openid,offline \
--callbacks http://localhost:3009 \

OAuth 2.0 Client ID: myspa
This OAuth 2.0 Client has no secret

We should not create a secret and set token-endpoint-auth-method to none as public clients cannot provide a secret.

SPA app

Our SPA app is a vanilla (frameworkless) javascript app sign users in, once user signed in, the user's profile will be displayed.


If you use React, try crossid-react that wraps the crossid-spa-js library in a more convinience way.

Clone the crossid-spa-js-example repo:

git clone myspa
cd myspa
npm install

This example is composed only from two files: src/main.js which contains the logic to initialize a client and signin and index.html which is the html of our app.

Lets edit src/main.js to init a Client that matches the params of the Hydra client we created above.

// ...
import { newCrossidClientByDiscovery, Client } from "@crossid/crossid-spa-js";

// inits a client that handles signing users in and out.
export const initClient = async () => {
const client = await newCrossidClientByDiscovery({
// configure a client by the oidc configuration well known endpoint
client_id: "myspa",
audience: [""],
scope: "openid offline",
redirect_uri: "http://localhost:3009",
// default cache is 'memory', enable to preserve user in cache
// cache_type: "session_storage",

return client;


Take a look at index.html that uses our login method.

Start the spa app:

npm run dev

And open browser at http://localhost:3009


We see more and more SPA apps, crossid-spa-js library makes it easy to integrate any JS app with OIDC provider such Hydra.


Interested in SAAS identity platform? visit