<!-- https://docs.elchi.dev/quickstart | EAuth | Elchi Docs -->

# 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](https://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:

```js
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:

```bash
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](/sdk#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](https://eauth.me/.well-known/jwks.json), then read the claims:

```json
{
  "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](/playground) builds a real authorization request from your own
client id so you can watch the flow happen before writing any code.
