Checking fully authenticated vs not fully authd vs not authd at all and performing select actions to login, clear cookie, etc.

This commit is contained in:
Bradley Shellnut 2024-06-17 13:06:45 -07:00
parent ad20f88b84
commit 95117cee21
19 changed files with 108 additions and 80 deletions

View file

@ -1,5 +1,8 @@
export const notSignedInMessage = { type: 'error', message: 'You are not signed in' } as const;
export const notSignedInMessage = {
type: 'error',
message: 'You are not signed in',
} as const;
export const forbiddenMessage = {
type: 'error',
message: 'You are not allowed to access this'
message: 'You are not allowed to access this',
} as const;

View file

@ -24,9 +24,13 @@ export async function createPasswordResetToken(userId: string): Promise<string>
* @returns True if the user is not fully authenticated, otherwise false.
*/
export function userNotFullyAuthenticated(user: User | null, session: Session | null) {
console.log(
'userNotFullyAuthenticated?',
user && session && (!session.isTwoFactorAuthEnabled || session.isTwoFactorAuthenticated),
);
return !user || !session || (session.isTwoFactorAuthEnabled && !session.isTwoFactorAuthenticated);
return user && session && session.isTwoFactorAuthEnabled && !session.isTwoFactorAuthenticated;
}
export function userNotAuthenticated(user: User | null, session: Session | null) {
return !user || !session || userNotFullyAuthenticated(user, session);
}
export function userFullyAuthenticated(user: User | null, session: Session | null) {
return !userNotAuthenticated(user, session);
}

View file

@ -2,18 +2,18 @@ import { redirect, loadFlash } from 'sveltekit-flash-message/server';
import { forbiddenMessage, notSignedInMessage } from '$lib/flashMessages';
import { eq } from 'drizzle-orm';
import db from '../../../../db';
import { user_roles } from '$db/schema';
import { userNotFullyAuthenticated } from '$lib/server/auth-utils';
import { userRoles } from '$db/schema';
import { userNotAuthenticated } from '$lib/server/auth-utils';
export const load = loadFlash(async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event);
}
const userRoles = await db.query.user_roles.findMany({
where: eq(user_roles.user_id, user.id),
const userRoles = await db.query.userRoles.findMany({
where: eq(user_roles.user_id, user!.id!),
with: {
role: {
columns: {
@ -23,7 +23,7 @@ export const load = loadFlash(async (event) => {
},
});
const containsAdminRole = userRoles.some((user_role) => user_role?.role?.name === 'admin');
const containsAdminRole = userRoles.some((userRoles) => user_role?.role?.name === 'admin');
if (!userRoles?.length || !containsAdminRole) {
console.log('Not an admin');
redirect(302, '/', forbiddenMessage, event);

View file

@ -1,13 +1,11 @@
import { redirect } from 'sveltekit-flash-message/server';
import type { PageServerLoad } from './$types';
import { notSignedInMessage } from '$lib/flashMessages';
import db from '../../../../../db';
import { userNotFullyAuthenticated } from '$lib/server/auth-utils';
import { userNotAuthenticated } from '$lib/server/auth-utils';
export const load: PageServerLoad = async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}

View file

@ -3,16 +3,15 @@ import { and, eq } from 'drizzle-orm';
import { superValidate } from 'sveltekit-superforms/server';
import { zod } from 'sveltekit-superforms/adapters';
import { redirect } from 'sveltekit-flash-message/server';
import { modifyListGameSchema, type ListGame } from '$lib/validations/zod-schemas';
import { search_schema } from '$lib/zodValidation.js';
import { modifyListGameSchema } from '$lib/validations/zod-schemas';
import db from '../../../../db';
import { collection_items, collections, games } from '$db/schema';
import { notSignedInMessage } from '$lib/flashMessages';
import { userNotFullyAuthenticated } from '$lib/server/auth-utils';
import { userNotAuthenticated } from '$lib/server/auth-utils';
export async function load(event) {
const { user, session } = event.locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event);
}
@ -23,7 +22,7 @@ export async function load(event) {
name: true,
created_at: true,
},
where: eq(collections.user_id, user.id),
where: eq(collections.user_id, user!.id!),
});
console.log('collections', userCollections);

View file

@ -8,14 +8,14 @@ import db from '../../../../../db';
import { notSignedInMessage } from '$lib/flashMessages.js';
import { collections, games, collection_items } from '$db/schema';
import { search_schema } from '$lib/zodValidation';
import { userNotFullyAuthenticated } from '$lib/server/auth-utils';
import { userNotAuthenticated } from '$lib/server/auth-utils';
export async function load(event) {
const { locals, params, url } = event;
const { user, session } = locals;
const { id } = params;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event);
}
const searchParams = Object.fromEntries(url?.searchParams);
@ -104,7 +104,7 @@ export const actions: Actions = {
add: async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}
@ -153,7 +153,7 @@ export const actions: Actions = {
create: async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}
return error(405, 'Method not allowed');
@ -162,7 +162,7 @@ export const actions: Actions = {
delete: async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}
return error(405, 'Method not allowed');
@ -171,7 +171,7 @@ export const actions: Actions = {
remove: async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}
const form = await superValidate(event, zod(modifyListGameSchema));

View file

@ -1,12 +1,11 @@
import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from '../$types';
import { notSignedInMessage } from '$lib/flashMessages';
import { userNotFullyAuthenticated } from '$lib/server/auth-utils';
import { userNotAuthenticated } from '$lib/server/auth-utils';
export async function load(event) {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event);
}

View file

@ -3,13 +3,13 @@ import { superValidate } from 'sveltekit-superforms/server';
import { zod } from 'sveltekit-superforms/adapters';
import type { PageServerLoad } from '../$types';
import { BggForm } from '$lib/zodValidation';
import { userNotFullyAuthenticated } from '$lib/server/auth-utils';
import { userNotAuthenticated } from '$lib/server/auth-utils';
import { notSignedInMessage } from '$lib/flashMessages';
export const load: PageServerLoad = async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event);
}

