Coder Social home page Coder Social logo

delay / sveltekit-auth-starter Goto Github PK

View Code? Open in Web Editor NEW
203.0 11.0 30.0 231 KB

This is a Sveltekit auth starter project. It utilizes Lucia for authentication, Skeleton for ui elements, Prisma for database connectivity and type safety, Lucide for icons, inlang for translation, Zod and Superforms to handle forms and validation and Sveltekit.

Home Page: https://sveltekit-auth-starter.vercel.app

License: MIT License

JavaScript 3.61% HTML 0.60% CSS 6.83% Svelte 47.96% TypeScript 41.00%

sveltekit-auth-starter's Introduction

Sveltekit Auth Starter

Sveltekit Auth User Interface

IMPORTANT UPDATE. I recently switched UI components to shadcn-svelte. I made a new starter project here. Any new features will be added to that project. I will still accept bug fixes for this project.

This is a Sveltekit Auth Starter Project. An example website is currently deployed here. It is an open source auth starter project utilizing Lucia for authentication, Skeleton for ui elements, Lucide for icons, Prisma for database connectivity and type safety, inlang for language translation and Sveltekit for the javascript framework. I also used Zod and Superforms to handle form validation and management. It has email verification, password reset, and will send an email if the user changes their email address to re-verify it. It also has a custom error logging system which I wrote about in this blog post. The log results are sent to Axiom. It is released as open source under an MIT license.

While creating this project, I made use of several great videos and tutorials from Huntabyte and Joy of Code. Both have great tutorials for all things related to Sveltekit.

This project creates an email and password user log in system and is my attempt to make something production ready with all of the usual features you expect. You can create new users and sign them in. It has user roles. It will verify a users email address. You can edit your profile including changing your email address and password. You can reset your password in case you forgot it. You can also change themes and have a light and dark mode. I didn’t see any examples utilizing these frameworks that had all of these necessary features.

I picked Lucia for auth because it had great documentation and seemed to be in active development and was very full featured. It can provide authentication for OAuth providers as well. I always want to have a fallback for email and password, so that is what I chose to make for this project.

Skeleton is another great project with a really nice development experience. Perhaps the coolest feature is it makes use of design tokens. This allows for lots of customization just by modifying the theme.css.

skeleton themes

Prisma is another great package and it is used for database connectivity and type safety. It works with many databases so it’s easy to change your database with one line of code. It has an easy to use ORM that cuts back on the amount of code you need to write.

Zod is a typescript schema validation that allows you to easily validate your input in projects. It is very easy to setup what your data should look like to validate against.

Finally Superforms makes it easy to work with forms in Sveltekit. It cuts down a lot on boilerplate code when working with forms.

This was the first time working with many of these packages, but they really do streamline much of the Sveltekit development process. If there are any mistakes, please open up an issue on the project. Also I was pleasantly surprised at the scores from Google PageSpeed Insights. This project scored a 100% in all metrics.

pagespeed insights metrics

File Structure for the App

sample.env — private environmental server side variables that must be set. Rename to.env and supply your personal project settings.

/prisma/schema.prisma — holds the prism schema which is the design of your data in the app and db. Currently holds the auth schema for Lucia auth.

/src/

app.d.ts — holds type definitions for lucia and can hold your additional types for other features.

hooks.server.ts — holds a Lucia auth handle function.

theme.postcss — holds a custom theme for skeleton. This can be set in /routes/+layout.svelte. Comment out the theme-skeleton and add in theme.postcss. You can create your own custom theme https://www.skeleton.dev/docs/generator. There are also lots of premade themes included with sveltekit. To use those, change theme-skeleton.css to theme-modern.css or another theme name.

/lib

/_helpers

convertNameToInitials.ts — function for making initials from first and last name of user for the avatar.

getAllUrlParams.ts - puts any url parameters in an object to be used in our log system. parseMessage - puts event.locals.message message into object or string and for our log. parseTrack - puts event.locals.track message into object or string for our log.

/components

footer.svelte — footer in the app, used in /routes/+layout.svelte

logo.svelte — used as the logo throughout the app.

navigation.svelte — navigation menu links used in /routes/+layout.svelte. They change based on whether user is logged in or not.

sign-in.svelte — sign in form component used in /auth/sign-in/+page.svelte

sign-up.svelte — sign up form component used in /auth/sign-up/+page.svelte

/config

constants.ts — all of the public constants that do not need to be hidden server side. I prefer this to naming constants PUBLIC_WHATEVER in the .env file, which is another option. I prefer to keep my .env file with only server side env variables.

