@ouim/simple-logto
Version:
A simpler way to use @logto/react with prebuilt UI components and hooks for fast authentication setup
217 lines (216 loc) • 6.08 kB
JavaScript
import { importJWK as x, jwtVerify as y } from "jose";
const w = /* @__PURE__ */ new Map(), I = 5 * 60 * 1e3;
function i(t, e = "guest_logto_authtoken") {
if (typeof (t == null ? void 0 : t.get) == "function") {
const r = t.get(e);
return (r == null ? void 0 : r.value) || f();
} else if (t && typeof t == "object")
return t[e] || f();
return f();
}
function l(t, e = "logto_authtoken") {
if (typeof (t == null ? void 0 : t.get) == "function") {
const r = t.get(e);
return (r == null ? void 0 : r.value) || null;
} else if (t && typeof t == "object")
return t[e] || null;
return null;
}
function d(t) {
const e = typeof t.get == "function" ? t.get("authorization") : t.authorization;
return typeof e == "string" && e.startsWith("Bearer ") ? e.slice(7) : null;
}
function p(t) {
let e = t.replace(/-/g, "+").replace(/_/g, "/");
const r = e.length % 4;
return r && (e += "=".repeat(4 - r)), typeof atob < "u" ? atob(e) : Buffer.from(e, "base64").toString();
}
async function k(t) {
const r = `${t.replace(/\/+$/, "")}/oidc/jwks`, o = Date.now(), n = w.get(r);
if (n && n.expires > o)
return n.keys;
try {
const s = await fetch(r);
if (!s.ok)
throw new Error(`Failed to fetch JWKS: ${s.status} ${s.statusText}`);
const a = (await s.json()).keys || [];
return w.set(r, {
keys: a,
expires: o + I
}), a;
} catch (s) {
throw new Error(`Failed to fetch JWKS from ${r}: ${s instanceof Error ? s.message : "Unknown error"}`);
}
}
function m(t, e, r) {
if (!t || t.length === 0)
throw new Error("No keys found in JWKS");
if (e) {
const n = t.find((s) => s.kid === e);
if (n)
return n;
throw new Error(`Key with kid "${e}" not found in JWKS`);
}
if (r) {
const n = t.find((s) => s.alg === r);
if (n)
return n;
}
const o = t.find((n) => n.kty === "RSA" && (n.use === "sig" || !n.use));
return o || t[0];
}
function E(t, e) {
const { logtoUrl: r, audience: o, requiredScope: n } = e, s = new URL("oidc", r).toString();
if (t.iss !== s)
throw new Error(`Invalid issuer. Expected: ${s}, Got: ${t.iss}`);
if (o && t.aud !== o)
throw new Error(`Invalid audience. Expected: ${o}, Got: ${t.aud}`);
const u = Math.floor(Date.now() / 1e3);
if (t.exp && t.exp < u)
throw new Error("Token has expired");
if (t.nbf && t.nbf > u)
throw new Error("Token is not yet valid");
if (n && (!t.scope || !t.scope.includes(n)))
throw new Error(`Missing required scope: ${n}`);
}
async function h(t, e) {
const { logtoUrl: r } = e;
try {
const [o] = t.split(".");
if (!o)
throw new Error("Invalid JWT format");
const n = p(o), s = JSON.parse(n), u = await k(r), a = m(u, s.kid, s.alg), g = await x(a, (s == null ? void 0 : s.alg) || "RS256"), { payload: c } = await y(t, g);
return E(c, e), {
userId: c.sub,
isAuthenticated: !0,
payload: c,
isGuest: !1
};
} catch (o) {
throw new Error(`Token verification failed: ${o instanceof Error ? o.message : "Unknown error"}`);
}
}
const f = () => "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(t) {
const e = Math.random() * 16 | 0;
return (t === "x" ? e : e & 3 | 8).toString(16);
});
function v(t) {
return async (e, r, o) => {
try {
let n = l(e.cookies, t.cookieName);
if (n || (n = d(e.headers)), !n) {
if (t.allowGuest) {
const u = i(e.cookies);
return e.auth = {
userId: null,
isAuthenticated: !1,
payload: null,
isGuest: !0,
guestId: u || void 0
}, o();
}
return r.status(401).json({
error: "Authentication required",
message: "No token found in cookies or Authorization header"
});
}
const s = await h(n, t);
return e.auth = s, o();
} catch (n) {
if (t.allowGuest) {
const s = i(e.cookies);
return e.auth = {
userId: null,
isAuthenticated: !1,
payload: null,
isGuest: !0,
guestId: s || void 0
}, o();
}
return r.status(401).json({
error: "Authentication failed",
message: n instanceof Error ? n.message : "Unknown error"
});
}
};
}
async function G(t, e) {
try {
let r = l(t.cookies, e.cookieName);
return r || (r = d(t.headers)), r ? {
success: !0,
auth: await h(r, e)
} : e.allowGuest ? {
success: !1,
error: "No authentication token found",
auth: {
userId: null,
isAuthenticated: !1,
payload: null,
isGuest: !0,
guestId: i(t.cookies) || void 0
}
} : {
success: !1,
error: "No token found in cookies or Authorization header"
};
} catch (r) {
if (e.allowGuest) {
const n = {
userId: null,
isAuthenticated: !1,
payload: null,
isGuest: !0,
guestId: i(t.cookies) || void 0
};
return {
success: !1,
error: r instanceof Error ? r.message : "Unknown error",
auth: n
};
}
return {
success: !1,
error: r instanceof Error ? r.message : "Unknown error"
};
}
}
async function U(t, e) {
let r;
if (typeof t == "string")
r = t;
else {
const o = l(t.cookies, e.cookieName) || d(t.headers);
if (!o) {
if (e.allowGuest)
return {
userId: null,
isAuthenticated: !1,
payload: null,
isGuest: !0,
guestId: i(t.cookies) || void 0
};
throw new Error("No token found in request");
}
r = o;
}
try {
return await h(r, e);
} catch (o) {
if (e.allowGuest && typeof t == "object")
return {
userId: null,
isAuthenticated: !1,
payload: null,
isGuest: !0,
guestId: i(t.cookies) || void 0
};
throw o;
}
}
export {
v as createExpressAuthMiddleware,
U as verifyAuth,
h as verifyLogtoToken,
G as verifyNextAuth
};