View file

@ -2,12 +2,12 @@ import { fail } from '@sveltejs/kit';
import { eq } from 'drizzle-orm';
import db from '../../../../db';
import { wishlists } from '$db/schema';
import { userNotFullyAuthenticated } from '$lib/server/auth-utils';
import { userNotAuthenticated } from '$lib/server/auth-utils';
import { notSignedInMessage } from '$lib/flashMessages';
export async function load({ locals }) {
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
throw fail(401);
}

View file

@ -5,13 +5,13 @@ import { superValidate } from 'sveltekit-superforms/server';
import db from '../../../../../db';
import { modifyListGameSchema } from '$lib/validations/zod-schemas';
import { games, wishlist_items, wishlists } from '$db/schema';
import { userNotFullyAuthenticated } from '$lib/server/auth-utils';
import { userNotAuthenticated } from '$lib/server/auth-utils';
import { notSignedInMessage } from '$lib/flashMessages';
export async function load(event) {
const { params, locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event);
}
@ -44,7 +44,7 @@ export const actions: Actions = {
add: async (event) => {
const { params, locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}
const form = await superValidate(event, zod(modifyListGameSchema));
@ -102,7 +102,7 @@ export const actions: Actions = {
create: async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}
},
@ -110,7 +110,7 @@ export const actions: Actions = {
delete: async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}
},
@ -118,7 +118,7 @@ export const actions: Actions = {
remove: async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}
},

View file

@ -9,12 +9,12 @@ import { notSignedInMessage } from '$lib/flashMessages';
import db from '../../../../db';
import type { PageServerLoad } from './$types';
import { users } from '$db/schema';
import { userNotFullyAuthenticated } from '$lib/server/auth-utils';
import { userNotAuthenticated } from '$lib/server/auth-utils';
export const load: PageServerLoad = async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event);
}

View file

@ -11,13 +11,13 @@ import { lucia } from '$lib/server/auth.js';
import { users } from '$db/schema';
import { notSignedInMessage } from '$lib/flashMessages';
import type { Cookie } from 'lucia';
import { userNotFullyAuthenticated } from '$lib/server/auth-utils';
import { userNotAuthenticated } from '$lib/server/auth-utils';
export const load: PageServerLoad = async (event) => {
const form = await superValidate(event, zod(changeUserPasswordSchema));
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event);
}
@ -35,7 +35,7 @@ export const actions: Actions = {
default: async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}

