@auth/mongodb-adapter
Version:
MongoDB adapter for Auth.js
422 lines (421 loc) • 15.8 kB
JavaScript
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
if (value !== null && value !== void 0) {
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
var dispose;
if (async) {
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
dispose = value[Symbol.asyncDispose];
}
if (dispose === void 0) {
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
dispose = value[Symbol.dispose];
}
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
env.stack.push({ value: value, dispose: dispose, async: async });
}
else if (async) {
env.stack.push({ async: true });
}
return value;
};
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
return function (env) {
function fail(e) {
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
env.hasError = true;
}
function next() {
while (env.stack.length) {
var rec = env.stack.pop();
try {
var result = rec.dispose && rec.dispose.call(rec.value);
if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
}
catch (e) {
fail(e);
}
}
if (env.hasError) throw env.error;
}
return next();
};
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
});
/**
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center", padding: 16}}>
* <p>Official <a href="https://www.mongodb.com">MongoDB</a> adapter for Auth.js / NextAuth.js.</p>
* <a href="https://www.mongodb.com">
* <img style={{display: "block"}} src="https://authjs.dev/img/adapters/mongodb.svg" width="30" />
* </a>
* </div>
*
* ## Installation
*
* ```bash npm2yarn
* npm install @auth/mongodb-adapter mongodb
* ```
*
* @module @auth/mongodb-adapter
*/
import { ObjectId } from "mongodb";
/**
* This adapter uses https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management.
* This feature is very new and requires runtime polyfills for `Symbol.asyncDispose` in order to work properly in all environments.
* It is also required to set in the `tsconfig.json` file the compilation target to `es2022` or below and configure the `lib` option to include `esnext` or `esnext.disposable`.
*
* You can find more information about this feature and the polyfills in the link above.
*/
// @ts-expect-error read only property is not assignable
Symbol.asyncDispose ?? (Symbol.asyncDispose = Symbol("Symbol.asyncDispose"));
export const defaultCollections = {
Users: "users",
Accounts: "accounts",
Sessions: "sessions",
VerificationTokens: "verification_tokens",
};
export const format = {
/** Takes a MongoDB object and returns a plain old JavaScript object */
from(object) {
const newObject = {};
for (const key in object) {
const value = object[key];
if (key === "_id") {
newObject.id = value.toHexString();
}
else if (key === "userId") {
newObject[key] = value.toHexString();
}
else {
newObject[key] = value;
}
}
return newObject;
},
/** Takes a plain old JavaScript object and turns it into a MongoDB object */
to(object) {
const newObject = {
_id: _id(object.id),
};
for (const key in object) {
const value = object[key];
if (key === "userId")
newObject[key] = _id(value);
else if (key === "id")
continue;
else
newObject[key] = value;
}
return newObject;
},
};
/** @internal */
export function _id(hex) {
if (hex?.length !== 24)
return new ObjectId();
return new ObjectId(hex);
}
export function MongoDBAdapter(
/**
* The MongoDB client.
*
* The MongoDB team recommends providing a non-connected `MongoClient` instance to avoid unhandled promise rejections if the client fails to connect.
*
* Alternatively, you can also pass:
* - A promise that resolves to a connected `MongoClient` (not recommended).
* - A function, to handle more complex and custom connection strategies.
*
* Using a function combined with `options.onClose`, can be useful when you want a more advanced and customized connection strategy to address challenges related to persistence, container reuse, and connection closure.
*/
client, options = {}) {
const { collections } = options;
const { from, to } = format;
const getDb = async () => {
const _client = await (typeof client === "function"
? client()
: client);
const _db = _client.db(options.databaseName);
const c = { ...defaultCollections, ...collections };
return {
U: _db.collection(c.Users),
A: _db.collection(c.Accounts),
S: _db.collection(c.Sessions),
V: _db.collection(c?.VerificationTokens),
[Symbol.asyncDispose]: async () => {
await options.onClose?.(_client);
},
};
};
return {
async createUser(data) {
const env_1 = { stack: [], error: void 0, hasError: false };
try {
const user = to(data);
const db = __addDisposableResource(env_1, await getDb(), true);
await db.U.insertOne(user);
return from(user);
}
catch (e_1) {
env_1.error = e_1;
env_1.hasError = true;
}
finally {
const result_1 = __disposeResources(env_1);
if (result_1)
await result_1;
}
},
async getUser(id) {
const env_2 = { stack: [], error: void 0, hasError: false };
try {
const db = __addDisposableResource(env_2, await getDb(), true);
const user = await db.U.findOne({ _id: _id(id) });
if (!user)
return null;
return from(user);
}
catch (e_2) {
env_2.error = e_2;
env_2.hasError = true;
}
finally {
const result_2 = __disposeResources(env_2);
if (result_2)
await result_2;
}
},
async getUserByEmail(email) {
const env_3 = { stack: [], error: void 0, hasError: false };
try {
const db = __addDisposableResource(env_3, await getDb(), true);
const user = await db.U.findOne({ email });
if (!user)
return null;
return from(user);
}
catch (e_3) {
env_3.error = e_3;
env_3.hasError = true;
}
finally {
const result_3 = __disposeResources(env_3);
if (result_3)
await result_3;
}
},
async getUserByAccount(provider_providerAccountId) {
const env_4 = { stack: [], error: void 0, hasError: false };
try {
const db = __addDisposableResource(env_4, await getDb(), true);
const account = await db.A.findOne(provider_providerAccountId);
if (!account)
return null;
const user = await db.U.findOne({ _id: new ObjectId(account.userId) });
if (!user)
return null;
return from(user);
}
catch (e_4) {
env_4.error = e_4;
env_4.hasError = true;
}
finally {
const result_4 = __disposeResources(env_4);
if (result_4)
await result_4;
}
},
async updateUser(data) {
const env_5 = { stack: [], error: void 0, hasError: false };
try {
const { _id, ...user } = to(data);
const db = __addDisposableResource(env_5, await getDb(), true);
const result = await db.U.findOneAndUpdate({ _id }, { $set: user }, { returnDocument: "after" });
return from(result);
}
catch (e_5) {
env_5.error = e_5;
env_5.hasError = true;
}
finally {
const result_5 = __disposeResources(env_5);
if (result_5)
await result_5;
}
},
async deleteUser(id) {
const env_6 = { stack: [], error: void 0, hasError: false };
try {
const userId = _id(id);
const db = __addDisposableResource(env_6, await getDb(), true);
await Promise.all([
db.A.deleteMany({ userId: userId }),
db.S.deleteMany({ userId: userId }),
db.U.deleteOne({ _id: userId }),
]);
}
catch (e_6) {
env_6.error = e_6;
env_6.hasError = true;
}
finally {
const result_6 = __disposeResources(env_6);
if (result_6)
await result_6;
}
},
linkAccount: async (data) => {
const env_7 = { stack: [], error: void 0, hasError: false };
try {
const account = to(data);
const db = __addDisposableResource(env_7, await getDb(), true);
await db.A.insertOne(account);
return account;
}
catch (e_7) {
env_7.error = e_7;
env_7.hasError = true;
}
finally {
const result_7 = __disposeResources(env_7);
if (result_7)
await result_7;
}
},
async unlinkAccount(provider_providerAccountId) {
const env_8 = { stack: [], error: void 0, hasError: false };
try {
const db = __addDisposableResource(env_8, await getDb(), true);
const account = await db.A.findOneAndDelete(provider_providerAccountId);
return from(account);
}
catch (e_8) {
env_8.error = e_8;
env_8.hasError = true;
}
finally {
const result_8 = __disposeResources(env_8);
if (result_8)
await result_8;
}
},
async getSessionAndUser(sessionToken) {
const env_9 = { stack: [], error: void 0, hasError: false };
try {
const db = __addDisposableResource(env_9, await getDb(), true);
const session = await db.S.findOne({ sessionToken });
if (!session)
return null;
const user = await db.U.findOne({ _id: new ObjectId(session.userId) });
if (!user)
return null;
return {
user: from(user),
session: from(session),
};
}
catch (e_9) {
env_9.error = e_9;
env_9.hasError = true;
}
finally {
const result_9 = __disposeResources(env_9);
if (result_9)
await result_9;
}
},
async createSession(data) {
const env_10 = { stack: [], error: void 0, hasError: false };
try {
const session = to(data);
const db = __addDisposableResource(env_10, await getDb(), true);
await db.S.insertOne(session);
return from(session);
}
catch (e_10) {
env_10.error = e_10;
env_10.hasError = true;
}
finally {
const result_10 = __disposeResources(env_10);
if (result_10)
await result_10;
}
},
async updateSession(data) {
const env_11 = { stack: [], error: void 0, hasError: false };
try {
const { _id, ...session } = to(data);
const db = __addDisposableResource(env_11, await getDb(), true);
const updatedSession = await db.S.findOneAndUpdate({ sessionToken: session.sessionToken }, { $set: session }, { returnDocument: "after" });
return from(updatedSession);
}
catch (e_11) {
env_11.error = e_11;
env_11.hasError = true;
}
finally {
const result_11 = __disposeResources(env_11);
if (result_11)
await result_11;
}
},
async deleteSession(sessionToken) {
const env_12 = { stack: [], error: void 0, hasError: false };
try {
const db = __addDisposableResource(env_12, await getDb(), true);
const session = await db.S.findOneAndDelete({
sessionToken,
});
return from(session);
}
catch (e_12) {
env_12.error = e_12;
env_12.hasError = true;
}
finally {
const result_12 = __disposeResources(env_12);
if (result_12)
await result_12;
}
},
async createVerificationToken(data) {
const env_13 = { stack: [], error: void 0, hasError: false };
try {
const db = __addDisposableResource(env_13, await getDb(), true);
await db.V.insertOne(to(data));
return data;
}
catch (e_13) {
env_13.error = e_13;
env_13.hasError = true;
}
finally {
const result_13 = __disposeResources(env_13);
if (result_13)
await result_13;
}
},
async useVerificationToken(identifier_token) {
const env_14 = { stack: [], error: void 0, hasError: false };
try {
const db = __addDisposableResource(env_14, await getDb(), true);
const verificationToken = await db.V.findOneAndDelete(identifier_token);
if (!verificationToken)
return null;
const { _id, ...rest } = verificationToken;
return rest;
}
catch (e_14) {
env_14.error = e_14;
env_14.hasError = true;
}
finally {
const result_14 = __disposeResources(env_14);
if (result_14)
await result_14;
}
},
};
}