Arrow keys to move, Enter to open, Escape to close.

  1. Docs
  2. EAuth
  3. Login with 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://eauth.me/.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://eauth.me/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://eauth.me

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://eauth.me. 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

A confidential client exchanges the code from its server, never from the browser, because the request carries its secret:

curl -X POST https://eauth.me/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. A single-page application sends this request from the page itself: EAuth lets a page read the answer when it runs on the origin of one of the application's registered redirect URIs (see requests from the browser).

You get back an access token, an ID token, and a refresh token if you asked for offline_access. New applications may ask for it. For one whose settings leave it out, EAuth leaves it out of the sign-in too, rather than refusing it.

6. Read who signed in

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

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

email_verified is always true here: EAuth signs nobody in to your application before they have confirmed their address with the link it mails them. Somebody who registers is asked to open that link first; opened in the browser they registered in, it signs them in and brings them back to you; opened anywhere else, it asks for the account's password as well (see Security). Somebody who signs in with an address not yet confirmed is stopped on the way, with a page that sends a new link.

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.

4 min read