Sign in on a website
Authorization code flow with a client secret held by your server. People never type a password into your site.
Standard OpenID Connect, so any OIDC library or framework plugin works. Discovery: https://api.unet.ovh/oidc/.well-known/openid-configuration. Your server authenticates to the token endpoint with client_secret_basic (secret in the Authorization header; client_secret_post is not accepted, and a few libraries default to it, so set it explicitly). PKCE (S256) is supported and recommended.
// npm i openid-client (any OpenID Connect library works the same way)
import * as client from "openid-client";
// UNET wants the secret in the Authorization header (client_secret_basic); openid-client defaults to the request body
const config = await client.discovery(
new URL("https://api.unet.ovh/oidc"),
"YOUR_APP_ID",
undefined,
client.ClientSecretBasic("YOUR_APP_SECRET"),
);
// 1. send the person to UNET
const codeVerifier = client.randomPKCECodeVerifier();
const state = client.randomState();
const url = client.buildAuthorizationUrl(config, {
redirect_uri: "https://your-site.example/auth/unet/callback",
scope: "openid profile",
code_challenge: await client.calculatePKCECodeChallenge(codeVerifier),
code_challenge_method: "S256",
state,
});
// keep codeVerifier + state in the session, then redirect to url.href
// 2. on your callback URL
const tokens = await client.authorizationCodeGrant(config, new URL(req.url), {
pkceCodeVerifier: codeVerifier,
expectedState: state,
});
const { sub } = tokens.claims(); // the id you store
const profile = await client.fetchUserInfo(config, tokens.access_token, sub);
// profile: { sub, username, display_name, picture, account_created_at }// Auth.js / NextAuth: a custom OpenID Connect provider
import NextAuth from "next-auth";
export const { handlers, auth } = NextAuth({
providers: [
{
id: "unet",
name: "UNET",
type: "oidc",
issuer: "https://api.unet.ovh/oidc",
clientId: process.env.UNET_CLIENT_ID,
clientSecret: process.env.UNET_CLIENT_SECRET,
authorization: { params: { scope: "openid profile" } },
checks: ["pkce", "state"],
profile: (p) => ({
id: p.sub,
name: p.display_name ?? p.username,
image: p.picture ?? null,
}),
},
],
});