email-messages.ts — this is where I keep all of the email messages that need to be sent. It makes it easier in case changes need to be made to the emails that are sent out.

prima.ts — file used to initialize the prisma client.

zod-schema.ts — holds the schema used in zod. This defines how our form data needs to be validated.

/server

email-send.ts — this handles our email sending with AWS SES. It only runs server side to keep your credentials hidden. It also allows you to use SMTP settings in case you are not using AWS through nodemailer.

lucia.ts- this initializes the lucia-auth package for handling our auth functions. It also holds the extra custom fields we added to the user.

log.ts - This is used by our hook to get the log data and send to our log service.

/routes

+layout.server.ts — gets the user info from lucia-auth if available so we can access it in our app.

+layout.svelte — overall site layout which is inherited by the rest of our app.

+page.svelte - basic info about our app

+error.svelte - custom error page.

/auth

+layout.svelte-handles our layout for the auth section.

/legal

[email protected] — resets our layout for the legal section so it doesn’t inherit the auth layout. add the @ at the end to do this.

/legal/terms

Holds our terms and conditions page. Do not use this for your own website as I just used ChatGPT to make this. You should consult a legal professional to develop the terms for your own app.

/legal/privacy

Holds our privacy policy page. Do not use this for your own website as I just used ChatGPT to make this. You should consult a legal professional to develop the privacy policy for your own app.

/password/reset

This holds the password reset form and function to send a password reset email when the user enters there email address,

/password/update-[token]

This allows the user to actually put in the new password, the token comes from the email from the users reset request. Anything in [] is able to be accessed as a parameter in Sveltekit, so [token] can be accessed via (token = event.params.token).

/password/update-[token]/success

This is the message the user sees if there reset was successful.

/profile

This allows the user to update their profile with new information. If they change their email address we also un-verify them and send them an email asking them to reconfirm their email. We also send an email to their old address telling them this change was made with the old and new address so that the data can be reset manually if the users account was hacked.

/sign-in

Page and functions for signing in the user.

/sign-out

Function for signing out the user.

/sign-up

Page and functions for signing up the user.

/verify/email

This page asks user to check there email and verify it.

/verify/email-[token]

This page confirms the email address by verifying the token the user received in his email account.

/verify/resend-email-[token]

This resends the verify email token email in case the user didn’t receive or lost the email.

/(protected)

This route group is only allowed to be accessed when a user is logged in.

Hopefully you may find some of this code useful for your own project. Please feel free to use it in any project.

sveltekit-auth-starter's People

Contributors

ak4zh avatar crushingcodes avatar delay avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sveltekit-auth-starter's Issues

Fresh install, Docker build failed.

Great project!

Wanted to try it out, but run in to issues. Here is what I did. Am I missing something?

git clone https://github.com/delay/sveltekit-auth-starter.git
cd sveltekit-auth-starter/
docker run -d --name postgresql -p 32768:5432 -e POSTGRES_USER=mysecretuser -e POSTGRES_PASSWORD=mysecretpassword -e POSTGRES_DB=mysecretuser postgres:latest

cd prisma
prisma migrate dev --name init
prisma migrate deploy 
cd ..
echo 'PRISMA_URL="postgresql://mysecretuser:[email protected]:32768/mysecretuser"' >> .env
echo 'SVELTEKIT_PORT=3000' >> .env
echo 'AWS_ACCESS_KEY_ID="BUBUBU"' >> .env
echo 'AWS_SECRET_ACCESS_KEY="EASDASD+8nHeqQk"' >> .env
echo 'AWS_REGION="eu-north-1"' >> .env
echo 'AWS_API_VERSION="2010-12-01"' >> .env
echo 'FROM_EMAIL="[email protected]"' >> .env
sh docker-startup.sh 

