Quickstart

Claim a page, wire your app, sign someone in.

From nothing to a working sign-in. You need a Google (or GitHub, Microsoft…) account and an app with a URL it can be redirected to.

1. Claim your page

Pick a name and open https://<name>.loginwith.page — for example https://acme.loginwith.page. A page nobody owns says so and offers to sign you in. Do it, with any provider.

That sign-in is your signup. It creates, under the name you picked:

  • acme.loginwith.page — your hosted login page, where your users sign in.
  • https://acme.loginwith.dev/ — your issuer: the OAuth endpoints and the keys your tokens are signed with.
  • A client — the client_id your app identifies itself with.

…and drops you in the console, owning all three.

2. Set it up in the console

Three things before your users can sign in:

  1. Pages → acme: choose the sign-in methods it offers, and publish it. A claimed page starts as a draft with none.
  2. Clients → acme: add your app’s callback URL to the redirect URIs — https://app.acme.com/auth/callback, or http://localhost:3000/auth/callback while developing. It must match exactly.
  3. Copy the client id (cli_…). If your app has a server that will redeem codes itself, make the client confidential and keep the secret it shows you once.

3. Wire your app

Plain HTML

<script defer src="https://acme.loginwith.dev/cdn/sdk-latest"></script>
<button onclick="lgw.signIn()">Sign in</button>

Register https://<your site>/lgw/callback as the redirect URI in step 2, and have that path serve a page with the same script — it finishes the sign-in. See Browser SDK.

Any OAuth / OIDC library

LoginWith is a standard OAuth 2.1 authorization server. Point your library at the discovery document and it finds the rest:

https://acme.loginwith.dev/.well-known/openid-configuration

Authorization code flow with PKCE (S256) — required for every client. A confidential client also authenticates at the token endpoint with client_secret_basic or client_secret_post. The access token is an RS256 JWT whose aud is your client id, verifiable against the jwks_uri.

SvelteKit

npm add @loginwith/sveltekit
// src/lib/server/auth.ts — server-side (BFF): tokens never reach the browser
import { createSvelteKitAuth } from "@loginwith/sveltekit/server"
import { env } from "$env/dynamic/private"

export const auth = createSvelteKitAuth({
  api: "https://acme.loginwith.dev",
  issuer: "https://acme.loginwith.dev/",   // trailing slash
  clientId: env.LGW_CLIENT_ID,
  clientSecret: env.LGW_CLIENT_SECRET,     // a confidential client
  loginPage: "https://acme.loginwith.page/",
  sessionSecret: env.LGW_SESSION_SECRET,   // openssl rand -base64 32
})
// src/hooks.server.ts                → export const { handle } = auth
// src/routes/lgw/login/+server.ts    → export const GET = auth.login
// src/routes/lgw/callback/+server.ts → export const GET = auth.callback
// src/routes/lgw/logout/+server.ts   → export const POST = auth.logout

locals.loginwith is then { token, claims } for a signed-in request, or null. Register https://<your app>/lgw/callback as the redirect URI in step 2.

Verifying tokens in a Koa API

npm add @loginwith/koa
import { authenticate, LoginWithVerifier, whitelist } from "@loginwith/koa"

app.use(authenticate(
  {
    verify: LoginWithVerifier({ issuer: "https://acme.loginwith.dev/", clientId: "cli_…" }),
    realm: "acme-api",
  },
  whitelist([["/health", ["GET"]]]),
))

What your users get

Anyone who signs in on acme.loginwith.page lands in your app: your org starts with one tenant open to everyone. Close it, or add more, from the console when you need to.