@ohlom/authjs
v0.1.0
Published
Login with Ohlom — an Auth.js (NextAuth v5) / next-auth v4 OAuth provider. PKCE + state, OIDC discovery, maps the Ohlom profile (sub/name/phone_number).
Downloads
86
Maintainers
Readme
@ohlom/authjs
Login with Ohlom as an Auth.js / NextAuth OAuth provider.
- Works with Auth.js / NextAuth v5 (
next-auth@5or@auth/core) and NextAuth v4 (next-auth@4). - OAuth 2.0 Authorization Code + PKCE (S256) +
state, over Ohlom's OIDC endpoints. - Maps the Ohlom profile →
{ id: sub, name, phone: phone_number }. There is no email (Ohlom keys accounts onsub), soemailisnull.
Install
npm install @ohlom/authjs
# plus your Auth.js version, e.g.
npm install next-auth@beta # v5
# or
npm install next-auth@4 # v4Contract
| | |
|---|---|
| Base URL | https://api.ohlom.com |
| Authorize | GET /oauth/authorize (response_type=code, PKCE S256) |
| Token | POST /oauth/token (form-encoded; confidential = client_secret_post) |
| Userinfo | GET /oauth/userinfo (Authorization: Bearer → sub, name, phone_number) |
| Discovery | /.well-known/openid-configuration |
| Scopes | openid profile phone (+ partner scopes) |
Auth.js runs server-side, so register a confidential client (with a secret).
Quick start — Next.js App Router (v5)
auth.ts
import NextAuth from "next-auth";
import { Ohlom } from "@ohlom/authjs";
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
Ohlom({
clientId: process.env.OHLOM_CLIENT_ID!,
clientSecret: process.env.OHLOM_CLIENT_SECRET!,
// scopes: ["openid", "profile", "phone", "orders:read"], // optional
}),
],
});app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth";
export const { GET, POST } = handlers;A sign-in button (Server Action):
import { signIn } from "@/auth";
export function SignIn() {
return (
<form action={async () => { "use server"; await signIn("ohlom"); }}>
<button type="submit">Login with Ohlom</button>
</form>
);
}Surfacing phone in the session
Ohlom returns phone instead of email. Carry it through the JWT/session:
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [Ohlom({ clientId: "...", clientSecret: "..." })],
callbacks: {
async jwt({ token, profile }) {
if (profile && "phone_number" in profile) {
token.phone = (profile as { phone_number?: string }).phone_number ?? null;
}
return token;
},
async session({ session, token }) {
(session.user as { phone?: string | null }).phone =
(token as { phone?: string | null }).phone ?? null;
return session;
},
},
});NextAuth v4 — pages/api/auth/[...nextauth].ts
import NextAuth from "next-auth";
import { Ohlom } from "@ohlom/authjs";
export default NextAuth({
providers: [
Ohlom({
clientId: process.env.OHLOM_CLIENT_ID!,
clientSecret: process.env.OHLOM_CLIENT_SECRET!,
}),
],
});Callback / redirect URI
Register this redirect URI with Ohlom (at dev.ohlom.com) for each environment:
{APP_ORIGIN}/api/auth/callback/ohlome.g. http://localhost:3000/api/auth/callback/ohlom and your production origin.
It must match exactly.
Options
Ohlom({
clientId: string, // required
clientSecret?: string, // confidential client (recommended server-side)
scopes?: string[], // default: ["openid", "profile", "phone"]
baseUrl?: string, // default: "https://api.ohlom.com"
useDiscovery?: boolean, // default: false (use explicit endpoints)
overrides?: Record<string, unknown>, // merged last; override id/name/style/etc.
})useDiscovery: false(default) — explicitauthorize/token/userinfoendpoints (one fewer round-trip;type: "oauth").useDiscovery: true— remote OIDC discovery viawellKnown(type: "oidc").
Both modes apply checks: ["pkce", "state"] and confidential client_secret_post.
Profile mapping
profile(profile) returns:
{ id: profile.sub, name: profile.name ?? null, phone: profile.phone_number ?? null, email: null, image: null }The standalone ohlomProfile() mapper is also exported for use in custom callbacks.
Notes
- This package never logs tokens.
- Ohlom has no end-session/revocation endpoint;
signOut()clears the local Auth.js session.
License
MIT
