EAuth docs

EAuth

Login with EAuth

Ten minutes, ending with a signed-in user and their email address in your application.

1. Register your application

Go to panel.elchi.dev and create an application.

Choose the type carefully, because it cannot be changed afterwards:

Type Use when Authenticated by
Public Browser app, mobile app, anything whose code the user can read PKCE alone
Confidential Server-rendered app, backend service PKCE and a client secret

A single-page application is public. Choosing confidential would put a secret into a JavaScript bundle, where it is not a secret.

Add your redirect URI exactly as your application will send it. A trailing slash makes it a different URI, and wildcards are refused.

2. Point your library at the discovery URL

https://auth.elchi.dev/.well-known/openid-configuration

Every conforming OpenID Connect library takes that one address and configures each endpoint itself. You should not need to hardcode any other URL.

3. Send the user to sign in

Generate a PKCE pair, keep the verifier, send the challenge:

const verifier = base64url(crypto.getRandomValues(new Uint8Array(48)));
const challenge = base64url(
  await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier))
);
sessionStorage.setItem("pkce_verifier", verifier);

const url = new URL("https://auth.elchi.dev/authorize");
url.searchParams.set("response_type", "code");
url.searchParams.set("client_id", CLIENT_ID);
url.searchParams.set("redirect_uri", REDIRECT_URI);
url.searchParams.set("scope", "openid profile email");
url.searchParams.set("state", crypto.randomUUID());
url.searchParams.set("code_challenge", challenge);
url.searchParams.set("code_challenge_method", "S256");
location.assign(url);

Store the state value. You will compare it in the next step.

4. Handle the redirect back

The user returns to your redirect URI with three parameters:

?code=...&state=...&iss=https://auth.elchi.dev

Check all three before doing anything else:

  • state must equal what you stored. If it does not, the request did not start in this browser session, and you should abandon it.
  • iss must be exactly https://auth.elchi.dev. This is RFC 9207, and checking it is what stops a mix-up attack when your application talks to more than one provider.
  • code is single use and expires in sixty seconds.

5. Exchange the code for tokens

From your server, never from the browser:

curl -X POST https://auth.elchi.dev/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=authorization_code \
  -d code="$CODE" \
  -d redirect_uri="$REDIRECT_URI" \
  -d code_verifier="$VERIFIER"

A public client omits -u and sends client_id in the body instead.

You get back an access token, an ID token, and a refresh token if you asked for offline_access.

6. Read who signed in

The ID token is a signed JWT. Verify it against our JWKS, then read the claims:

{
  "iss": "https://auth.elchi.dev",
  "aud": "eauth_cnf_...",
  "sub": "01a06188-ea2a-7c15-8ac2-d33d2eacd4db",
  "name": "Anna Muster",
  "email": "anna@example.com",
  "email_verified": false
}

Use sub as the user's identity in your database. It never changes. Do not key on email: people change addresses, and two different people can hold the same address years apart.

What to do next

The playground builds a real authorization request from your own client id so you can watch the flow happen before writing any code.

3 min read · Elchi Studios, Zug · Terms