vuethenticate
Version:
A Vue 3 authentication state management library using oidc-client-ts
540 lines (539 loc) • 17.1 kB
JavaScript
import { ref as E, computed as N, watch as X, defineComponent as T, onMounted as M, createElementBlock as G, openBlock as K, Fragment as q, renderSlot as S, createCommentVNode as V, unref as x, createElementVNode as v, toDisplayString as O } from "vue";
import { WebStorageStateStore as Z, Log as m, UserManager as ee } from "oidc-client-ts";
var f = /* @__PURE__ */ ((e) => (e[e.NONE = 0] = "NONE", e[e.ERROR = 1] = "ERROR", e[e.WARN = 2] = "WARN", e[e.INFO = 3] = "INFO", e[e.DEBUG = 4] = "DEBUG", e))(f || {});
const Y = {
error: (e, ...t) => console.error(e, ...t),
warn: (e, ...t) => console.warn(e, ...t),
info: (e, ...t) => console.info(e, ...t),
debug: (e, ...t) => console.debug(e, ...t)
}, te = {
error: () => {
},
warn: () => {
},
info: () => {
},
debug: () => {
}
}, re = (e, t) => ({
error: t >= 1 ? e.error : () => {
},
warn: t >= 2 ? e.warn : () => {
},
info: t >= 3 ? e.info : () => {
},
debug: t >= 4 ? e.debug : () => {
}
});
function ae(e) {
const t = typeof window < "u" ? window.location.origin : "";
return se(e), {
authority: e.authority,
client_id: e.clientId,
client_secret: e.clientSecret,
client_authentication: e.clientAuthentication,
redirect_uri: e.redirectUri ?? `${t}/auth/callback`,
scope: e.scope ?? "openid profile",
response_type: e.responseType ?? "code",
userStore: e.storage ? new Z({ store: ne(e.storage) }) : void 0,
automaticSilentRenew: e.automaticSilentRenew ?? !0,
silent_redirect_uri: e.silentRedirectUri ?? `${t}/auth/silent-callback`,
post_logout_redirect_uri: e.postLogoutRedirectUri ?? `${t}/auth/logout-callback`,
includeIdTokenInSilentRenew: !0,
accessTokenExpiringNotificationTimeInSeconds: 60
};
}
function ne(e) {
if (typeof window > "u")
return B();
switch (e) {
case "localStorage":
return window.localStorage;
case "sessionStorage":
return window.sessionStorage;
case "memory":
return B();
default:
return window.localStorage;
}
}
function B() {
const e = /* @__PURE__ */ new Map();
return {
getItem: (t) => e.get(t) ?? null,
setItem: (t, r) => e.set(t, r),
removeItem: (t) => e.delete(t),
clear: () => e.clear(),
get length() {
return e.size;
},
key: (t) => Array.from(e.keys())[t] ?? null
};
}
const se = (e) => {
if (e.logLevel === void 0 && !e.logger)
return;
const t = e.logger ?? (e.logLevel !== void 0 ? Y : void 0), r = e.logLevel ?? f.NONE;
t && (m.setLogger({
error: t.error,
warn: t.warn,
info: t.info,
debug: t.debug
}), m.setLevel(ie(r)));
}, ie = (e) => {
switch (e) {
case f.NONE:
return m.NONE;
case f.ERROR:
return m.ERROR;
case f.WARN:
return m.WARN;
case f.INFO:
return m.INFO;
case f.DEBUG:
return m.DEBUG;
default:
return m.NONE;
}
}, A = /* @__PURE__ */ new Map(), le = (e) => {
try {
const { userManager: t, eventCallbacks: r } = e;
r.userLoaded && t.events.removeUserLoaded(r.userLoaded), r.userUnloaded && t.events.removeUserUnloaded(r.userUnloaded), r.accessTokenExpired && t.events.removeAccessTokenExpired(
r.accessTokenExpired
), r.accessTokenExpiring && t.events.removeAccessTokenExpiring(
r.accessTokenExpiring
), r.silentRenewError && t.events.removeSilentRenewError(
r.silentRenewError
), t.stopSilentRenew(), t.clearStaleState();
} catch (t) {
console.warn("Error during UserManager cleanup:", t);
}
}, j = (e) => `${e.authority}_${e.clientId}`, oe = () => A.size === 0 ? null : (A.size === 1 || console.warn(
"Multiple auth instances detected. Callback components may not work as expected. Consider using a single auth instance or specify config explicitly."
), Array.from(A.values())[0] || null), ue = (e) => {
const t = j(e), r = A.get(t);
if (r)
return r;
const n = ce(e), a = E(!1), c = E(!1), s = E(null), h = E(!1), o = E(null), b = E(null), g = N(() => !!s.value && !s.value.expired), u = N(() => {
var y;
return ((y = s.value) == null ? void 0 : y.access_token) || null;
}), d = N(() => {
var y;
return !!((y = s.value) != null && y.expired);
}), w = de(e), z = {
userManager: n,
isInitialized: a,
isInitializing: c,
user: s,
isLoading: h,
initError: o,
authError: b,
isAuthenticated: g,
accessToken: u,
isExpired: d,
logger: w,
eventCallbacks: {},
initializationPromise: null
};
return A.set(t, z), z;
}, ce = (e) => {
const t = ae(e);
return new ee(t);
}, de = (e) => {
if (!e.logger && !e.logLevel)
return te;
const t = e.logger ?? Y, r = e.logLevel ?? f.NONE;
return re(t, r);
}, C = async (e) => {
await e.userManager.removeUser(), e.user.value = null;
}, D = (e, t, r, n) => {
const a = e instanceof Error ? e : new Error(t);
return r.initError.value = a, n == null || n(a), a;
}, L = (e, t, r, n) => {
const a = e instanceof Error ? e : new Error(t);
return r.authError.value = a, n == null || n(a), a;
}, ge = (e) => {
var t, r;
if (!((t = e.authority) != null && t.trim()))
throw new Error("AuthConfig: authority is required");
if (!((r = e.clientId) != null && r.trim()))
throw new Error("AuthConfig: clientId is required");
try {
new URL(e.authority);
} catch {
throw new Error("AuthConfig: authority must be a valid URL");
}
}, P = async (e, t = 1e4) => e.initializationPromise ? e.initializationPromise : e.isInitialized.value ? Promise.resolve() : new Promise((r, n) => {
const a = setTimeout(() => {
n(new Error("Authentication initialization timeout"));
}, t), c = X(
e.isInitialized,
(s) => {
s && (clearTimeout(a), c(), r());
},
{ immediate: !0 }
);
}), he = (e) => {
const t = window.location.href;
return !!(e.silentRedirectUri && t.startsWith(e.silentRedirectUri) || e.redirectUri && t.startsWith(e.redirectUri) || e.postLogoutRedirectUri && t.startsWith(e.postLogoutRedirectUri));
}, R = (e) => {
e.initError.value = null, e.authError.value = null;
}, ve = (e) => {
ge(e);
const t = ue(e), {
userManager: r,
isInitialized: n,
isInitializing: a,
user: c,
isLoading: s,
initError: h,
authError: o,
isAuthenticated: b,
accessToken: g,
isExpired: u
} = t, d = j(e), w = async () => {
if (!(a.value || n.value))
try {
a.value = !0, s.value = !0, R(t);
const i = await r.getUser();
if (i)
if (!i.expired)
c.value = i, t.logger.info(
"[Vuethenticate] useAuth: User restored from storage"
);
else if (e.automaticSilentRenew !== !1) {
t.logger.info(
"[Vuethenticate] useAuth: Token expired, attempting silent renewal"
);
try {
await $();
} catch (l) {
t.logger.warn(
"[Vuethenticate] useAuth: Silent renewal failed during initialization",
l
), await C(t);
}
} else
await r.removeUser(), c.value = null;
n.value = !0;
} catch (i) {
throw t.logger.error(
"[Vuethenticate] useAuth: Initialization failed",
i
), D(
i,
"Authentication initialization failed",
t,
e.onError
), i;
} finally {
a.value = !1, s.value = !1;
}
}, z = async (i) => {
try {
s.value = !0, R(t), t.logger.info("[Vuethenticate] useAuth: Starting sign-in flow"), await r.signinRedirect({ state: i });
} catch (l) {
t.logger.error("[Vuethenticate] useAuth: Sign-in failed", l), L(l, "Sign in failed", t, e.onError);
} finally {
s.value = !1;
}
}, y = async (i) => {
try {
s.value = !0, R(t), c.value = null, t.logger.info(
"[Vuethenticate] useAuth: Starting sign-out flow"
), await r.signoutRedirect({ state: i });
} catch (l) {
t.logger.error("[Vuethenticate] useAuth: Sign-out failed", l), L(l, "Sign out failed", t, e.onError);
} finally {
s.value = !1;
}
}, $ = async () => {
try {
s.value = !0, R(t);
const i = await r.signinSilent();
c.value = i;
} catch (i) {
throw L(
i,
"Silent renewal failed",
t,
e.onError
);
} finally {
s.value = !1;
}
}, H = () => {
R(t);
}, J = async () => {
try {
await C(t), t.logger.info(
"[Vuethenticate] useAuth: Authentication state reset"
);
} catch (i) {
t.logger.error(
"[Vuethenticate] useAuth: Failed to reset authentication state",
i
), D(
i,
"Failed to reset authentication state",
t,
e.onError
);
}
}, _ = (i, l) => {
const { userManager: U, user: W, eventCallbacks: p } = i;
p.userLoaded = (k) => {
var I;
W.value = k, (I = l.onUserLoaded) == null || I.call(l, k);
}, p.userUnloaded = () => {
var k;
W.value = null, (k = l.onUserUnloaded) == null || k.call(l);
}, p.accessTokenExpired = () => {
var k;
(k = l.onAccessTokenExpired) == null || k.call(l);
}, p.accessTokenExpiring = () => {
var k;
(k = l.onAccessTokenExpiring) == null || k.call(l);
}, p.silentRenewError = async (k) => {
i.logger.warn("Silent renewal failed:", k);
try {
await C(i);
} catch (I) {
i.logger.error(
"Failed to remove user after silent renewal error:",
I
);
}
L(
k,
"Silent renewal failed",
i,
l.onSilentRenewError
);
}, U.events.addUserLoaded(p.userLoaded), U.events.addUserUnloaded(p.userUnloaded), U.events.addAccessTokenExpired(
p.accessTokenExpired
), U.events.addAccessTokenExpiring(
p.accessTokenExpiring
), U.events.addSilentRenewError(p.silentRenewError);
}, Q = () => {
le(t), A.delete(d);
};
return t.initializationPromise || (!n.value && !a.value && !he(e) ? (t.initializationPromise = w(), _(t, e)) : (!n.value && !a.value && (_(t, e), n.value = !0), t.initializationPromise = Promise.resolve())), {
user: c,
isAuthenticated: b,
isLoading: s,
isInitialized: n,
initError: h,
authError: o,
accessToken: g,
isExpired: u,
signIn: z,
signOut: y,
silentRenew: $,
clearError: H,
resetAuthState: J,
cleanup: Q,
initializationPromise: t.initializationPromise
};
}, F = () => {
const e = oe();
if (!e)
throw new Error(
"Authentication not initialized. Make sure useAuth() is called in a parent component first."
);
return {
processCallback: async () => {
try {
await P(e), e.logger.info(
"[Vuethenticate] useAuthCallbacks: Processing authentication callback"
);
const a = await e.userManager.signinCallback();
if (!a)
throw new Error(
"Authentication callback failed: no user returned"
);
return e.logger.info(
"[Vuethenticate] useAuthCallbacks: Authentication callback successful"
), {
user: a,
state: a.state
};
} catch (a) {
throw e.logger.error(
"[Vuethenticate] useAuthCallbacks: Authentication callback failed",
a
), await C(e), a;
}
},
processLogoutCallback: async () => {
try {
await P(e), e.logger.debug(
"[Vuethenticate] useAuthCallbacks: Processing logout callback"
);
const a = await e.userManager.signoutCallback();
return e.logger.info(
"[Vuethenticate] useAuthCallbacks: Logout callback completed successfully"
), {
state: a == null ? void 0 : a.userState
};
} catch (a) {
throw e.logger.error(
"[Vuethenticate] useAuthCallbacks: Logout callback failed",
a
), await C(e), a;
}
},
processSilentCallback: async () => {
try {
await P(e), e.logger.debug(
"[Vuethenticate] useAuthCallbacks: Processing silent callback"
), await e.userManager.signinCallback(), e.logger.debug(
"[Vuethenticate] useAuthCallbacks: Silent callback completed successfully"
);
} catch (a) {
throw e.logger.error(
"[Vuethenticate] useAuthCallbacks: Silent callback failed",
a
), await C(e), a;
}
},
isInitialized: e.isInitialized,
isInitializing: e.isInitializing,
logger: e.logger
};
}, be = /* @__PURE__ */ T({
__name: "AuthCallback",
props: {
onSuccess: { type: Function },
onError: { type: Function }
},
emits: ["success", "error"],
setup(e, { emit: t }) {
const r = e, n = t, { processCallback: a, isInitializing: c, logger: s } = F(), h = E(!0), o = E(null), b = async () => {
var g, u;
try {
s.info(
"[Vuethenticate] AuthCallback: Processing authentication callback"
);
const d = await a();
s.info(
"[Vuethenticate] AuthCallback: Authentication callback completed successfully"
), (g = r.onSuccess) == null || g.call(r, d.user, d.state), n("success", d.user, d.state);
} catch (d) {
const w = d instanceof Error ? d : new Error("Callback processing failed");
s.error(
"[Vuethenticate] AuthCallback: Authentication callback failed",
w
), o.value = w, (u = r.onError) == null || u.call(r, w), n("error", w);
} finally {
h.value = !1;
}
};
return M(() => {
b();
}), (g, u) => (K(), G(q, null, [
(x(c) || h.value) && !o.value ? S(g.$slots, "default", { key: 0 }, () => [
u[0] || (u[0] = v("p", null, "Processing authentication...", -1))
]) : V("", !0),
o.value ? S(g.$slots, "error", {
key: 1,
error: o.value
}, () => [
v("div", null, [
u[1] || (u[1] = v("h2", null, "Authentication Error", -1)),
v("p", null, O(o.value.message), 1)
])
]) : V("", !0)
], 64));
}
}), Ee = /* @__PURE__ */ T({
__name: "LogoutCallback",
props: {
onSuccess: { type: Function },
onError: { type: Function }
},
emits: ["success", "error"],
setup(e, { emit: t }) {
const r = e, n = t, { processLogoutCallback: a, isInitializing: c, logger: s } = F(), h = E(!0), o = E(null), b = async () => {
var g, u;
try {
s.info(
"[Vuethenticate] LogoutCallback: Processing logout callback"
);
const d = await a();
s.info(
"[Vuethenticate] LogoutCallback: Logout callback completed successfully"
), (g = r.onSuccess) == null || g.call(r, d.state), n("success", d.state);
} catch (d) {
const w = d instanceof Error ? d : new Error("Logout callback processing failed");
s.error(
"[Vuethenticate] LogoutCallback: Logout callback failed",
w
), o.value = w, (u = r.onError) == null || u.call(r, w), n("error", w);
} finally {
h.value = !1;
}
};
return M(() => {
b();
}), (g, u) => (x(c) || h.value) && !o.value ? S(g.$slots, "default", { key: 0 }, () => [
u[0] || (u[0] = v("p", null, "Processing logout...", -1))
]) : o.value ? S(g.$slots, "error", {
key: 1,
error: o.value
}, () => [
v("div", null, [
u[1] || (u[1] = v("h2", null, "Logout Error", -1)),
v("p", null, O(o.value.message), 1)
])
]) : S(g.$slots, "success", { key: 2 }, () => [
u[2] || (u[2] = v("div", null, [
v("h2", null, "Logout Successful"),
v("p", null, "You have been logged out successfully.")
], -1))
]);
}
}), pe = /* @__PURE__ */ T({
__name: "SilentCallback",
props: {
onError: { type: Function }
},
setup(e) {
const t = e, { processSilentCallback: r, isInitializing: n, logger: a } = F(), c = E(null), s = async () => {
var h;
try {
a.debug(
"[Vuethenticate] SilentCallback: Processing silent callback"
), await r();
} catch (o) {
const b = o instanceof Error ? o : new Error("Silent callback processing failed");
a.error(
"[Vuethenticate] SilentCallback: Silent callback failed",
b
), c.value = b, (h = t.onError) == null || h.call(t, b);
}
};
return M(() => {
s();
}), (h, o) => (K(), G(q, null, [
x(n) && !c.value ? S(h.$slots, "default", { key: 0 }, () => [
o[0] || (o[0] = v("p", null, "Processing silent renewal...", -1))
]) : V("", !0),
c.value ? S(h.$slots, "error", {
key: 1,
error: c.value
}, () => [
v("p", null, "Silent renewal error: " + O(c.value.message), 1)
]) : V("", !0)
], 64));
}
});
export {
be as AuthCallback,
f as LogLevel,
Ee as LogoutCallback,
pe as SilentCallback,
Y as consoleLogger,
te as silentLogger,
ve as useAuth
};