[+] Building 0.1s (15/15) FINISHED                                                            
 => [internal] load build definition from Dockerfile                                     0.0s
 => => transferring dockerfile: 32B                                                      0.0s
 => [internal] load .dockerignore                                                        0.0s
 => => transferring context: 2B                                                          0.0s
 => [internal] load metadata for docker.io/library/node:19-bullseye-slim                 0.0s
 => [build 1/7] FROM docker.io/library/node:19-bullseye-slim                             0.0s
 => [internal] load build context                                                        0.0s
 => => transferring context: 6.80kB                                                      0.0s
 => CACHED [build 2/7] RUN apt-get update; apt-get install -y --no-install-recommends c  0.0s
 => CACHED [build 3/7] WORKDIR /app                                                      0.0s
 => CACHED [deploy-node 4/7] RUN rm -fr ./*                                              0.0s
 => CACHED [build 4/7] COPY . .                                                          0.0s
 => CACHED [build 5/7] RUN npm ci                                                        0.0s
 => CACHED [build 6/7] RUN npx prisma generate                                           0.0s
 => CACHED [build 7/7] RUN npm run build                                                 0.0s
 => CACHED [deploy-node 5/7] COPY --from=build /app/package*.json ./                     0.0s
 => CACHED [deploy-node 6/7] RUN npm ci --production --ignore-scripts                    0.0s
 => ERROR [deploy-node 7/7] COPY --from=build /app/build ./                              0.0s
------
 > [deploy-node 7/7] COPY --from=build /app/build ./:
------
failed to solve: failed to compute cache key: "/app/build" not found: not found

Axiom Node is deprecated with replacement being Axiom JS

Axiom Node's site states: Deprecated, use axiom-js instead

My attempts to integrate axiomhq/js in place of the current implementation in this repository have not been successful. Errors as described in this issue on their repository.

Any help to migrate this repository to axiomhq/js would be helpful for others trying to do the same.

Serverside data load on protected route while logged out

tldr: #6 should fix this issue

I'm not sure if this is a security concern because - at least from what I can see - the loaded data are not returned to the client-side so they are safe, but at least it opens the door for kind of unnecessary data loads and potential load on your internal systems or costs for usage of external apis.

From my understanding it's the kinda the issue that is discussed in this issue.
The problem is, that all load functions run in parallel. So at long as you keep se simple structure of the current protected route, everything is fine.

But when you add an +page.server.ts file inside of the protected route where you access some external data sources in the load function, this access run in parallel to the load function inside of +layout.server.ts. In the end the data are not returned to the client (at least I didn't found them client side) because the auth check prevents the user from accessing the +page.svelte file where the loaded data are passed into via prop. But the fetch call still happens (which can be annoying in case of system load or costly in case of external apis with usage fees per call).

To have an example of the issue, you simple can add the file src/routes/protected/+page.server.ts with the following code (simple fetch and a log message we can see on the server side):

import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async () => {
	const getCustomers = async () => {
		console.log('Fetched customers from database');
		const res = await fetch(
			'https://dummyjson.com/users?limit=10&skip-10&select=firstName,lastName,id,email'
		);

		const customers = await res.json();
		return customers.users;
	};

	const customers = await getCustomers();

	return {
		props: {
			customers: customers
		}
	};
};

To see the date in case of an authorised user, you also can change src/routes/protected/+page.svelte so that you accept PageData inside the script block:

import type { PageData } from './$types';
export let data: PageData;

and display them in the body:

{#if data}
<pre>{JSON.stringify(data.props.customers, null, 2)}</pre>
{:else}
// This should not be reachable
<p>There is no data.</p>
{/if}

With this in place, data are loaded as soon as you hover over the Protected link inside of the menu. You can see the log "Fetched customers from database" (server side). This gif shows the behaviour:

To fix this problem, we can move the redirects from the layout.svelte.ts inside of the protected route into hooks.server.ts. See #6 for the implementation details.

Users can bypass email verification

The auth/verify/email page includes the user's token in the "click here to resend" link. Users can exploit this by extracting the token from the link and using it to become verified, without ever receiving the verification email. Armed with this knowledge, a bad actor could e.g. write a script to automatically register X number of fake users without going through the email verification process.

  1. Visit the signup page, e.g. https://sveltekit-auth.uv-ray.com/auth/sign-up
  2. Fill in the form, using a fake email address e.g. "[email protected]"
  3. When redirected to https://sveltekit-auth.uv-ray.com/auth/verify/email copy the url from the "click here to resend" link
  4. Edit the url to change "resend-email-(token)" to "email-(token)"
  5. Visit the url in your browser and notice that your user is now verified

Getting an odd error... Error: Not found: /__webpack_hmr/client

Hi there! There isn't any mention of webpack in this repo so I don't expect this to be an otherwise known error, but running this project I receive this error while just randomly navigating the app (which otherwise seems to run perfectly, besides it being really slow to load which could be caused by this.

After running npm run dev this occurs:


null
null
6:25:57 p.m. [vite] ✨ new dependencies optimized: @skeletonlabs/skeleton, lucide-svelte, zod, sveltekit-superforms/client
6:25:57 p.m. [vite] ✨ optimized dependencies changed. reloading

  lucia   Debug mode disabled
Error: Not found: /__webpack_hmr/client
    at resolve (/home/patrick/Projects/mvp/node_modules/@sveltejs/kit/src/runtime/server/respond.js:430:13)
    at resolve (/home/patrick/Projects/mvp/node_modules/@sveltejs/kit/src/runtime/server/respond.js:277:5)
    at Object.handle (/home/patrick/Projects/mvp/src/hooks.server.ts:19:16)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async Module.respond (/home/patrick/Projects/mvp/node_modules/@sveltejs/kit/src/runtime/server/respond.js:274:20)
    at async file:///home/patrick/Projects/mvp/node_modules/@sveltejs/kit/src/exports/vite/dev/index.js:505:22
null
null

and just for reference, my hooks.server.ts looks like:

import { auth } from '$lib/server/lucia';
import { redirect, type Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ event, resolve }) => {
	event.locals.auth = auth.handleRequest(event);
	if (event.locals?.auth) {
		const { user } = await event.locals.auth.validateUser();
		event.locals.user = user;
		if (event.route.id?.startsWith('/(protected)')) {
			if (!user) throw redirect(302, '/auth/sign-in');
			if (!user.verified) throw redirect(302, '/auth/verify/email');
		}
	}

	return await resolve(event);
};

Node version: v18.12.0
npm version: 8.19.0

Any ideas here? Really wacky.

Is auth working as expected (hooks.server.ts)?

Hey,
first of all, awesome project and a good starting point for any new sveltekit developer!
Right now I'm trying to wrap my head around auth in sveltekit so this repo is a great help for me. While trying to understand your code I came across the hooks.server.ts file, where you check if the called url starts with "/auth".

if (event.request.url.startsWith('/auth')) {
    return resolve(event);
}

The problem is, that event.request.url returns - at least on localhost - the complete url (e.g. 'http://localhost:5173/auth/sign-in'). I think its clear that this condition will never be true, so the auth check will always run. I guess what you want is to use event.url.pathname which return only the path (e.g. '/auth/sign-in'). In this case, the condition is true when a /auth route is called.

But inside of sign-in/+page.server.ts the load function actually tries to access event.locals.auth which is not set if the condition in hooks.server.ts works as I think expected. So we get a internal server error.

So my question is, is this a bug? Or what is the idea behind the if block inside hooks.server.ts?

Bug? Signed in without email verification

Testing locally, I accidentally discovered a way to login without ever clicking on (or even receiving) the confirmation email:

  1. Configure your .env with incorrect SMTP login credentials
  2. Start the dev server via npm run dev
  3. Fill in the sign up fields in the browser and click "Sign Up"
  4. Notice the server crashes. Console logs incorrectly report the email was sent successfully before subsequently failing with an SMTP authentication error, e.g.
E-mail sent successfully!
log:  {"level":"info","method":"GET","path":"/auth/verify/resend-email-a0455918-de53-4979-9aa4-3d72a06e491b","status":200,"timeInMs":1552,"user":"[email protected]","userId":"AhXN3CVU2QsGzOP","referer":"/auth/verify/email"}
log:  {"level":"info","method":"GET","path":"/","status":200,"timeInMs":4056,"user":"[email protected]","userId":"AhXN3CVU2QsGzOP","referer":"/auth/verify/email"}
E-mail sent successfully!
log:  {"level":"info","method":"GET","path":"/auth/verify/resend-email-a0455918-de53-4979-9aa4-3d72a06e491b","status":200,"timeInMs":654,"user":"[email protected]","userId":"AhXN3CVU2QsGzOP","referer":"/auth/verify/resend-email-a0455918-de53-4979-9aa4-3d72a06e491b"}
/src/lib/server/email-send.ts:78
            throw new Error(`Error sending email: ${JSON.stringify(err)}`);
            ^
Error: Error sending email: {"code":"EAUTH","response":"535 Incorrect authentication data","responseCode":535,"command":"AUTH PLAIN"}
    at eval (/src/lib/server/email-send.ts:78:19)
  1. Restart the server with npm run dev
  2. Return to the browser and click on the "If you did not receive the email, [click here] to resend it" link
  3. Notice the server crashes again with an SMTP Auth error
  4. Restart the server with npm run dev
  5. Return to the browser and visit http://localhost:5173/dashboard
  6. Notice under Protected Area, it says "If you are seeing this page, you are logged in."
  7. Visit http://localhost:5173/profile and notice that you can see your profile and make changes to it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.