View file

@ -13,7 +13,7 @@ import { addTwoFactorSchema, removeTwoFactorSchema } from '$lib/validations/acco
import { notSignedInMessage } from '$lib/flashMessages';
import db from '../../../../../../db';
import { recoveryCodes, users } from '$db/schema';
import { userNotFullyAuthenticated } from '$lib/server/auth-utils';
import { userNotAuthenticated } from '$lib/server/auth-utils';
export const load: PageServerLoad = async (event) => {
const addTwoFactorForm = await superValidate(event, zod(addTwoFactorSchema));
@ -21,12 +21,12 @@ export const load: PageServerLoad = async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event);
}
const dbUser = await db.query.users.findFirst({
where: eq(users.id, user.id),
where: eq(users.id, user!.id!),
});
if (dbUser?.two_factor_enabled) {
@ -46,10 +46,10 @@ export const load: PageServerLoad = async (event) => {
two_factor_secret: encodeHex(twoFactorSecret),
two_factor_enabled: false,
})
.where(eq(users.id, user.id));
.where(eq(users.id, user!.id!));
const issuer = 'bored-game';
const accountName = user.email || user.username;
const accountName = user!.email! || user!.username!;
// pass the website's name and the user identifier (e.g. email, username)
const totpUri = createTOTPKeyURI(issuer, accountName, twoFactorSecret);
@ -71,7 +71,7 @@ export const actions: Actions = {
enableTwoFactor: async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}

View file

@ -6,12 +6,12 @@ import { redirect } from 'sveltekit-flash-message/server';
import { notSignedInMessage } from '$lib/flashMessages';
import type { PageServerLoad } from '../../../$types';
import { recoveryCodes, users } from '$db/schema';
import { userNotFullyAuthenticated } from '$lib/server/auth-utils';
import { userNotAuthenticated } from '$lib/server/auth-utils';
export const load: PageServerLoad = async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event);
}

View file

