DocumentationSign in with Learnya
Operate
Sign in with Learnya
Let people sign in to your application with their Learnya account. OAuth 2.1 and OpenID Connect, on auth.learnya.ai.
On this page
The discovery document
Everything an OpenID Connect library needs is published at one URL. Most libraries can be configured with that alone.
curl https://auth.learnya.ai/.well-known/openid-configurationResponse
{
"issuer": "https://auth.learnya.ai",
"authorization_endpoint": "https://auth.learnya.ai/authorize",
"token_endpoint": "https://auth.learnya.ai/token",
"jwks_uri": "https://auth.learnya.ai/.well-known/jwks.json",
"grant_types_supported": [
"authorization_code",
"refresh_token",
"client_credentials",
"urn:ietf:params:oauth:grant-type:token-exchange"
],
"code_challenge_methods_supported": ["S256"]
}Which flow to choose
| Your use case | OAuth flow |
|---|---|
| A person signs in to your application | authorization_code |
| Your server acts on its own behalf | client_credentials |
| Keep a session open | refresh_token |
| A service acts on behalf of another | token-exchange |
Authorization code with PKCE
PKCE is required for every application, with the S256 method. The redirect URI must exactly match the registered one.
Send the user to sign in
GET https://auth.learnya.ai/authorize ?response_type=code &client_id=lyc_your_client &redirect_uri=https://app.example.ch/callback &scope=openid profile email offline_access &state=a value you check on return &code_challenge=BASE64URL(SHA256(verifier)) &code_challenge_method=S256Exchange the code for tokens
curl https://auth.learnya.ai/token \ -u "lyc_your_client:$CLIENT_SECRET" \ -d grant_type=authorization_code \ -d code=the_code_from_the_callback \ -d redirect_uri=https://app.example.ch/callback \ -d code_verifier=the_verifier_you_generatedResponse{ "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCJ9…", "token_type": "Bearer", "expires_in": 900, "refresh_token": "…", "id_token": "eyJhbGciOiJSUzI1NiJ9…", "scope": "openid profile email offline_access" }
Verify a token
Access tokens are JWTs signed with RS256. Verify them with the published public keys, no shared secret needed. Five checks matter: the algorithm pinned to RS256, the at+jwt type, the issuer, your audience and the expiry.
import jwt from "jsonwebtoken"
const ISSUER = "https://auth.learnya.ai"
const AUDIENCE = "https://api.example.ch"
// keyFor maps a kid to a public key from the JWKS.
export function verifyAccessToken(token, keyFor) {
const decoded = jwt.decode(token, { complete: true })
if (!decoded?.header.kid) throw new Error("no kid in header")
if (decoded.header.typ !== "at+jwt")
throw new Error("not an access token")
return jwt.verify(token, keyFor(decoded.header.kid), {
algorithms: ["RS256"],
issuer: ISSUER,
audience: AUDIENCE,
})
}import type { KeyObject } from "node:crypto"
import jwt, { type JwtPayload } from "jsonwebtoken"
const ISSUER = "https://auth.learnya.ai"
const AUDIENCE = "https://api.example.ch"
// keyFor maps a kid to a public key from the JWKS.
export function verifyAccessToken(
token: string,
keyFor: (kid: string) => KeyObject,
): JwtPayload {
const decoded = jwt.decode(token, { complete: true })
if (!decoded?.header.kid) throw new Error("no kid in header")
if (decoded.header.typ !== "at+jwt")
throw new Error("not an access token")
const payload = jwt.verify(
token,
keyFor(decoded.header.kid),
{
algorithms: ["RS256"],
issuer: ISSUER,
audience: AUDIENCE,
},
)
if (typeof payload === "string")
throw new Error("not a JSON payload")
return payload
}Also available
| Feature | For |
|---|---|
| Pushed authorization requests | Send the request to the server rather than in the URL |
| DPoP | Bind a token to a key your application holds |
| Revocation and introspection | Revoke a token, or check whether it is still valid |
| Logout | End the Learnya session from your application |
Register your application
Learnya registers each application, with its exact redirect URIs. Write to us with your application’s name, its redirect URIs and the information you need about the user.