@auth/core
Version:
Authentication for the Web.
116 lines (115 loc) • 4.61 kB
JavaScript
import { defaultCookies } from "./cookie.js";
import { InvalidCallbackUrl, InvalidEndpoints, MissingAdapter, MissingAdapterMethods, MissingAuthorize, MissingSecret, UnsupportedStrategy, UntrustedHost, } from "../errors.js";
let warned = false;
function isValidHttpUrl(url, baseUrl) {
try {
return /^https?:/.test(new URL(url, url.startsWith("/") ? baseUrl : undefined).protocol);
}
catch {
return false;
}
}
let hasCredentials = false;
let hasEmail = false;
const emailMethods = [
"createVerificationToken",
"useVerificationToken",
"getUserByEmail",
];
const sessionMethods = [
"createUser",
"getUser",
"getUserByEmail",
"getUserByAccount",
"updateUser",
"linkAccount",
"createSession",
"getSessionAndUser",
"updateSession",
"deleteSession",
];
/**
* Verify that the user configured Auth.js correctly.
* Good place to mention deprecations as well.
*
* This is invoked before the init method, so default values are not available yet.
*/
export function assertConfig(request, options) {
const { url } = request;
const warnings = [];
if (!warned && options.debug)
warnings.push("debug-enabled");
if (!options.trustHost) {
return new UntrustedHost(`Host must be trusted. URL was: ${request.url}`);
}
if (!options.secret) {
return new MissingSecret("Please define a `secret`.");
}
const callbackUrlParam = request.query?.callbackUrl;
if (callbackUrlParam && !isValidHttpUrl(callbackUrlParam, url.origin)) {
return new InvalidCallbackUrl(`Invalid callback URL. Received: ${callbackUrlParam}`);
}
const { callbackUrl: defaultCallbackUrl } = defaultCookies(options.useSecureCookies ?? url.protocol === "https:");
const callbackUrlCookie = request.cookies?.[options.cookies?.callbackUrl?.name ?? defaultCallbackUrl.name];
if (callbackUrlCookie && !isValidHttpUrl(callbackUrlCookie, url.origin)) {
return new InvalidCallbackUrl(`Invalid callback URL. Received: ${callbackUrlCookie}`);
}
for (const p of options.providers) {
const provider = typeof p === "function" ? p() : p;
if ((provider.type === "oauth" || provider.type === "oidc") &&
!(provider.issuer ?? provider.options?.issuer)) {
const { authorization: a, token: t, userinfo: u } = provider;
let key;
if (typeof a !== "string" && !a?.url)
key = "authorization";
else if (typeof t !== "string" && !t?.url)
key = "token";
else if (typeof u !== "string" && !u?.url)
key = "userinfo";
if (key) {
return new InvalidEndpoints(`Provider "${provider.id}" is missing both \`issuer\` and \`${key}\` endpoint config. At least one of them is required.`);
}
}
if (provider.type === "credentials")
hasCredentials = true;
else if (provider.type === "email")
hasEmail = true;
}
if (hasCredentials) {
const dbStrategy = options.session?.strategy === "database";
const onlyCredentials = !options.providers.some((p) => (typeof p === "function" ? p() : p).type !== "credentials");
if (dbStrategy && onlyCredentials) {
return new UnsupportedStrategy("Signin in with credentials only supported if JWT strategy is enabled");
}
const credentialsNoAuthorize = options.providers.some((p) => {
const provider = typeof p === "function" ? p() : p;
return provider.type === "credentials" && !provider.authorize;
});
if (credentialsNoAuthorize) {
return new MissingAuthorize("Must define an authorize() handler to use credentials authentication provider");
}
}
const { adapter, session } = options;
if (hasEmail ||
session?.strategy === "database" ||
(!session?.strategy && adapter)) {
let methods;
if (hasEmail) {
if (!adapter)
return new MissingAdapter("Email login requires an adapter.");
methods = emailMethods;
}
else {
if (!adapter)
return new MissingAdapter("Database session requires an adapter.");
methods = sessionMethods;
}
const missing = methods.filter((m) => !adapter[m]);
if (missing.length) {
return new MissingAdapterMethods(`Required adapter methods were missing: ${missing.join(", ")}`);
}
}
if (!warned)
warned = true;
return warnings;
}