@xsprtd/nuxt-api
Version:
Nuxt API Authentication and Http Client
125 lines (124 loc) • 3.75 kB
JavaScript
import { useApiOptions } from "../composables/useApiOptions.js";
import { useErrorBag } from "../composables/useErrorBag.js";
import { useTokenStorage } from "../composables/useTokenStorage.js";
import { useCookie, useRequestHeaders, useRequestURL } from "#app";
const getCredentials = () => {
return "credentials" in Request.prototype ? "include" : void 0;
};
const fetchCsrfCookie = async (config) => {
try {
await $fetch(config.endpoints.csrf, {
baseURL: config.apiBaseURL,
credentials: "include"
});
} catch (error) {
console.error("Failed to initialize CSRF cookie", error);
}
};
const attachCsrfHeader = async (headers, config) => {
let csrfToken = useCookie(config.csrf.cookieName, { readonly: true });
if (!csrfToken.value) {
await fetchCsrfCookie(config);
csrfToken = useCookie(config.csrf.cookieName, { readonly: true });
}
if (!csrfToken.value) {
console.warn(
`Unable to set ${config.csrf.headerName} header`
);
return headers ?? {};
}
return {
...headers,
[config.csrf.headerName]: csrfToken.value
};
};
const attachServerHeaders = (headers, config) => {
const clientCookies = useRequestHeaders(["cookie"]);
const origin = config.originUrl ?? useRequestURL().origin;
return {
...headers,
Referer: origin,
Origin: origin,
...clientCookies
};
};
const attachToken = async (headers) => {
const token = await useTokenStorage().get();
if (!token) {
console.debug("Authentication token is not set in the storage");
return headers;
}
return {
...headers,
Authorization: `Bearer ${token}`
};
};
const prepareContext = async (context, config) => {
const method = context.options.method?.toLowerCase() ?? "get";
context.options.headers = new Headers({
Accept: "application/json",
...context.options.headers instanceof Headers ? Object.fromEntries(context.options.headers.entries()) : context.options.headers
});
if (context.options.body instanceof FormData) {
context.options.method = "POST";
context.options.body.append("_method", method.toUpperCase());
}
if (config.authMode === "cookie") {
if (import.meta.server) {
context.options.headers = new Headers(
attachServerHeaders(
Object.fromEntries(context.options.headers.entries()),
config
)
);
}
if (["post", "delete", "put", "patch"].includes(method)) {
context.options.headers = new Headers(
await attachCsrfHeader(
Object.fromEntries(context.options.headers.entries()),
config
)
);
}
} else if (config.authMode === "token") {
context.options.headers = new Headers(
await attachToken(
Object.fromEntries(context.options.headers.entries())
)
);
}
};
export default (options) => {
const config = useApiOptions();
options ||= {};
return {
baseURL: config.apiBaseURL,
credentials: getCredentials(),
redirect: "manual",
retry: config.fetchOptions.retryAttempts,
onRequest: async (context) => {
if (options.onRequest) {
if (Array.isArray(options.onRequest)) {
for (const hook of options.onRequest) {
await hook(context);
}
} else {
await options.onRequest(context);
}
}
await prepareContext(context, config);
},
onResponseError: async (context) => {
useErrorBag().handle(context);
if (options.onResponseError) {
if (Array.isArray(options.onResponseError)) {
for (const hook of options.onResponseError) {
await hook(context);
}
} else {
await options.onResponseError(context);
}
}
}
};
};