add type-safe `getSession()` (#486)

* fix types for auth
* implement safer to use `getSession`
This commit is contained in:
Alex Johansson 2021-08-18 13:52:25 +02:00 committed by GitHub
parent a162949cf1
commit a0a0ec86f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 130 additions and 92 deletions

View File

@ -1,11 +1,29 @@
import { hash, compare } from 'bcryptjs';
import { compare, hash } from "bcryptjs";
import { DefaultSession } from "next-auth";
import { getSession as getSessionInner, GetSessionOptions } from "next-auth/client";
export async function hashPassword(password) {
const hashedPassword = await hash(password, 12);
return hashedPassword;
export async function hashPassword(password: string) {
const hashedPassword = await hash(password, 12);
return hashedPassword;
}
export async function verifyPassword(password, hashedPassword) {
const isValid = await compare(password, hashedPassword);
return isValid;
}
export async function verifyPassword(password: string, hashedPassword: string) {
const isValid = await compare(password, hashedPassword);
return isValid;
}
type DefaultSessionUser = NonNullable<DefaultSession["user"]>;
type CalendsoSessionUser = DefaultSessionUser & {
id: number;
username: string;
};
interface Session extends DefaultSession {
user?: CalendsoSessionUser;
}
export async function getSession(options: GetSessionOptions): Promise<CalendsoSession | null> {
const session = await getSessionInner(options);
// that these are equal are ensured in `[...nextauth]`'s callback
return session as CalendsoSession;
}

View File

@ -55,6 +55,7 @@
"uuid": "^8.3.2"
},
"devDependencies": {
"@types/bcryptjs": "^2.4.2",
"@types/jest": "^27.0.1",
"@types/node": "^16.6.1",
"@types/nodemailer": "^6.4.4",

View File

@ -1,59 +1,73 @@
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
import prisma from '../../../lib/prisma';
import {verifyPassword} from "../../../lib/auth";
import NextAuth from "next-auth";
import Providers from "next-auth/providers";
import prisma from "../../../lib/prisma";
import { CalendsoSession, verifyPassword } from "../../../lib/auth";
export default NextAuth({
session: {
jwt: true
session: {
jwt: true,
},
pages: {
signIn: "/auth/login",
signOut: "/auth/logout",
error: "/auth/error", // Error code passed in query string as ?error=
},
providers: [
Providers.Credentials({
name: "Calendso",
credentials: {
email: { label: "Email Address", type: "email", placeholder: "john.doe@example.com" },
password: { label: "Password", type: "password", placeholder: "Your super secure password" },
},
async authorize(credentials) {
const user = await prisma.user.findFirst({
where: {
email: credentials.email,
},
});
if (!user) {
throw new Error("No user found");
}
if (!user.password) {
throw new Error("Incorrect password");
}
const isValid = await verifyPassword(credentials.password, user.password);
if (!isValid) {
throw new Error("Incorrect password");
}
return {
id: user.id,
username: user.username,
email: user.email,
name: user.name,
image: user.avatar,
};
},
}),
],
callbacks: {
async jwt(token, user) {
// Add username to the token right after signin
if (user?.username) {
token.id = user.id;
token.username = user.username;
}
return token;
},
pages: {
signIn: '/auth/login',
signOut: '/auth/logout',
error: '/auth/error', // Error code passed in query string as ?error=
},
providers: [
Providers.Credentials({
name: 'Calendso',
credentials: {
email: { label: "Email Address", type: "email", placeholder: "john.doe@example.com" },
password: { label: "Password", type: "password", placeholder: "Your super secure password" }
},
async authorize(credentials) {
const user = await prisma.user.findFirst({
where: {
email: credentials.email
}
});
if (!user) {
throw new Error('No user found');
}
const isValid = await verifyPassword(credentials.password, user.password);
if (!isValid) {
throw new Error('Incorrect password');
}
return {id: user.id, username: user.username, email: user.email, name: user.name, image: user.avatar};
}
})
],
callbacks: {
async jwt(token, user, account, profile, isNewUser) {
// Add username to the token right after signin
if (user?.username) {
token.id = user.id;
token.username = user.username;
}
return token;
},
async session(session, token) {
session.user = session.user || {}
session.user.id = token.id;
session.user.username = token.username;
return session;
async session(session, token) {
const calendsoSession: CalendsoSession = {
...session,
user: {
...session.user,
id: token.id as number,
username: token.username as string,
},
};
return calendsoSession;
},
},
});

View File

@ -1,7 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { hashPassword, verifyPassword } from '../../../lib/auth';
import { getSession } from 'next-auth/client';
import prisma from '../../../lib/prisma';
import type { NextApiRequest, NextApiResponse } from "next";
import { hashPassword, verifyPassword } from "../../../lib/auth";
import { getSession } from "@lib/auth";
import prisma from "../../../lib/prisma";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const session = await getSession({req: req});

View File

@ -1,7 +1,7 @@
import type {NextApiRequest, NextApiResponse} from 'next';
import {getSession} from 'next-auth/client';
import prisma from '../../../lib/prisma';
import {IntegrationCalendar, listCalendars} from "../../../lib/calendarClient";
import type { NextApiRequest, NextApiResponse } from "next";
import { getSession } from "@lib/auth";
import prisma from "../../../lib/prisma";
import { IntegrationCalendar, listCalendars } from "../../../lib/calendarClient";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const session = await getSession({req: req});

View File

@ -1,6 +1,6 @@
import type {NextApiRequest, NextApiResponse} from 'next';
import {getSession} from 'next-auth/client';
import prisma from '../../../lib/prisma';
import type { NextApiRequest, NextApiResponse } from "next";
import { getSession } from "@lib/auth";
import prisma from "../../../lib/prisma";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const session = await getSession({req: req});

View File

@ -1,5 +1,5 @@
import prisma from '../../lib/prisma';
import { getSession } from 'next-auth/client';
import prisma from "../../lib/prisma";
import { getSession } from "@lib/auth";
export default async function handler(req, res) {
if (req.method === 'GET') {

View File

@ -1,7 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { getSession } from 'next-auth/client';
import prisma from '../../../../lib/prisma';
const {google} = require('googleapis');
import type { NextApiRequest, NextApiResponse } from "next";
import { getSession } from "@lib/auth";
import prisma from "../../../../lib/prisma";
const { google } = require("googleapis");
const credentials = process.env.GOOGLE_API_CREDENTIALS;
const scopes = ['https://www.googleapis.com/auth/calendar.readonly', 'https://www.googleapis.com/auth/calendar.events'];

View File

@ -1,7 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { getSession } from 'next-auth/client';
import prisma from '../../../../lib/prisma';
const {google} = require('googleapis');
import type { NextApiRequest, NextApiResponse } from "next";
import { getSession } from "@lib/auth";
import prisma from "../../../../lib/prisma";
const { google } = require("googleapis");
const credentials = process.env.GOOGLE_API_CREDENTIALS;

View File

@ -1,6 +1,6 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { getSession } from 'next-auth/client';
import prisma from '../../../../lib/prisma';
import type { NextApiRequest, NextApiResponse } from "next";
import { getSession } from "@lib/auth";
import prisma from "../../../../lib/prisma";
const scopes = ['User.Read', 'Calendars.Read', 'Calendars.ReadWrite', 'offline_access'];

View File

@ -1,7 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { getSession } from 'next-auth/client';
import prisma from '../../../../lib/prisma';
const scopes = ['offline_access', 'Calendars.Read', 'Calendars.ReadWrite'];
import type { NextApiRequest, NextApiResponse } from "next";
import { getSession } from "@lib/auth";
import prisma from "../../../../lib/prisma";
const scopes = ["offline_access", "Calendars.Read", "Calendars.ReadWrite"];
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { code } = req.query;

View File

@ -1,6 +1,6 @@
import type {NextApiRequest, NextApiResponse} from 'next';
import {getSession} from 'next-auth/client';
import prisma from '../../../../lib/prisma';
import type { NextApiRequest, NextApiResponse } from "next";
import { getSession } from "@lib/auth";
import prisma from "../../../../lib/prisma";
const client_id = process.env.ZOOM_CLIENT_ID;

View File

@ -1151,6 +1151,11 @@
dependencies:
"@babel/types" "^7.3.0"
"@types/bcryptjs@^2.4.2":
version "2.4.2"
resolved "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz#e3530eac9dd136bfdfb0e43df2c4c5ce1f77dfae"
integrity sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==
"@types/graceful-fs@^4.1.2":
version "4.1.5"
resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15"