Credentials authentication
Auth.js is built in a way that is flexible to integrate it with any authentication back-end you or your company may already have.
This library has been designed to handle the user session client-wise, to support multiple authentication methods (OAuth, Email, etc...) so that you're not forced to run your own authentication service.
In case you already have an authentication service, you can use the Credentials Provider, which will just forward the credentials inserted by the user in the login form to your service.
For this tutorial, we're going to use Auth.js example app as a base.
The functionality provided for credentials based authentication is intentionally limited to discourage use of passwords due to the inherent security risks associated with them and the additional complexity associated with supporting usernames and passwords.
Integrating the Credentials Provider is as simple as initializing it in the Auth.js configuration file:
- Next.js
- SvelteKit
- SolidStart
- Vanilla (No Framework)
import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"
export default NextAuth({
providers: [
CredentialsProvider({
async authorize(credentials) {
const authResponse = await fetch("/users/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(credentials),
})
if (!authResponse.ok) {
return null
}
const user = await authResponse.json()
return user
},
}),
],
})
Prerequisites
This tutorial assumes you have a SvelteKit application set up. If you don't, you can follow the SvelteKit tutorial to get started.
Installing Auth.js
- npm
- Yarn
- pnpm
npm install @auth/core @auth/sveltekit
yarn add @auth/core @auth/sveltekit
pnpm add @auth/core @auth/sveltekit
Adding environment variables
In your project root, create a .env.local
file and add the AUTH_SECRET
environment variable:
AUTH_SECRET="This is an example"
AUTH_SECRET
is a random string used by the library to encrypt tokens and email verification hashes, and it's mandatory to keep things secure! 🔥 🔐 . You can use:
openssl rand -base64 32
or https://generate-secret.vercel.app/32 to generate a random value for it.
Create server hook
Create the following Server hook file. This route contains the necessary configuration for Auth.js, as well as the dynamic route handler:
import { SvelteKitAuth } from "@auth/sveltekit"
import Credentials from '@auth/core/providers/credentials'
export const handle = SvelteKitAuth({
providers: [
Credentials({
// You can specify which fields should be submitted, by adding keys to the `credentials` object.
// e.g. domain, username, password, 2FA token, etc.
credentials: {
username: {},
password: {}
},
authorize: async (credentials) => {
let user = null
// logic to salt and hash password
const pwHash = saltAndHashPassword(credentials.password)
// logic to verify if user exists
user = await getUserFromDb(credentials.username, pwHash)
if (!user) {
throw new Error('User not found.')
}
// return json object with the user data
return user
}
})
],
})
Exposing the session via page store
Auth.js provides us a getSession, function to access the session data and status, to call from the event.locals
variable. We can now just call it and add it to our $page
store.
import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async (event) => {
return {
session: await event.locals.getSession()
};
};
Consuming the session via page store
You can use the $page.data.session
variable from anywhere on your page. Learn more about SvelteKit's page store in the SvelteKit docs.
<script>
import { signIn, signOut } from '@auth/sveltekit/client'
import { page } from '$app/stores'
let email = ''
let password = ''
</script>
<div>
{#if $page.data.session?.user}
<p>Signed in as {$page.data.session.user.email}</p>
<button on:click={ () => signOut() }>Sign out</button>
{:else}
<form>
<label>
Email
<input name="email" type="email" bind:value={email}>
</label>
<label>
Password
<input name="password" type="password" bind:value={password}>
</label>
<button on:click={() => signIn('credentials', { email, password })}>Log in</button>
</form>
{/if}
</div>
Protecting API Routes
To protect your API Routes (blocking unauthorized access to resources), you can use locals.getSessions()
just like in the layouts file to know whether a session exists or not:
import { json, error } from "@sveltejs/kit"
import type { RequestEvent } from "./$types"
export async function GET({ locals }: RequestEvent) {
const session = await locals.getSession()
if (!session?.user) {
throw error(401, "You must sign in to view movies.")
}
return json({
movies: [
{ title: "Alien vs Predator", id: 1 },
{ title: "Reservoir Dogs", id: 2 },
],
})
}
TODO SolidStart
TODO Core
Check the Credentials Provider options for further customization
Note that we only need to define an authorize
method that is in charge of receiving the credentials inserted by the user and call the authorization service.
If you're using TypeScript, you can augment the User
interface to match the response of your authorize
callback, so whenever you read the user in other callbacks (like the jwt
) the type will match correctly.