Authentication for SvelteKit
Developed by Stephen Shkeda
[email protected]
- Overview
- Get Started
- Features
- Security
- Environment Variables
- Server API
- Client API
- Providers
- Adapters
- Components
- License
svauth is a complete open source authentication solution for SvelteKit applications.
Designed from the ground up to support SvelteKit and serverless.
Heavily inspired by NextAuth.js.
Written for Svelte Hack 2023.
This is a monorepo containing the following packages / projects:
- The primary
svauth
package - A development test application
npm install svauth
yarn add svauth
pnpm add svauth
// hooks.server.ts
import Svauth from 'svauth';
import { Google } from 'svauth/providers';
import { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } from '$env/static/private';
export const handle = Svauth({
providers: [
Google({
clientId: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET
})
]
});
// routes/+page.server.ts
import type { PageServerLoad } from './$types';
export const load = (async (event) => {
return {
session: await event.locals.getSession()
};
}) satisfies PageServerLoad;
<script lang="ts">
// routes/+page.svelte
import { session, signIn, signOut } from 'svauth/client';
</script>
{#if $session}
<button on:click={() => signOut()}> Sign out </button>
<p>Logged in as {$session.user.email}</p>
{:else}
<button on:click={() => signIn('google')}> Sign in with Google </button>
<p>Not signed in</p>
{/if}
- OAuth Providers - Seamless integration with Google, Discord, and GitHub.
- Serverless - Designed to work with serverless environments.
- Session Management - Built-in session management for server and client-side rendering.
- TypeScript - Written in TypeScript and includes type definitions.
- Security - Designed to be secure by default and encourage best practices for safeguarding user data.
- Customizable Components - Pre-built, customizable components like the SignInWithGoogleButton for a smoother user authentication experience.
- CSRF Protection - SvelteKit's built-in CSRF protection is used to prevent CSRF attacks.
- JWT Encryption - When JSON Web Tokens are enabled, they are encrypted by default (JWE) with A256GCM.
- Client Independence - Doesn't rely on client-side JavaScript.
- Secure Cookie Management - Signed, prefixed, server-only cookies.
- OWASP Compliance - Attempts to implement the latest guidance published by Open Web Application Security Project.
Please contact me directly, [email protected], to report serious issues that might impact the security of sites using svauth.
Name | Description |
---|---|
SVAUTH_SECRET | The secret used to encrypt the session. |
SVAUTH_URL | The URL of the application. |
PUBLIC_SVAUTH_PREFIX | The prefix used for the svauth routes. |
For production environments, if SVAUTH_URL is not set, VERCEL_URL will be used. For development environments, SVAUTH_URL defaults to localhost:5173.
PUBLIC_SVAUTH_PREFIX is optional and defaults to /auth.
The Svauth
function is used to create the Svauth handler.
Name | Type | Description |
---|---|---|
options |
SvauthOptions |
The options object. |
Name | Type | Default | Description |
---|---|---|---|
providers |
Provider[] |
An array of providers. | |
adapter |
Adapter |
The adapter to link svauth with a database. | |
expires |
number | string |
30d |
The expiration time of the session. When number is passed that is used as a value in seconds, when string is passed it is resolved to a time span and added to the current timestamp. |
// hooks.server.ts
import Svauth from 'svauth';
export const handle = Svauth({
providers: [
Google({
clientId: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET
})
],
expires: '14d'
});
The event.locals
object contains the following properties:
Name | Type | Description |
---|---|---|
getSession() |
async |
Returns the current session. |
// routes/+page.server.ts
import type { PageServerLoad } from './$types';
export const load = (async (event) => {
return {
session: await event.locals.getSession()
};
}) satisfies PageServerLoad;
The session
store is the easiest way to obtain information on current session.
session
returns 4 possible values:
- Session - The user's current session.
- null - The credentials are wrong or the session has expired.
- undefined - There is no session.
- false - The session is loading.
Note: If you are obtaining the session during load, session
will not return false
.
<script lang="ts">
import { session } from 'svauth/client';
</script>
{#if $session}
<p>Logged in as {$session.user.email}</p>
{:else}
<p>Not signed in</p>
{/if}
The Session
object is returned by the session
store.
Name | Type | Description |
---|---|---|
User |
User |
The user object. |
expires |
Date |
The expiration time of the session. |
issuedAt |
Date |
The time the session was issued. |
The User
object is found inside the Session
object.
Name | Type | Description |
---|---|---|
id |
string |
The user's ID. |
email |
string |
The user's email. |
name |
string |
The user's name. |
image |
string |
A link to the user's image |
The signIn
function is used to sign in a user.
Name | Type | Default | Description |
---|---|---|---|
provider |
string |
The provider to sign in with. | |
redirect |
string |
Current user's location | The URL to redirect the user to after signing out. |
<script lang="ts">
import { signIn } from 'svauth/client';
</script>
<button on:click={() => signIn('google')}> Sign in with Google </button>
The signOut
function is used to sign out a user.
Name | Type | Default | Description |
---|---|---|---|
redirect |
string |
Current user's location | The URL to redirect the user to after signing out. |
<script lang="ts">
import { signOut } from 'svauth/client';
</script>
<button on:click={() => signOut()}> Sign out </button>
Currently, svauth has support for three OAuth providers:
In the future, I plan to add support for more providers.
https://developers.google.com/identity/protocols/oauth2 https://developers.google.com/identity/openid-connect/openid-connect
https://console.developers.google.com/apis/credentials
The "Authorized redirect URIs" used when creating the credentials must include your full domain and end in the callback path.
For example:
- Production: https://{YOUR_DOMAIN}/auth/callback/google
- Development: http://localhost:5173/auth/callback/google
// hooks.server.ts
import Svauth from 'svauth';
import { Google } from 'svauth/providers';
import { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } from '$env/static/private';
export const handle = Svauth({
providers: [
Google({
clientId: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET
})
]
});
https://discord.com/developers/docs/topics/oauth2
https://discord.com/developers/applications
The "Redirects" used when creating the credentials must include your full domain and end in the callback path.
For example:
- Production: https://{YOUR_DOMAIN}/auth/callback/discord
- Development: http://localhost:5173/auth/callback/discord
// hooks.server.ts
import Svauth from 'svauth';
import { Discord } from 'svauth/providers';
import { DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET } from '$env/static/private';
export const handle = Svauth({
providers: [
Discord({
clientId: DISCORD_CLIENT_ID,
clientSecret: DISCORD_CLIENT_SECRET
})
]
});
https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps
https://github.com/settings/developers
The "Authorization callback URL" used when creating the credentials must include your full domain and end in the callback path.
For example:
- Production: https://{YOUR_DOMAIN}/auth/callback/github
- Development: http://localhost:5173/auth/callback/github
// hooks.server.ts
import Svauth from 'svauth';
import { GitHub } from 'svauth/providers';
import { GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET } from '$env/static/private';
export const handle = Svauth({
providers: [
GitHub({
clientId: GITHUB_CLIENT_ID,
clientSecret: GITHUB_CLIENT_SECRET
})
]
});
// routes/hooks.server.ts
import Svauth from 'svauth';
import { Google } from 'svauth/providers';
import { Prisma } from 'svauth/adapters';
import prisma from './server/prisma';
import { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } from '$env/static/private';
export const handle = Svauth({
providers: [
Google({
clientId: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET
})
],
adapter: Prisma(prisma)
});
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
relationMode = "prisma"
}
model Account {
id String @id @default(cuid())
userId String
provider String
providerAccountId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
@@index([userId])
}
model Session {
id String @id @default(cuid())
userId String
issuedAt DateTime @default(now())
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
}
model User {
id String @id @default(cuid())
name String
email String @unique
picture String
accounts Account[]
sessions Session[]
}
The SignInWithGoogleButton
component is a simple button that will sign the user in with Google.
https://developers.google.com/identity/gsi/web/guides/overview
https://console.developers.google.com/apis/credentials
The "Authorized JavaScript origins" used when creating the credentials must include your HTTP origins.
For example:
- Production: https://{YOUR_DOMAIN}
- Development: http://localhost:5173 and http://localhost
Name | Type | Default | Description |
---|---|---|---|
oneTap |
boolean |
false |
Whether to use Google One Tap sign in. |
type |
standard | icon |
standard |
The button type.button. |
theme |
outline | filled_blue | filled_black |
outline |
The button theme. |
size |
large | medium | small |
large |
The button size. |
text |
signin_with | signup_with | continue_with | signin |
signin_with |
The button text. |
shape |
rectangular | pill | circle | square |
rectangular |
The button shape. |
logo_alignment |
left | center |
left |
The Google logo alignment. |
width |
number |
The button width, in pixels. | |
locale |
string |
The button language. |
<script lang="ts">
// routes/+page.svelte
import { SignInWithGoogleButton } from 'svauth/components';
</script>
<SignInWithGoogleButton />
ISC
Portions of this page are reproduced from work created and shared by Google and used according to terms described in the Creative Commons 4.0 Attribution License.