@withstudiocms/auth-kit
Version:
Utilities for managing authentication
170 lines (169 loc) • 5.97 kB
JavaScript
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
};