UNPKG

@withstudiocms/auth-kit

Version:

Utilities for managing authentication

170 lines (169 loc) 5.97 kB
import { Effect } from "@withstudiocms/effect"; import { UserError, useSessionError, useUserError, useUserErrorPromise } from "../errors.js"; import { UserPermissionLevel } from "../types.js"; import libravatar from "../utils/libravatar.js"; import { getDefaultUserSession, getLevel, parseRequiredPerms, verifyUsernameCharacters, verifyUsernameLength, verifyUsernameSafe } from "../utils/user.js"; import { Password as _Password } from "./password.js"; import { Session as _Session } from "./session.js"; const User = ({ Scrypt, session, userTools }) => Effect.gen(function* () { const [Password, Session] = yield* Effect.all([_Password(Scrypt), _Session(session)]); if (!userTools) { return yield* Effect.fail(new UserError({ cause: "User tools are not available" })); } const notifier = userTools.notifier; const verifyUsernameInput = Effect.fn( "@withstudiocms/AuthKit/modules/user.verifyUsernameInput" )(function* (username) { const [testLength, usernameChars, safeCheck] = yield* Effect.all([ verifyUsernameLength(username), verifyUsernameCharacters(username), verifyUsernameSafe(username) ]); if (testLength) return testLength; if (usernameChars) return usernameChars; if (safeCheck) return safeCheck; return true; }); const createUserAvatar = Effect.fn("@withstudiocms/AuthKit/modules/user.createUserAvatar")( (email) => useUserErrorPromise( async () => libravatar.getAvatarUrl({ email, https: true, size: 400, default: "retro" }) ) ); const createLocalUser = Effect.fn("@withstudiocms/AuthKit/modules/user.createLocalUser")( function* (name, username, email, password) { const [passwordHash, avatar, id] = yield* Effect.all([ Password.hashPassword(password), createUserAvatar(email), useUserError(() => userTools.idGenerator()) ]); const createdAt = /* @__PURE__ */ new Date(); const createdUser = yield* useUserErrorPromise( () => userTools.createLocalUser({ id, name, username, email, createdAt, avatar, updatedAt: createdAt, password: passwordHash, emailVerified: false, notifications: null, url: null }) ); if (notifier) { yield* useUserErrorPromise(() => notifier.admin("new_user", createdUser.username)); } return createdUser; } ); const createOAuthUser = Effect.fn("@withstudiocms/AuthKit/modules/user.createOAuthUser")( function* (data, oAuthFields) { const createdUser = yield* useUserErrorPromise(() => userTools.createLocalUser(data)); yield* useUserErrorPromise( () => userTools.createOAuthUser({ userId: createdUser.id, ...oAuthFields }) ); if (notifier) { yield* useUserErrorPromise(() => notifier.admin("new_user", createdUser.username)); } return createdUser; } ); const updateUserPassword = Effect.fn("@withstudiocms/AuthKit/modules/user.updateUserPassword")( function* (userId, password) { const newHash = yield* Password.hashPassword(password); return yield* useUserErrorPromise( () => userTools.updateLocalUser(userId, { password: newHash }) ); } ); const getUserPasswordHash = Effect.fn( "@withstudiocms/AuthKit/modules/user.getUserPasswordHash" )(function* (userId) { const user = yield* useUserErrorPromise(() => userTools.getUserById(userId)); if (!user) return yield* Effect.fail(new UserError({ cause: "User not found" })); if (!user.password) return yield* Effect.fail(new UserError({ cause: "User has no password" })); return user.password; }); const getUserFromEmail = Effect.fn("@withstudiocms/AuthKit/modules/user.getUserFromEmail")( (email) => useUserErrorPromise(() => userTools.getUserByEmail(email)) ); const getUserData = Effect.fn("@withstudiocms/AuthKit/modules/user.getUserData")(function* (context) { const sessionToken = yield* useSessionError( () => context.cookies.get(session.cookieName)?.value ); if (!sessionToken) { return yield* getDefaultUserSession(); } const { session: ses, user } = yield* Session.validateSessionToken(sessionToken); if (!ses || !user) { yield* Session.deleteSessionTokenCookie(context); return yield* getDefaultUserSession(); } const rankToPermissionLevel = { owner: "owner", admin: "admin", editor: "editor", visitor: "visitor" }; const result = yield* useUserErrorPromise(() => userTools.getCurrentPermissions(user.id)); const permissionLevel = result && rankToPermissionLevel[result.rank] || "unknown"; const out = { isLoggedIn: true, user, permissionLevel }; return out; }); const getUserPermissionLevel = Effect.fn( "@withstudiocms/AuthKit/modules/user.getUserPermissionLevel" )(function* (userData) { const level = yield* getLevel(userData); switch (level) { case "owner": return UserPermissionLevel.owner; case "admin": return UserPermissionLevel.admin; case "editor": return UserPermissionLevel.editor; case "visitor": return UserPermissionLevel.visitor; default: return UserPermissionLevel.unknown; } }); const isUserAllowed = Effect.fn("@withstudiocms/AuthKit/modules/user.isUserAllowed")(function* (userData, requiredPerms) { const [userLevel, neededLevel] = yield* Effect.all([ getUserPermissionLevel(userData), parseRequiredPerms(requiredPerms) ]); return userLevel >= neededLevel; }); return { verifyUsernameInput, createUserAvatar, createLocalUser, createOAuthUser, updateUserPassword, getUserPasswordHash, getUserFromEmail, getUserData, getUserPermissionLevel, isUserAllowed }; }); export { User };