feat: svelte ssr auth

This commit is contained in:
loks0n
2024-02-06 10:52:07 +00:00
parent 9531daca4d
commit 0e034e9def
11 changed files with 446 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
<script lang="ts">
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';
export let data;
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>
<slot />

View File

@@ -0,0 +1,11 @@
import type { LayoutLoad } from './$types';
export const load: LayoutLoad = ({ url }) => {
const tutorials = import.meta.glob('./**/*.markdoc', {
eager: true
});
return {
tutorials,
pathname: url.pathname
};
};

View File

@@ -0,0 +1,6 @@
import { redirect } from '@sveltejs/kit';
import type { PageLoad } from './$types';
export const load: PageLoad = async () => {
throw redirect(303, '/docs/tutorials/sveltekit-auth/step-1');
};

View File

@@ -0,0 +1,20 @@
---
layout: tutorial
title: Server Side Rendering with SvelteKit
description: Add SSR Authentication to your SvelteKit app with Appwrite
step: 1
difficulty: beginner
readtime: 20
framework: SvelteKit
category: Auth
---
Appwrite takes away the stress of building and maintaining a backend. Appwrite helps implement authentication, databases, file storage, and respond to real-time events with **secure** APIs out of the box.
If you're a Svelte developer, the examples in this guide show you how Appwrite can help you add authentication to Svelte apps faster.
## Before you start
Even if you've never tried Appwrite, you will get an idea of what it'll feel like to build with Svelte and Appwrite.
If you're inspired and wish to follow along, make sure you've followed [Start with Svelte](https://appwrite.io/docs/quick-starts/sveltekit) first.
You can also clone the [Demos for svelte](https://github.com/appwrite/demos-for-svelte) examples and follow along by looking at the code.

View File

@@ -0,0 +1,53 @@
---
layout: tutorial
title: Create project
description: Add Authentication to a SvelteKit project using Appwrite.
step: 2
---
Create a Svelte project using [SvelteKit](https://kit.svelte.dev/docs/creating-a-project).
```sh
npm create svelte@latest
```
The command will give you a prompt with several project types. We'll be starting with a skeleton project.
The prompt will be something similar to this.
```sh
create-svelte version 3.2.0
┌ Welcome to SvelteKit!
◇ Where should we create your project?
│ my-svelte-project
◇ Which Svelte app template?
│ Skeleton project
◇ Add type checking with TypeScript?
│ Yes, using JavaScript with JSDoc comments
◇ Select additional options (use arrow keys/space bar)
│ Add ESLint for code linting
└ Your project is ready!
```
After the prompt is finished, you can head over to the newly create project.
```sh
cd my-svelte-project
npm install
```
## Adding `node-appwrite` to Your Svelte App
Appwrite provides a Node SDK that can be used in your Svelte apps. You can use Appwrite by installing the Node SDK as an NPM package.
The Node SDK is intended for server-side use. If you want to use Appwrite in a client-side application, you should [use the Web SDK instead](/docs/tutorials/svelte-auth).
```sh
npm install node-appwrite
```

View File

@@ -0,0 +1,65 @@
---
layout: tutorial
title: Initialize SDK
description: Add Authentication to a SvelteKit project using Appwrite.
step: 3
---
Before you can use Appwrite, you need to create the Appwrite `Client` and set the project ID and endpoint.
The client is then used to create services like `Databases` and `Account`, so they all point to the same Appwrite project.
Create a function to build services you need in a file like `src/lib/server/appwrite.js` and **exporting the instances**.
As part of the function, set the current user's session if they are logged in. This is done by accessing the session cookie from the request and calling the `setSession(session)` with the cookie value.
```js
// src/lib/server/appwrite.js
import { Client, Account } from 'node-appwrite';
import { APPWRITE_KEY } from '$env/static/private';
import { PUBLIC_APPWRITE_ENDPOINT, PUBLIC_APPWRITE_PROJECT_ID } from '$env/static/public';
export const SESSION_COOKIE = 'my-custom-session';
export function createAppwriteClient(event) {
// Build the Appwrite client
const client = new Client()
.setEndpoint(PUBLIC_APPWRITE_ENDPOINT)
.setProject(PUBLIC_APPWRITE_PROJECT_ID);
// Set the API key for the client
client.setKey(APPWRITE_KEY);
// Extract our custom domain's session cookie from the request
const session = event.cookies.get(SESSION_COOKIE);
if (session) client.setSession(session);
// Return the services we want to use.
// This allows us to use syntax like:
// import { createAppwriteClient } from '$lib/server/appwrite';
//
// const { account } = createAppwriteClient(event);
// const user = await account.get();
return {
get account() {
return new Account(client);
}
};
}
```
`APPWRITE_KEY`, `PUBLIC_APPWRITE_ENDPOINT` and `PUBLIC_APPWRITE_PROJECT` are environment variables that are exported in your project's [.env file](https://kit.svelte.dev/docs/modules#$env-dynamic-public).
You can get the values for these variables from the Appwrite console. The `PUBLIC_APPWRITE_ENDPOINT` and `PUBLIC_APPWRITE_PROJECT` are the endpoint and project ID for your Appwrite project.
For example, your `.env` might look something similar to this.
```text
APPWRITE_KEY=4d5e6f7g8h9i0j1k2l3m4n5o
PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
PUBLIC_APPWRITE_PROJECT=642sdddf85b440dc7e5bf
```
For this tutorial you'll need an API key with the following scopes:
- `accounts.read`
- `accounts.write`
- `sessions.read`
- `sessions.write`

View File

@@ -0,0 +1,71 @@
---
layout: tutorial
title: Add a server hook
description: Add Authentication to a SvelteKit project using Appwrite.
step: 4
---
SvelteKit hooks which are functions that run on the server before a page is displayed to the user. SvelteKit locals are a way to store data that is specific to the current request. We can use this to store the user's account data, so that it is available to all pages.
Create a new file in the `src/hooks` directory called `server.js`:
```js
// src/hooks.server.js
import { createAppwriteClient } from '$lib/server/appwrite';
export async function handle({ event, resolve }) {
// Use our helper function to create the Appwrite client.
const { account } = createAppwriteClient(event);
// Let's store the current logged in user in locals,
// for easy access in our other routes.
try {
const user = await account.get();
event.locals.user = user;
} catch (error) {}
// Continue with the request.
return resolve(event);
}
```
To ensure the `locals` object is typed correctly, we can add a type definition for it in the `env.d.ts` file:
```ts
import type { Models } from 'node-appwrite';
import type { AppwriteService } from '$lib/server/appwrite';
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
interface Locals {
user: Models.User<Preferences> | null;
}
// interface PageData {}
// interface Platform {}
}
}
export {};
```
Now, use the `locals` object in the home page to redirect based on the user's login status. Create a new file in the `src/routes` directory called `+page.server.js`:
```js
import { createAppwriteClient } from '$lib/server/appwrite.js';
import { redirect } from '@sveltejs/kit';
export async function load({ locals }) {
// Access our user from locals.
if (!locals.user) {
// If no user is logged in, redirect to the sign in page.
throw redirect(301, '/signin');
}
// If the user is logged in, redirect to the account page.
throw redirect(301, '/account');
}
```
When a user visits the home page, they will be redirected to the sign in page if they are not logged in, or to the account page if they are logged in.

View File

@@ -0,0 +1,50 @@
---
layout: tutorial
title: Create sign in page
description: Add Authentication to a SvelteKit project using Appwrite.
step: 5
---
We can now implement our sign in page. Create a `+page.svelte` file in the `src/routes/signin` directory:
```svelte
<!-- src/routes/sigin/+page.svelte -->
<form method="post">
<input id="email" placeholder="Email" type="email" />
<input id="password" placeholder="Password" type="password" />
<button type="submit">Sign in</button>
</form>
```
This is an HTML form with an email and password input. When the form is submitted, we want to send the email and password to Appwrite to authenticate the user. To use SvelteKit form actions we create a `+page.server.js` file in the same directory:
```javascript
import { SESSION_COOKIE, createAppwriteClient } from '$lib/server/appwrite.js';
import { redirect } from '@sveltejs/kit';
export const actions = {
default: async (event) => {
// Extract the form data.
const form = await event.request.formData();
const email = form.get('email') as string;
const password = form.get('password') as string;
// Create the Appwrite client.
const { account } = createAppwriteClient(event);
// Create the session using the client
const session = await account.createEmailPasswordSession(email, password);
// Set the session cookie with the secret
event.cookies.set(SESSION_COOKIE, session.secret, {
sameSite: 'strict',
expires: new Date(session.expire),
secure: true,
path: '/'
});
// Redirect to the account page.
throw redirect(301, '/account');
}
};
```

View File

@@ -0,0 +1,65 @@
---
layout: tutorial
title: Create account page
description: Add Authentication to a SvelteKit project using Appwrite.
step: 6
---
Now the end-user is able to sign in, we can create the account page. This page will display basic information about the user, and allow the user to log out. Create a new file in the `src/routes/account` directory called `+page.server.js` and add the following code:
```js
// src/routes/account/+page.server.js
import { SESSION_COOKIE, createAppwriteClient } from '$lib/server/appwrite.js';
import { redirect } from '@sveltejs/kit';
export async function load({ locals }) {
// Logged out users can't access this page.
if (!locals.user) throw redirect(301, '/signin');
// Pass the stored user local to the page.
return {
user: locals.user
};
}
// Define our log out endpoint/server action.
export const actions = {
default: async (event) => {
// Create the Appwrite client.
const { account } = createAppwriteClient(event);
// Delete the session on Appwrite, and delete the session cookie.
await account.deleteSession('current');
event.cookies.delete(SESSION_COOKIE);
// Redirect to the sign in page.
throw redirect(301, '/signin');
}
};
```
Create a new file in the `src/routes/account` directory called `+page.svelte` and add the following code:
```svelte
<script>
export let data;
const { user } = data
</script>
<ul>
<li>
<strong>Email:</strong> {user.email}
</li>
<li>
<strong>Name:</strong> {user.name}
</li>
<li>
<strong>ID: </strong> {user.$id}
</li>
</ul>
<form action="/account" method="post">
<button type="submit">Log out</button>
</form>
```

View File

@@ -0,0 +1,84 @@
---
layout: tutorial
title: Adding OAuth2 authentication with SSR
description: Add Authentication to a SvelteKit project using Appwrite.
step: 7
---
To support the OAuth2 flow, we first redirect the user to the OAuth2 provider, and then handle the callback from the OAuth2 provider.
To redirect, add a button to our sign in page that redirects the user to the OAuth2 provider.
```svelte
<!-- src/routes/sigin/+page.svelte -->
<!-- ... existing sign in form -->
<form action="/oauth2" method="post">
<input type="hidden" name="provider" value="github" />
<button type="submit">Sign in with GitHub</button>
</form>
```
Add a new server route to handle the redirect.
```js
// src/routes/signin/+page.server.js
// ..
import { SESSION_COOKIE, createAppwriteClient } from '$lib/server/appwrite.js';
import { redirect } from '@sveltejs/kit';
export const actions = {
// ... existing actions
oauth2: async (event) => {
const formData = await event.request.formData();
const provider = formData.get('provider') ?? 'github';
const { account } = createAppwriteClient();
const redirectURL = account.createOAuth2Token(
provider,
'<YOUR_WEBSITE_DOMAIN>/oauth2',
'<YOUR_WEBSITE_DOMAIN>/signin'
);
throw redirect(url);
}
};
```
The `createOAuth2Token` method redirects the user to the OAuth2 provider, and then the OAuth2 provider redirects the user back to the `/oauth2` route with the `userId` and `secret` URL query parameters.
Handle the callback and create a session for the user. Create a new server route at `src/routes/oauth2/+server.js`:
```js
// src/routes/oauth2/+server.js
import { SESSION_COOKIE, createAppwriteClient } from '$lib/server/appwrite';
export async function GET(event) {
// We should have a `userId` and `secret` query parameters in the URL
const userId = event.url.searchParams.get('userId') as string;
const secret = event.url.searchParams.get('secret') as string;
// Exchange the token `userId` and `secret` for a session
const { account } = createAppwriteClient(event);
const session = await account.createSession(userId, secret);
// Redirect to the account page, and set the session cookie
const headers = new Headers({
location: '/account',
'set-cookie': event.cookies.serialize(SESSION_COOKIE, session.secret, {
sameSite: 'strict',
expires: new Date(session.expire),
secure: true,
path: '/'
})
});
return new Response(null, { status: 302, headers });
}
```

View File

@@ -0,0 +1,11 @@
---
layout: tutorial
title: All set
description: Add Authentication to a SvelteKit project using Appwrite.
step: 8
---
If you want to see the complete source code, see the [demos-for-svelte](https://github.com/appwrite/demos-for-svelte/tree/main/server-side-rendering) repository.
## Other authentication methods
Appwrite also supports OAuth, passwordless login, anonymous login, and phone login.
Learn more about them in the [authentication guide](https://appwrite.io/docs/products/auth).