signup and loginbasics

This commit is contained in:
Timur Ercan
2023-01-10 18:52:04 +01:00
parent 93d96c3768
commit 58fbaab935
8 changed files with 671 additions and 142 deletions

101
packages/lib/auth.ts Normal file
View File

@ -0,0 +1,101 @@
import { compare, hash } from "bcryptjs";
import type { NextApiRequest } from "next";
import type { Session } from "next-auth";
import {
getSession as getSessionInner,
GetSessionParams,
} from "next-auth/react";
import { HttpError } from "@documenso/lib/server";
export async function hashPassword(password: string) {
const hashedPassword = await hash(password, 12);
return hashedPassword;
}
export async function verifyPassword(password: string, hashedPassword: string) {
const isValid = await compare(password, hashedPassword);
return isValid;
}
export function validPassword(password: string) {
if (password.length < 7) return false;
if (!/[A-Z]/.test(password) || !/[a-z]/.test(password)) return false;
if (!/\d+/.test(password)) return false;
return true;
}
export async function getSession(
options: GetSessionParams
): Promise<Session | null> {
const session = await getSessionInner(options);
// that these are equal are ensured in `[...nextauth]`'s callback
return session as Session | null;
}
export function isPasswordValid(password: string): boolean;
export function isPasswordValid(
password: string,
breakdown: boolean,
strict?: boolean
): { caplow: boolean; num: boolean; min: boolean; admin_min: boolean };
export function isPasswordValid(
password: string,
breakdown?: boolean,
strict?: boolean
) {
let cap = false, // Has uppercase characters
low = false, // Has lowercase characters
num = false, // At least one number
min = false, // Eight characters, or fifteen in strict mode.
admin_min = false;
if (password.length > 7 && (!strict || password.length > 14)) min = true;
if (strict && password.length > 14) admin_min = true;
for (let i = 0; i < password.length; i++) {
if (!isNaN(parseInt(password[i]))) num = true;
else {
if (password[i] === password[i].toUpperCase()) cap = true;
if (password[i] === password[i].toLowerCase()) low = true;
}
}
if (!breakdown)
return cap && low && num && min && (strict ? admin_min : true);
let errors: Record<string, boolean> = { caplow: cap && low, num, min };
// Only return the admin key if strict mode is enabled.
if (strict) errors = { ...errors, admin_min };
return errors;
}
type CtxOrReq =
| { req: NextApiRequest; ctx?: never }
| { ctx: { req: NextApiRequest }; req?: never };
export const ensureSession = async (ctxOrReq: CtxOrReq) => {
const session = await getSession(ctxOrReq);
if (!session?.user)
throw new HttpError({ statusCode: 401, message: "Unauthorized" });
return session;
};
export enum ErrorCode {
UserNotFound = "user-not-found",
IncorrectPassword = "incorrect-password",
UserMissingPassword = "missing-password",
TwoFactorDisabled = "two-factor-disabled",
TwoFactorAlreadyEnabled = "two-factor-already-enabled",
TwoFactorSetupRequired = "two-factor-setup-required",
SecondFactorRequired = "second-factor-required",
IncorrectTwoFactorCode = "incorrect-two-factor-code",
InternalServerError = "internal-server-error",
NewPasswordMatchesOld = "new-password-matches-old",
ThirdPartyIdentityProviderEnabled = "third-party-identity-provider-enabled",
RateLimitExceeded = "rate-limit-exceeded",
SocialIdentityProviderRequired = "social-identity-provider-required",
}

View File

@ -3,6 +3,7 @@
"version": "0.0.0",
"private": true,
"main": "index.ts",
"devDependencies": {},
"dependencies": {}
"dependencies": {
"bcryptjs": "^2.4.3"
}
}

View File

@ -26,12 +26,48 @@ model Recipient {
}
model User {
id Int @id @default(autoincrement())
firstName String? @db.VarChar(255)
lastName String? @db.VarChar(255)
email String @unique @db.VarChar(255)
password String? @db.VarChar(255)
Document Document[]
id Int @id @default(autoincrement())
username String? @unique
name String?
email String @unique
emailVerified DateTime?
password String?
identityProvider IdentityProvider @default(DOCUMENSO)
accounts Account[]
sessions Session[]
Document Document[]
}
model Account {
id String @id @default(cuid())
userId Int
type String
provider String
providerAccountId String
refresh_token String? @db.Text
access_token String? @db.Text
expires_at Int?
token_type String?
scope String?
id_token String? @db.Text
session_state String?
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}
model Session {
id String @id @default(cuid())
sessionToken String @unique
userId Int
expires DateTime
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
}
enum IdentityProvider {
DOCUMENSO
GOOGLE
}
enum ReadStatus {

20
packages/types/next-auth.d.ts vendored Normal file
View File

@ -0,0 +1,20 @@
import { User as PrismaUser } from "@prisma/client";
import { DefaultUser } from "next-auth";
declare module "next-auth" {
/**
* Returned by `useSession`, `getSession` and received as a prop on the `Provider` React Context
*/
interface Session {
hasValidLicense: boolean;
user: User;
}
interface User extends Omit<DefaultUser, "id"> {
id: PrismaUser["id"];
emailVerified?: PrismaUser["emailVerified"];
email_verified?: boolean;
impersonatedByUID?: number;
belongsToActiveTeam?: boolean;
username?: PrismaUser["username"];
}
}