@ -7,12 +7,12 @@ import { modifyListGameSchema } from '$lib/validations/zod-schemas';
import db from '../../../../db';
import { notSignedInMessage } from '$lib/flashMessages.js';
import { games, wishlist_items, wishlists } from '$db/schema';
import { userNotFullyAuthenticated } from '$lib/server/auth-utils';
import { userNotAuthenticated } from '$lib/server/auth-utils';
export async function load(event) {
const { params, locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event);
}
@ -41,7 +41,7 @@ export const actions: Actions = {
add: async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}
const form = await superValidate(event, zod(modifyListGameSchema));
@ -89,7 +89,7 @@ export const actions: Actions = {
create: async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}
return error(405, 'Method not allowed');
@ -98,7 +98,7 @@ export const actions: Actions = {
delete: async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}
return error(405, 'Method not allowed');
@ -107,7 +107,7 @@ export const actions: Actions = {
remove: async (event) => {
const { params, locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}
const form = await superValidate(event, zod(modifyListGameSchema));

View file

@ -7,13 +7,13 @@ import { modifyListGameSchema } from '$lib/validations/zod-schemas';
import db from '../../../../../db';
import { notSignedInMessage } from '$lib/flashMessages.js';
import { games, wishlist_items, wishlists } from '$db/schema';
import { userNotFullyAuthenticated } from '$lib/server/auth-utils';
import { userNotAuthenticated } from '$lib/server/auth-utils';
export async function load(event) {
const { params, locals } = event;
const { user, session } = locals;
const { id } = params;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
redirect(302, '/login', notSignedInMessage, event);
}
@ -44,7 +44,7 @@ export const actions: Actions = {
add: async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}
const form = await superValidate(event, zod(modifyListGameSchema));
@ -92,7 +92,7 @@ export const actions: Actions = {
create: async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}
return error(405, 'Method not allowed');
@ -101,7 +101,7 @@ export const actions: Actions = {
delete: async ({ locals }) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}
return error(405, 'Method not allowed');
@ -110,7 +110,7 @@ export const actions: Actions = {
remove: async (event) => {
const { locals } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
if (userNotAuthenticated(user, session)) {
return fail(401);
}
const form = await superValidate(event, zod(modifyListGameSchema));

View file

@ -1,10 +1,23 @@
import { loadFlash } from 'sveltekit-flash-message/server';
import type { LayoutServerLoad } from '../$types';
import { userFullyAuthenticated, userNotFullyAuthenticated } from '$lib/server/auth-utils';
import { lucia } from '$lib/server/auth';
export const load: LayoutServerLoad = loadFlash(async (event) => {
const { url, locals, cookies } = event;
const { user, session } = locals;
if (userNotFullyAuthenticated(user, session)) {
await lucia.invalidateSession(locals.session!.id!);
const sessionCookie = lucia.createBlankSessionCookie();
cookies.set(sessionCookie.name, sessionCookie.value, {
path: '.',
...sessionCookie.attributes,
});
}
export const load: LayoutServerLoad = loadFlash(async ({ url, locals }) => {
console.log('user from app', locals.user);
return {
url: url.pathname,
user: locals.user
user: userFullyAuthenticated(user, session) ? locals.user : null,
};
});

View file

@ -3,8 +3,12 @@ import { eq } from 'drizzle-orm';
import type { PageServerLoad } from './$types';
import db from '../../db';
import { collections, wishlists } from '$db/schema';
import { userFullyAuthenticated } from '$lib/server/auth-utils';
export const load: PageServerLoad = async (event) => {
const { locals, url } = event;
const { user, session } = locals;
export const load: PageServerLoad = async ({ locals, url }) => {
const image = {
url: `${
new URL(url.pathname, url.origin).href
@ -37,21 +41,21 @@ export const load: PageServerLoad = async ({ locals, url }) => {
},
});
const user = locals.user;
if (user) {
if (userFullyAuthenticated(user, session)) {
console.log('Sending back user details');
const userWishlists = await db.query.wishlists.findMany({
columns: {
cuid: true,
name: true,
},
where: eq(wishlists.user_id, user.id),
where: eq(wishlists.user_id, user!.id!),
});
const userCollection = await db.query.collections.findMany({
columns: {
cuid: true,
name: true,
},
where: eq(collections.user_id, user.id),
where: eq(collections.user_id, user!.id!),
});
console.log('Wishlists', userWishlists);
@ -59,5 +63,5 @@ export const load: PageServerLoad = async ({ locals, url }) => {
return { metaTagsChild: metaTags, user, wishlists: userWishlists, collections: userCollection };
}
return { metaTagsChild: metaTags, user: locals.user, wishlists: [], collections: [] };
return { metaTagsChild: metaTags, user: null, wishlists: [], collections: [] };
};

View file

@ -1,8 +1,6 @@
import { fail, error, type Actions } from '@sveltejs/kit';
import { and, eq } from 'drizzle-orm';
import { Argon2id } from 'oslo/password';
import { decodeHex } from 'oslo/encoding';
import { TOTPController } from 'oslo/otp';
import { zod } from 'sveltekit-superforms/adapters';
import { setError, superValidate } from 'sveltekit-superforms/server';
import { redirect } from 'sveltekit-flash-message/server';
@ -10,15 +8,25 @@ import { RateLimiter } from 'sveltekit-rate-limiter/server';
import db from '../../../db';
import { lucia } from '$lib/server/auth';
import { signInSchema } from '$lib/validations/auth';
import { users, recoveryCodes, type Users } from '$db/schema';
import { users, type Users } from '$db/schema';
import type { PageServerLoad } from './$types';
import { userFullyAuthenticated, userNotFullyAuthenticated } from '$lib/server/auth-utils';
export const load: PageServerLoad = async (event) => {
if (event.locals.user) {
const { locals, cookies } = event;
const { user, session } = event.locals;
if (userFullyAuthenticated(user, session)) {
const message = { type: 'success', message: 'You are already signed in' } as const;
throw redirect('/', message, event);
} else if (userNotFullyAuthenticated(user, session)) {
await lucia.invalidateSession(locals.session!.id!);
const sessionCookie = lucia.createBlankSessionCookie();
cookies.set(sessionCookie.name, sessionCookie.value, {
path: '.',
...sessionCookie.attributes,
});
}
const form = await superValidate(event, zod(signInSchema));
return {