Integration guide
LycianAuth is a standard OpenID Provider. Apps integrate with the Authorization Code flow and PKCE, then verify EdDSA-signed JWTs locally. Any OIDC client library works; the SDKs below are optional.
Issuer · https://auth.lycian.app
Core concepts
- App — each product is one OAuth client, identified by a
client_id(for exampleo-pt-pt). - User — identity is the pair
(app, email). The same email in two apps is two separate users; they never share access. - No public sign-up — you add users from the dashboard. Only provisioned users can receive a login code.
- Login — the user enters their email on the hosted page and types a six-digit code. The JWT
aud(audience) is always yourclient_id.
Client types
When you register an app, you choose how it will authenticate. Pick based on where your code runs.
- Public client — for code that runs on the user's device: single-page web apps and mobile apps. There is no client secret, because anything shipped to a browser or phone can be read by the user, so a secret couldn't stay secret. Security comes from PKCE (a one-time proof the app generates per login) plus your registered redirect URIs.
- Confidential client — for code that runs on a server you control: a backend or server-rendered web app. It gets a client secret — a private password shown to you once at creation. Your backend stores it and sends it when exchanging the login code for tokens, proving the request really comes from your server.
If you're not sure: a React/Vue/mobile app is public; a Node/Go/Rails backend is confidential.
1 · Send the user to log in
Redirect the browser to the authorization endpoint with a PKCE challenge:
GET https://auth.lycian.app/authorize
?client_id=o-pt-pt
&redirect_uri=https://o-pt-pt.com/callback
&response_type=code
&scope=openid email profile offline_access
&state=RANDOM_STRING
&code_challenge=BASE64URL(SHA256(verifier))
&code_challenge_method=S256The user lands on the hosted login page, signs in, and is redirected back to
redirect_uri?code=...&state=.... Verify that state matches what you sent.
2 · Exchange the code for tokens
From your app, exchange the authorization code:
POST https://auth.lycian.app/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=THE_CODE
&redirect_uri=https://o-pt-pt.com/callback
&client_id=o-pt-pt
&code_verifier=THE_VERIFIER # public clients (PKCE)
# confidential clients also send their client secret
# (HTTP Basic auth, or client_secret_post)You receive an access_token (a JWT), an id_token, a refresh_token,
and expires_in.
3 · Verify access tokens — Go
On your API, verify the JWT on each request. The Go SDK wraps discovery, key fetching and validation:
import lycianauth "github.com/lycianapp/lycianauth/sdk/go"
verifier, _ := lycianauth.NewVerifier(ctx, "https://auth.lycian.app", "o-pt-pt")
mux.Handle("/api/", verifier.Middleware(apiHandler))
// inside a protected handler:
claims, _ := lycianauth.ClaimsFrom(r.Context())
userID := claims.SubjectThe middleware checks the signature against the published keys and confirms the issuer, audience and expiry. No call back to LycianAuth is needed per request.
4 · Browser — TypeScript
For single-page apps, the browser SDK handles the PKCE dance:
import { LycianAuth } from "./lycianauth";
const client = new LycianAuth({
issuer: "https://auth.lycian.app",
clientId: "o-pt-pt",
redirectUri: location.origin + "/callback",
});
await client.login(); // on your sign-in button
const tokens = await client.handleCallback(); // on your /callback routeRefresh & logout
- Refresh — call
POST /oauth/tokenwithgrant_type=refresh_token. Refresh tokens rotate: each use returns a new one and invalidates the previous. - Logout — redirect the user to
https://auth.lycian.app/end_session. - Profile data — email and name live in the
id_tokenand at/userinfo, not in the access token. Call/userinfowith the access token when you need them.
Access tokens last 15 minutes; refresh tokens last 30 days.
Endpoint reference
/.well-known/openid-configuration— discovery document./authorize— start the login flow./oauth/token— exchange code, or refresh./userinfo— claims for the bearer access token./keys— JWKS (public keys for verification)./end_session— logout.- /openapi.yaml — machine-readable spec.