feat: nuxt ssr auth tutorial

This commit is contained in:
loks0n
2024-02-09 21:29:15 +00:00
parent 3b3c12a66c
commit 2e3cff571a
11 changed files with 401 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/nuxt-ssr-auth/step-1');
};

View File

@@ -0,0 +1,20 @@
---
layout: tutorial
title: Server-side authentication with Nuxt
description: Add SSR authentication to your Nuxt app with Appwrite
step: 1
difficulty: beginner
readtime: 20
framework: Nuxt
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.
This tutorials shows how Appwrite can help you add authentication to your Nuxt app using server-side rendering (SSR).
# Before you start {% #before-you-start %}
Even if you've never tried Appwrite, you will get an idea of what it'll feel like to build with Vue and Appwrite.
If you're inspired and wish to follow along, make sure you've followed [Start with Nuxt](https://appwrite.io/docs/quick-starts/nuxt) first.
Clone the [demos-for-vue](https://github.com/appwrite/demos-for-vue) examples and follow along with the source code.

View File

@@ -0,0 +1,39 @@
---
layout: tutorial
title: Create project
description: Add authentication to a Nuxt project using Appwrite.
step: 2
---
Create a Vue project using [Nuxt](https://nuxt.com/docs/getting-started/installation#new-project).
```sh
npx nuxi@latest init my-nuxt-project
```
The command will give you a prompt with several options/
The prompt will be something similar to this.
```sh
Which package manager would you like to use?
● npm
Initialize git repository?
● Yes
```
After the prompt is finished, you can head over to the newly create project.
```sh
cd my-nuxt-project
```
# Adding `node-appwrite` to your Vue app
Appwrite provides a Node SDK that can be used in your Nuxt 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/nuxt)
```sh
npm install node-appwrite
```

View File

@@ -0,0 +1,73 @@
---
layout: tutorial
title: Initialize SDK
description: Add authentication to a Nuxt 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.
{% info title="Appwrite client security" %}
We recommend creating a new instance of the Appwrite client for each request. This ensures that the client is not shared between requests and that the session is not shared between users.
{% /info %}
```js
// server/lib/appwrite.js
import { Client, Account } from "node-appwrite";
export const SESSION_COOKIE = "my-custom-session";
export function createAppwriteClient(event) {
const config = useRuntimeConfig(event);
const client = new Client()
.setEndpoint(config.public.appwriteEndpoint)
.setProject(config.public.appwriteProjectId);
client.setKey(config.appwriteApiKey);
const session = getCookie(event, SESSION_COOKIE);
if (session) {
client.setSession(session);
}
return {
get account() {
return new Account(client);
},
};
}
```
`config.appwriteApiKey`, `config.public.appwriteEndpoint` and `config.public.appwriteProjectId` are Nuxt runtime configuration variables. Create a [nuxt.config.js](https://nuxt.com/docs/api/configuration/nuxt-config) file in the root of your project and add the following code to set the runtime configuration.
```js
export default defineNuxtConfig({
runtimeConfig: {
appwriteKey: process.env.APPWRITE_KEY,
public: {
appwriteEndpoint: process.env.PUBLIC_APPWRITE_ENDPOINT,
appwriteProjectId: process.env.PUBLIC_APPWRITE_PROJECT_ID,
},
},
});
```
Now you can use `.env` files to set the environment variables for your project. Retrieve the values for these variables from the Appwrite console. The `PUBLIC_APPWRITE_ENDPOINT` and `PUBLIC_APPWRITE_PROJECT_ID` are the endpoint and project ID for your Appwrite project. The `APPWRITE_KEY` is the API key for your Appwrite project.
For this tutorial you'll need an API key with the following scopes:
- `accounts.read`
- `accounts.write`
- `sessions.read`
- `sessions.write`
For example, your `.env` might look something similar to this.
```text
APPWRITE_KEY=<YOUR_APPWRITE_KEY>
PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
PUBLIC_APPWRITE_PROJECT_ID=<YOUR_APPWRITE_PROJECT_ID>
```

View File

@@ -0,0 +1,48 @@
---
layout: tutorial
title: Add server middleware
description: Add authentication to a Nuxt project using Appwrite.
step: 4
---
Nuxt server middle are functions that run on the server before a route is displayed to the user. Nuxt context allows you to store data for the lifecycle of 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 `server/middleware` directory called `auth.js`:
```js
// server/middleware/auth.js
import { createAppwriteClient } from "../lib/appwrite";
export default defineEventHandler(async (event) => {
const { account } = createAppwriteClient(event);
try {
event.context.user = await account.get();
} catch (error) {}
});
```
To ensure the `context` 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";
declare module "h3" {
interface H3EventContext {
user?: Models.User<Models.Preferences<any>>;
}
}
```
Now, use the `context` object in the home page to redirect based on the user's login status. Create a new file in the `server/routes` directory called `index.js`:
```js
export default defineEventHandler(async (event) => {
if (event.context.user) {
await sendRedirect(event, "/account");
}
await sendRedirect(event, "/signin");
});
```
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,56 @@
---
layout: tutorial
title: Create sign in page
description: Add authentication to a Nuxt project using Appwrite.
step: 5
---
We can now implement our sign in page. Create a `signin.vue` file in the `pages` directory:
```vue
<!-- pages/signin.vue -->
<template>
<form method="post" action="/api/signin">
<input id="email" name="email" placeholder="Email" type="email" />
<input
id="password"
name="password"
placeholder="Password"
type="password"
/>
<button type="submit">Sign in</button>
</form>
</template>
```
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 Nuxt form actions we create a `signin.post.js` file in the `server/api` directory:
```javascript
// server/api/signin.post.js
import { SESSION_COOKIE, createAppwriteClient } from "~/server/lib/appwrite";
export default defineEventHandler(async (event) => {
// Extract the form data
const formData = await readFormData(event);
const email = formData.get("email") as string;
const password = formData.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
setCookie(event, SESSION_COOKIE, session.secret, {
expires: new Date(session.expire),
path: "/",
httpOnly: true,
secure: true,
sameSite: "strict",
});
// Redirect to the account page.
await sendRedirect(event, "/account");
});
```

View File

@@ -0,0 +1,49 @@
---
layout: tutorial
title: Create account page
description: Add authentication to a Nuxt 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 `pages` directory called `account.vue` and add the following code:
```vue
<!-- pages/account.vue -->
<script setup>
const { context } = useEvent();
const { user } = context;
</script>
<template>
<ul>
<li>
<strong>Email:</strong> {{ user.email }}
</li>
<li>
<strong>Name:</strong> {{ user.name }}
</li>
<li>
<strong>ID: </strong> {{ user.$id }}
</li>
</ul>
<form method="post" action="/api/signout">
<button type="submit">Log out</button>
</form>
</template>
```
This page will display the user's email, name, and ID. It also contains a form that will log the user out when submitted. The form will send a `POST` request to the `/api/signout` endpoint. We need to create this endpoint in the server. Create a new file in the `server/routes/api` directory called `signout.post.js` and add the following code:
```javascript
// server/routes/api/signout.post.js
import { SESSION_COOKIE, createAppwriteClient } from "~/server/lib/appwrite";
export default defineEventHandler(async (event) => {
const { account } = createAppwriteClient(event);
await account.deleteSession("current");
deleteCookie(event, SESSION_COOKIE);
await sendRedirect(event, "/signin");
});
```

View File

@@ -0,0 +1,78 @@
---
layout: tutorial
title: Adding OAuth2 authentication with SSR
description: Add authentication to a Nuxt 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.
```vue
<!-- pages/signin.vue -->
<!-- ... existing sign in form -->
<form action="/api/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
// server/routes/api/oauth2.post.js
import { createAppwriteClient } from "~/server/lib/appwrite";
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig(event);
const { account } = createAppwriteClient(event);
const redirectUrl = await account.createOAuth2Token(
"github", // OAuth2 provider
`${config.public.appwriteEndpoint}/api/oauth2`, // Success URL: redirect back to the /oauth2 route
`${config.public.appwriteEndpoint}/signin` // Failure URL: redirect to the sign in page
);
// Redirect the user to the OAuth2 provider authorization page
await sendRedirect(event, redirectUrl);
});
```
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 `server/routes/api/oauth2.get.js`.
```js
// server/routes/api/oauth2.post.js
import { SESSION_COOKIE, createAppwriteClient } from "~/server/lib/appwrite";
export default defineEventHandler(async (event) => {
// Extract the userId and secret from the URL query parameters
const { userId, secret } = getQuery(event);
if (!userId || !secret) {
return sendRedirect(event, "/signin");
}
// Create the Appwrite client
const { account } = createAppwriteClient(event);
// Exchange the token userId and secret for a session
const session = await account.createSession(userId, secret);
// Set the session cookie
setCookie(event, SESSION_COOKIE, session.secret, {
expires: new Date(session.expire),
path: "/",
httpOnly: true,
secure: true,
sameSite: "strict",
});
// Redirect the user to the account page
await sendRedirect(event, "/account");
});
```

View File

@@ -0,0 +1,11 @@
---
layout: tutorial
title: All set
description: Add authentication to a Nuxt project using Appwrite.
step: 8
---
If you want to see the complete source code with styling, see the [demos-for-vue](https://github.com/appwrite/demos-for-vue/tree/main/server-side-rendering) repository.
# Other authentication methods {% #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).