Merge branch 'main' of github.com:appwrite/website into replace-prefix

This commit is contained in:
Arman
2024-03-10 16:16:43 +01:00
38 changed files with 530 additions and 446 deletions

View File

@@ -2,8 +2,15 @@ import { base } from '$app/paths';
import type { Tutorial } from '$markdoc/layouts/Tutorial.svelte';
const framework_order = ['React', 'Vue', 'SvelteKit', 'Stripe', 'Refine'];
const category_order = ['Web', 'Mobile and native', 'Server', 'Auth', 'Databases', 'Storage', 'Functions'];
const category_order = [
'Web',
'Mobile and native',
'Server',
'Auth',
'Databases',
'Storage',
'Functions'
];
export async function load() {
const tutorialsGlob = import.meta.glob('./**/step-1/+page.markdoc', {
@@ -20,7 +27,7 @@ export async function load() {
.replace('/+page.markdoc', '')
.replace('/step-1', '');
const tutorialName = slug.slice(slug.lastIndexOf('/') + 1);
return {
title: frontmatter.title,
framework: frontmatter.framework,
@@ -29,6 +36,7 @@ export async function load() {
href: `${base}/docs/tutorials/${tutorialName}`
};
})
.filter((tutorial) => !tutorial.draft)
.sort((a, b) => {
// Sort by framework order
const frameworkIndexA = framework_order.indexOf(a.framework as string);
@@ -44,23 +52,23 @@ export async function load() {
// Else, sort by title
return a.title.toLowerCase().localeCompare(b.title.toLowerCase());
});
const tutorials = Object.entries(
allTutorials.reduce((acc: { [key: string]: any[] }, item) => {
// If the category does not exist in the accumulator, initialize it
if (!acc[item.category]) {
acc[item.category] = [];
}
// Push the current item into the appropriate category
acc[item.category].push(item);
const tutorials = Object.entries(
allTutorials.reduce((acc: { [key: string]: any[] }, item) => {
// If the category does not exist in the accumulator, initialize it
if (!acc[item.category]) {
acc[item.category] = [];
}
return acc;
}, {})
).map(([title, tutorials]) => ({ title, tutorials }));
tutorials.sort((a, b) => category_order.indexOf(a.title) - category_order.indexOf(b.title));
// Push the current item into the appropriate category
acc[item.category].push(item);
return acc;
}, {})
).map(([title, tutorials]) => ({ title, tutorials }));
tutorials.sort((a, b) => category_order.indexOf(a.title) - category_order.indexOf(b.title));
return {
tutorials,
tutorials
};
}

View File

@@ -14,6 +14,9 @@ If you're a Astro developer, the examples in this guide show you how Appwrite ca
# 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 Astro and Appwrite.
Before following this tutorial, have the following prepared:
- A recent version of [Node.js](https://nodejs.org/en/download/) installed on your system.
- A basic knowledge of Astro.
Clone the [demos-for-astro](https://github.com/appwrite/demos-for-astro) examples and follow along with the source code.

View File

@@ -26,7 +26,7 @@ Install dependencies? Yes
Initialize a new git repository? Yes
```
After the prompt is finished, you can head over to the newly create project.
After the prompt is finished, you can head over to the newly created project.
```sh
cd my-astro-project
@@ -38,4 +38,12 @@ Appwrite provides a Node SDK that can be used in your Astro apps. You can use Ap
```sh
npm install node-appwrite
```
# Add Node adapter to Astro {% #add-node-to-astro %}
To use Astro as an SSR framework, you need to add the Node adapter to your project. Run the following command:
```sh
npx astro add node
```

View File

@@ -13,13 +13,14 @@ Create a function to build services you need in a file like `src/server/appwrite
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" %}
Notice that `createAppwriteClient` returns **a new instance** of the Appwrite Client.
Notice that `createAdminClient` and `createSessionClient` returns **a new instance** of the Appwrite Client.
When using Appwrite in server-integrations, it's important to **never share a `Client` instance** between two requests.
Doing so could create security vulnerabilities.
{% /info %}
```js
// src/server/appwrite.js
import { Client, Account } from "node-appwrite";
// The name of your cookie that will store the session
@@ -29,7 +30,7 @@ export const SESSION_COOKIE = "my-custom-session";
export function createAdminClient() {
const client = new Client()
.setEndpoint(import.meta.env.PUBLIC_APPWRITE_ENDPOINT)
.setProject(import.meta.env.PUBLIC_APPWRITE_PROJECT_ID)
.setProject(import.meta.env.PUBLIC_APPWRITE_PROJECT)
.setKey(import.meta.env.APPWRITE_KEY); // Set the API key here!
// Return the services you need
@@ -44,15 +45,17 @@ export function createAdminClient() {
export function createSessionClient(request) {
const client = new Client()
.setEndpoint(import.meta.env.PUBLIC_APPWRITE_ENDPOINT)
.setProject(import.meta.env.PUBLIC_APPWRITE_PROJECT_ID);
.setProject(import.meta.env.PUBLIC_APPWRITE_PROJECT);
// Get the session cookie from the request and set the session
const cookies = parseCookies(request.headers.get("cookie") ?? "");
const session = cookies.get(SESSION_COOKIE);
if (session) {
client.setSession(session);
if (!session) {
throw new Error("Session cookie not found");
}
client.setSession(session);
// Return the services you need
return {
get account() {
@@ -72,9 +75,15 @@ function parseCookies(cookies) {
}
```
`import.meta.env.APPWRITE_KEY`, `import.meta.env.PUBLIC_APPWRITE_ENDPOINT` and `import.meta.env.PUBLIC_APPWRITE_PROJECT_ID` are environment variables that are exported in your project's [.env file](https://kit.Astro.dev/docs/modules#$env-dynamic-public).
`import.meta.env.APPWRITE_KEY`, `import.meta.env.PUBLIC_APPWRITE_ENDPOINT` and `import.meta.env.PUBLIC_APPWRITE_PROJECT` are environment variables that are exported in your project's [.env file](https://kit.Astro.dev/docs/modules#$env-dynamic-public).
You can get the values for these variables from the Appwrite console.
For example, your `.env` might look something similar to this.
```env
APPWRITE_KEY=<YOUR_API_KEY>
PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
PUBLIC_APPWRITE_PROJECT=<YOUR_PROJECT_ID>
```
The `PUBLIC_APPWRITE_ENDPOINT` is the endpoint of your Appwrite project, and the `PUBLIC_APPWRITE_PROJECT` is the ID of the project you want to use.
You can get the values for these variables from the Appwrite console.
@@ -87,7 +96,7 @@ You can get the values for these variables from the Appwrite console.
![Create project screen](/images/docs/quick-starts/create-project.png)
{% /only_light %}
The `APPWRITE_KEY` is an Appwrite API key with the necessary permissions to read and write accounts and sessions.
The `APPWRITE_KEY` is an Appwrite API key with the necessary permissions to create new sessions.
For this tutorial you'll need an API key with the following scopes:
@@ -103,10 +112,3 @@ For this tutorial you'll need an API key with the following scopes:
![Server integrations](/images/docs/quick-starts/integrate-server.png)
{% /only_light %}
For example, your `.env` might look something similar to this.
```text
APPWRITE_KEY=<YOUR_API_KEY>
PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
PUBLIC_APPWRITE_PROJECT=<YOUR_PROJECT_ID>
```

View File

@@ -7,16 +7,17 @@ step: 4
Astro middleware are functions that run on the server before a page is displayed to the user. Astro locals are a way to store data that is specific to the current request. We can use these features to store the user's account data, so that it is available to all pages.
Create a new file in the `src/server` directory called `middleware.js`:
Create a new file in the `src/` directory called `middleware.js`:
```js
// src/server/middleware.js
// src/middleware.js
import { defineMiddleware } from "astro:middleware";
import { createSessionClient } from "./server/appwrite";
export const onRequest = defineMiddleware(async ({ request, locals }, next) => {
const { account } = createSessionClient(request);
try {
const { account } = createSessionClient(request);
locals.user = await account.get();
} catch {}
@@ -24,23 +25,22 @@ export const onRequest = defineMiddleware(async ({ request, locals }, next) => {
});
```
To ensure the `locals` object is typed correctly, we can add a type definition for it in the `env.d.ts` file:
To ensure the `locals` object is typed correctly, we can add a type definition for it in a `env.d.ts` file at the root of the project:
```ts
// <reference types="astro/client" />
/// <reference types="astro/client" />
type Models = import("node-appwrite").Models;
declare namespace App {
interface Locals {
user?:
| import("node-appwrite").Models.User<
import("node-appwrite").Models.Preferences<{}>
>
user?: Models.User<Models.Preferences>;
}
}
interface ImportMetaEnv {
readonly PUBLIC_APPWRITE_ENDPOINT: string;
readonly PUBLIC_APPWRITE_PROJECT_ID: string;
readonly PUBLIC_APPWRITE_PROJECT: string;
readonly APPWRITE_KEY: string;
}
@@ -49,7 +49,8 @@ interface ImportMeta {
}
```
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/pages` directory called `index.astro`:
Now, use the `locals` object in the home page to redirect based on the user's login status.
Overwrite the file in the `src/pages` directory called `index.astro`:
```js
---
@@ -58,8 +59,8 @@ if (user) {
return Astro.redirect("/account");
}
return Astro.redirect("/signin");
return Astro.redirect("/signup");
---
```
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.
If the user is logged in, they will be redirected to the account page. If they are not logged in, they will be redirected to the sign up page.

View File

@@ -1,11 +1,11 @@
---
layout: tutorial
title: Create sign in page
title: Create sign up page
description: Add authentication to a Astro project using Appwrite.
step: 5
---
We can now implement our sign in page. Create a `signin.astro` file in the `src/pages` directory:
We can now implement our sign up page. Create a `signin.astro` file in the `src/pages` directory:
```js
---
@@ -14,10 +14,12 @@ if (user) {
return Astro.redirect("/account");
}
---
<form method="POST">
<input id="email" name="email" placeholder="Email" type="email" />
<input id="password" name="email" placeholder="Password" type="password" />
<button class="button is-full-width" type="submit"> Sign in</button>
<input id="password" name="password" placeholder="Password" type="password" />
<input id="name" name="name" placeholder="Name" type="text" />
<button type="submit"> Sign up</button>
</form>
```
@@ -26,25 +28,28 @@ This is an HTML form with an email and password input. When the form is submitte
```js
---
import { SESSION_COOKIE, createAdminClient } from "../server/appwrite";
import { ID } from "node-appwrite";
// ... existing javascript
if (Astro.request.method === "POST") {
// Extract the form data
const data = await Astro.request.formData();
const email = data.get("email") as string;
const password = data.get("password") as string;
const email = data.get("email");
const password = data.get("password");
const name = data.get("name");
// Create the admin client
const { account } = createAdminClient();
// Create the email password session
await account.create(ID.unique(), email, password, name);
const session = await account.createEmailPasswordSession(email, password);
// Set the session cookie
Astro.cookies.set(SESSION_COOKIE, session.secret, {
path: "/",
expires: new Date(session.expires),
expires: new Date(session.expire),
sameSite: "strict",
secure: true,
httpOnly: true,

View File

@@ -5,7 +5,7 @@ description: Add authentication to a Astro 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/pages` directory called `account.astro` and add the following code:
Now the end-user is able to sign up, 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/pages` directory called `account.astro` and add the following code:
```js
---
@@ -14,7 +14,7 @@ import { SESSION_COOKIE, createSessionClient } from "../server/appwrite";
// Redirect the user if not signed in
const { user } = Astro.locals;
if (!user) {
return Astro.redirect("/signin");
return Astro.redirect("/signup");
}
// Handle form action
@@ -28,8 +28,8 @@ if (Astro.request.method === "POST") {
// Delete the session cookie
Astro.cookies.delete(SESSION_COOKIE);
// Redirect the user to sign in page
return Astro.redirect("/signin");
// Redirect the user to sign up page
return Astro.redirect("/signup");
}
---
<ul>

View File

@@ -1,22 +1,21 @@
---
layout: tutorial
title: OAuth2 authentication with SSR
title: OAuth authentication with SSR
description: Add authentication to a Astro 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 support the OAuth flow, we first redirect the user to the OAuth provider, and then handle the callback from the OAuth provider.
To redirect, add a button to our sign in page that redirects the user to the OAuth2 provider.
To redirect, add a button to our sign up page that redirects the user to the OAuth provider.
```js
<!-- src/pages/signin.astro -->
<!-- src/pages/signup.astro -->
<!-- ... existing sign in form -->
<!-- ... existing sign up form -->
<form action="/oauth2" method="post">
<input type="hidden" name="provider" value="github" />
<button type="submit">Sign in with GitHub</button>
<form action="/oauth" method="post">
<button type="submit">Sign up with GitHub</button>
</form>
```
@@ -24,6 +23,7 @@ Add a new `POST` route to handle the redirect.
```js
// src/pages/oauth.js
import { createAdminClient } from "../server/appwrite";
import { OAuthProvider } from "node-appwrite";
@@ -31,11 +31,11 @@ export const POST = async ({ redirect, url }) => {
// Create the Appwrite client
const { account } = createAdminClient();
// Create an OAuth2 token
// Create an OAuth token
const redirectUrl = await account.createOAuth2Token(
OAuthProvider.GitHub,
`${url.protocol}//${url.hostname}/oauth`,
`${url.protocol}//${url.hostname}/signin`
OAuthProvider.Github,
`${url.origin}/oauth`,
`${url.origin}/signup`
);
// Redirect the end-user to the OAuth2 provider authentication
@@ -43,28 +43,27 @@ export const POST = async ({ redirect, url }) => {
};
```
The `createOAuth2Token` method returns a URL to the OAuth2 provider. After authentication the OAuth2 provider redirects the user back to the `/oauth2` route with the `userId` and `secret` URL query parameters.
The `createOAuth2Token` method returns a URL to the OAuth provider. After authentication the OAuth provider redirects the user back to the `/oauth` route with the `userId` and `secret` URL query parameters.
Create a new `GET` route to handle the callback and create a session for the user.
```js
// src/pages/oauth.js
// ... existing imports
import { createAdminClient, SESSION_COOKIE } from "../server/appwrite";
import { OAuthProvider } from "node-appwrite";
import { SESSION_COOKIE } from '$lib/server/appwrite';
// ... existing POST handler
// ... existing code
export const GET = async ({ cookies, redirect, url }) => {
// Extract the token data from the URL params
export const GET = async ({ redirect, cookies, url }) => {
// Get the user ID and secret from the URL
const userId = url.searchParams.get("userId");
const secret = url.searchParams.get("secret");
// Create the Appwrite client
const { account } = createAdminClient();
// Exchange the token data for a session
// Exchange the token for a session
const session = await account.createSession(userId, secret);
// Set the session cookie

View File

@@ -14,7 +14,11 @@ If you're a Next.js developer, the examples in this guide show you how Appwrite
# 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 Next.js and Appwrite.
Before following this tutorial, have the following prepared:
- A recent version of [Node.js](https://nodejs.org/en/download/) installed on your system.
- A basic knowledge of Next.js and React.
If you're inspired and wish to follow along, make sure you've followed [Start with React](https://appwrite.io/docs/quick-starts/react) first.
Clone the [demos-for-react](https://github.com/appwrite/demos-for-react/tree/main/nextjs) examples and follow along with the source code.

View File

@@ -27,7 +27,7 @@ Would you like to customize the default import alias (@/*)? No
What import alias would you like configured? [Enter]
```
After the prompt is finished, you can head over to the newly create project.
After the prompt is finished, you can head over to the newly created project.
```sh
cd my-app

View File

@@ -9,31 +9,24 @@ The client is then used to create services like `Databases` and `Account`, so th
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" %}
Notice that `createAppwriteClient` returns **a new instance** of the Appwrite Client.
When using Appwrite in server-integrations, it's important to **never share a `Client` instance** between two requests.
Doing so could create security vulnerabilities.
{% /info %}
```js
// src/lib/server/appwrite.js
"use server";
import { Client, Account } from "node-appwrite";
import { parseCookie } from "next/dist/compiled/@edge-runtime/cookies";
import { cookies } from "next/headers";
// The name of your cookie that will store the session
export const SESSION_COOKIE = "my-custom-session";
// Admin client, used to create new accounts
export function createAdminClient() {
export async function createSessionClient() {
const client = new Client()
.setEndpoint(import.meta.env.PUBLIC_APPWRITE_ENDPOINT)
.setProject(import.meta.env.PUBLIC_APPWRITE_PROJECT_ID)
.setKey(import.meta.env.APPWRITE_KEY); // Set the API key here!
.setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT)
.setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT);
const session = cookies().get("my-custom-session");
if (!session || !session.value) {
throw new Error("No session");
}
client.setSession(session.value);
// Return the services you need
return {
get account() {
return new Account(client);
@@ -41,20 +34,12 @@ export function createAdminClient() {
};
}
// Session client, used to make requests on behalf of the logged in user
export function createSessionClient(request) {
export async function createAdminClient() {
const client = new Client()
.setEndpoint(import.meta.env.PUBLIC_APPWRITE_ENDPOINT)
.setProject(import.meta.env.PUBLIC_APPWRITE_PROJECT_ID);
.setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT)
.setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT)
.setKey(process.env.NEXT_APPWRITE_KEY);
// Get the session cookie from the request and set the session
const cookies = parseCookies(request.headers.get("cookie") ?? "");
const session = cookies.get(SESSION_COOKIE);
if (session) {
client.setSession(session);
}
// Return the services you need
return {
get account() {
return new Account(client);
@@ -63,10 +48,27 @@ export function createSessionClient(request) {
}
```
`APPWRITE_KEY`, `NEXT_PUBLIC_APPWRITE_ENDPOINT` and `NEXT_PUBLIC_APPWRITE_PROJECT` are environment variables that are exported in your project's [.env file](https://nextjs.org/docs/app/building-your-application/configuring/environment-variables).
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" %}
Notice that `createAdminClient` and `createSessionClient` returns **a new instance** of the Appwrite Client.
When using Appwrite in server-integrations, it's important to **never share a `Client` instance** between two requests.
Doing so could create security vulnerabilities.
{% /info %}
The `PUBLIC_APPWRITE_ENDPOINT` is the endpoint of your Appwrite project, and the `PUBLIC_APPWRITE_PROJECT` is the ID of the project you want to use.
# Environment variables {% #environment-variables %}
`NEXT_APPWRITE_KEY`, `NEXT_PUBLIC_APPWRITE_ENDPOINT` and `NEXT_PUBLIC_APPWRITE_PROJECT` are environment variables that are exported in your project's [.env file](https://kit.svelte.dev/docs/modules#$env-dynamic-public).
For example, your `.env` might look something similar to this.
```env
NEXT_APPWRITE_KEY=<YOUR_API_KEY>
NEXT_PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
NEXT_PUBLIC_APPWRITE_PROJECT=<YOUR_PROJECT_ID>
```
The `NEXT_PUBLIC_APPWRITE_ENDPOINT` is the endpoint of your appwrite instance , and the `NEXT_PUBLIC_APPWRITE_PROJECT` is the ID of the project you want to use.
You can get the values for these variables from the Appwrite console.
{% only_dark %}
![Create project screen](/images/docs/quick-starts/dark/create-project.png)
@@ -75,7 +77,7 @@ You can get the values for these variables from the Appwrite console.
![Create project screen](/images/docs/quick-starts/create-project.png)
{% /only_light %}
The `APPWRITE_KEY` is an Appwrite API key with the necessary permissions to read and write accounts and sessions.
The `NEXT_APPWRITE_KEY` is an Appwrite API key with the necessary permissions to create new sessions.
For this tutorial you'll need an API key with the following scopes:
@@ -90,10 +92,3 @@ For this tutorial you'll need an API key with the following scopes:
![Server integrations](/images/docs/quick-starts/integrate-server.png)
{% /only_light %}
For example, your `.env` might look something similar to this.
```text
APPWRITE_KEY=<YOUR_API_KEY>
PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
PUBLIC_APPWRITE_PROJECT=<YOUR_PROJECT_ID>
```

View File

@@ -7,32 +7,35 @@ step: 4
Build a utility function to get the logged in user from Appwrite. This function will be used in our components and routes to check if a user is logged in, and access the user's details.
Edit the `src/server/appwrite.js` file to create a new function called `getLoggedInUser`.
Edit the `src/lib/server/appwrite.js` file to create a new function called `getLoggedInUser`.
```js
// ... your createAppwriteClient function
// ... your initilization functions
export async function getLoggedInUser(account) {
export async function getLoggedInUser() {
try {
const { account } = await createSessionClient();
return await account.get();
} catch {}
} catch (error) {
return null;
}
}
```
Now, use the `getLoggedInUser` function in the home page to redirect based on the user's login status. Create a new file in the `app` directory called `+page.jsx`.
Now, use the `getLoggedInUser` function in the home page to redirect based on the user's login status. Create a new file in the `app` directory called `page.jsx`.
```js
import { createSessionClient, getLoggedInUser } from "@/lib/server/appwrite";
import { headers } from "next/headers";
// src/app/page.jsx
import { getLoggedInUser } from "@/lib/server/appwrite";
import { redirect } from "next/navigation";
export default async function Home() {
const { account } = createSessionClient(headers());
const user = await getLoggedInUser();
const user = await getLoggedInUser(account);
if (!user) redirect("/signin");
if (!user) redirect("/signup");
redirect("/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.
The user will be redirected to the sign up page if they are not logged in, or to the account page if they are logged in.

View File

@@ -1,74 +1,78 @@
---
layout: tutorial
title: Create sign in page
title: Create sign up page
description: Add authentication to a Next.js project using Appwrite.
step: 5
---
We can now implement our sign in page. Create a `+page.jsx` file in the `src/app/signin` directory:
We can now implement our sign up page. Create a `page.jsx` file in the `src/app/signup` directory:
```jsx
// src/app/signin/+page.jsx
// src/app/signup/page.jsx
import {
createSessionClient,
getLoggedInUser,
getLoggedInUser
} from "@/lib/server/appwrite";
export default async function SignInPage() {
const { account } = createSessionClient(headers());
const user = await getLoggedInUser(account);
export default async function SignUpPage() {
const user = await getLoggedInUser();
if (user) redirect("/account");
return (
<form action={signInWithEmail}>
<input
id="email"
name="email"
placeholder="Email"
type="email"
autoComplete="off"
/>
<input
id="password"
name="password"
placeholder="Password"
minLength={8}
type="password"
autoComplete="off"
/>
<button type="submit">Sign in</button>
</form>
<>
<form action={signUpWithEmail}>
<input
id="email"
name="email"
placeholder="Email"
type="email"
/>
<input
id="password"
name="password"
placeholder="Password"
minLength={8}
type="password"
/>
<input
id="name"
name="name"
placeholder="Name"
type="text"
/>
<button type="submit">Sign up</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 Next.js form actions we create the `signInWithEmail` function in the same file:
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 Next.js form actions we create the `signUpWithEmail` function in the same file:
```jsx
// src/app/signin/+page.jsx
// src/app/signup/page.jsx
// previous imports ...
import { SESSION_COOKIE, createAdminClient } from "@/lib/server/appwrite";
import { cookies, headers } from "next/headers";
import { ID } from "node-appwrite";
import { createAdminClient } from "@/lib/server/appwrite";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
// the SignInPage component ...
async function signInWithEmail(formData) {
async function signUpWithEmail(formData) {
"use server";
const email = formData.get("email");
const password = formData.get("password");
const name = formData.get("name");
const { account } = createAdminClient();
const { account } = await createAdminClient();
await account.create(ID.unique(), email, password, name);
const session = await account.createEmailPasswordSession(email, password);
cookies().set(SESSION_COOKIE, session.secret, {
cookies().set("my-custom-session", session.secret, {
path: "/",
httpOnly: true,
sameSite: "strict",
@@ -77,6 +81,8 @@ async function signInWithEmail(formData) {
redirect("/account");
}
// the SignUpPage component ...
```
The `signInWithEmail` function is an async function that takes the form data as an argument. It uses the `createAdminClient` function to create an admin Appwrite client and then calls the `createEmailPasswordSession` method on the `account` object. This method takes the email and password as arguments and returns a session object. We then set the session secret in a cookie and redirect the user to the account page.
The `signUpWithEmail` function is an async function that takes the form data as an argument. It uses the `createAdminClient` function to create an admin Appwrite client and then calls the `createEmailPasswordSession` method on the `account` object. This method takes the email and password as arguments and returns a session object. We then set the session secret in a cookie and redirect the user to the account page.

View File

@@ -5,34 +5,32 @@ description: Add authentication to a Next.js 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/app/account` directory called `page.jsx` and add the following code:
Now the end-user is able to sign up, 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/app/account` directory called `page.jsx` and add the following code:
```jsx
// src/app/account/page.jsx
import {
SESSION_COOKIE,
createSessionClient,
getLoggedInUser,
} from "@/lib/server/appwrite";
import { redirect } from "next/navigation";
import { cookies, headers } from "next/headers";
import { cookies } from "next/headers";
async function signOut() {
"use server";
const { account } = createSessionClient(headers());
const { account } = await createSessionClient();
cookies().delete(SESSION_COOKIE);
cookies().delete("my-custom-session");
await account.deleteSession("current");
redirect("/signin");
redirect("/signup");
}
export default async function HomePage() {
const { account } = createSessionClient(headers());
const user = await getLoggedInUser(account);
if (!user) redirect("/signin");
const user = await getLoggedInUser();
if (!user) redirect("/signup");
return (
<>
@@ -49,11 +47,11 @@ export default async function HomePage() {
</ul>
<form action={signOut}>
<button type="submit">Log out</button>
<button type="submit">Sign out</button>
</form>
</>
);
}
```
This code is similar to the `signin` page, but it uses the `getLoggedInUser` function to get the user's information. If the user is not logged in, the page will redirect to the sign-in page. Again, we use Next.js form actions to execute Appwrite code on the server. This time, the `signOut` function deletes the session cookie and redirect the user to the sign-in page.
This code is similar to the `signup` page, but it uses the `getLoggedInUser` function to get the user's information. If the user is not logged in, the page will redirect to the sign-in page. Again, we use Next.js form actions to execute Appwrite code on the server. This time, the `signOut` function deletes the session cookie and redirect the user to the sign-in page.

View File

@@ -1,62 +1,80 @@
---
layout: tutorial
title: OAuth2 authentication with SSR
title: OAuth authentication with SSR
description: Add authentication to a Next.js 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.
# Enable OAuth provider {% #enable-oauth-provider %}
Add a new server action. Navigate to `src/lib/server` and create a new file `oauth2.js`:
To enable the GitHub OAuth provider, navigate to your Appwrite Console > Auth > Settings > OAuth2 Providers > GitHub
To support the OAuth flow, we first redirect the user to the OAuth provider, and then handle the callback from the OAuth provider.
# OAuth server action {% #oauth-server-action %}
Add a new server action. Navigate to `src/lib/server` and create a new file `oauth.js`:
```js
// src/lib/server/oauth2.js
// src/lib/server/oauth.js
"use server";
import { SESSION_COOKIE, createAdminClient } from "@/lib/server/appwrite";
import { createAdminClient } from "@/lib/server/appwrite";
import { redirect } from "next/navigation";
import { headers } from "next/headers";
import { OAuthProvider } from "node-appwrite";
async function signInWithGithub(formData) {
"use server";
const { account } = createAdminClient();
export async function signUpWithGithub() {
const { account } = await createAdminClient();
const origin = headers().get("origin");
const redirectURL = await account.createOAuth2Token(
OAuthProvider.GitHub,
`${origin}/oauth2`,
`${origin}/oauth2`,
const redirectUrl = await account.createOAuth2Token(
OAuthProvider.Github,
`${origin}/oauth`,
`${origin}/signup`,
);
return redirect(redirectURL);
return redirect(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.
The `createOAuth2Token` method redirects the user to the OAuth provider, and then the OAuth provider redirects the user back to the `/OAuth` route with the `userId` and `secret` URL query parameters.
To redirect, add a button to our sign in page that redirects the user to the OAuth2 provider.
# OAuth form {% #oauth-form %}
To redirect, add a button to our sign up page that redirects the user to the OAuth provider.
```jsx
// src/app/signin/page.jsx
// src/app/signup/page.jsx
// ... existing imports
import { signInWithGithub } from "@/lib/server/oauth2";
import { signUpWithGithub } from "@/lib/server/oauth";
export default async function SignUpPage() {
const user = await getLoggedInUser();
if (user) redirect("/account");
return (
<>
{/* ... existing form */}
<form action={signUpWithGithub}>
<button type="submit">Sign up with GitHub</button>
</form>
</>
);
}
// ... existing sign in form
<form action={signInWithGithub} method="post">
<input type="hidden" name="provider" value="github" />
<button type="submit">Sign in with GitHub</button>
</form>
```
Handle the callback and create a session for the user. Create a new Next.js server route at `src/app/oauth2/route.js`:
# OAuth callback {% #oauth-callback %}
Handle the callback and create a session for the user. Create a new Next.js server route at `src/app/oauth/route.js`:
```js
// src/app/oauth2/route.js
// src/app/oauth/route.js
import { SESSION_COOKIE, createAdminClient } from "@/lib/server/appwrite";
import { createAdminClient } from "@/lib/server/appwrite";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
@@ -64,10 +82,10 @@ export async function GET(request) {
const userId = request.nextUrl.searchParams.get("userId");
const secret = request.nextUrl.searchParams.get("secret");
const { account } = createAdminClient();
const { account } = await createAdminClient();
const session = await account.createSession(userId, secret);
cookies().set(SESSION_COOKIE, session.secret, {
cookies().set("my-custom-session", session.secret, {
path: "/",
httpOnly: true,
sameSite: "strict",
@@ -76,4 +94,4 @@ export async function GET(request) {
return NextResponse.redirect(`${request.nextUrl.origin}/account`);
}
```
```

View File

@@ -4,6 +4,7 @@ title: Server-side authentication with Nuxt
description: Add SSR authentication to your Nuxt app with Appwrite
step: 1
difficulty: beginner
draft: true
readtime: 20
framework: Nuxt SSR
category: Auth
@@ -14,7 +15,10 @@ This tutorials shows how Appwrite can help you add authentication to your Nuxt a
# 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.
Before following this tutorial, have the following prepared:
- A recent version of [Node.js](https://nodejs.org/en/download/) installed on your system.
- A basic knowledge of Vue and Nuxt.
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

@@ -22,7 +22,7 @@ the prompt will be something similar to this.
● Yes
```
After the prompt is finished, you can head over to the newly create project.
After the prompt is finished, you can head over to the newly created project.
```sh
cd my-nuxt-project

View File

@@ -4,7 +4,7 @@ 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.
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**.
@@ -26,7 +26,7 @@ export const SESSION_COOKIE = "my-custom-session";
export function createAdminClient() {
const client = new Client()
.setEndpoint(process.env.APPWRITE_ENDPOINT!)
.setProject(process.env.APPWRITE_PROJECT_ID!)
.setProject(process.env.APPWRITE_PROJECT!)
.setKey(process.env.APPWRITE_KEY!);
return {
@@ -64,42 +64,45 @@ export default defineNuxtConfig({
appwriteKey: process.env.APPWRITE_KEY,
public: {
appwriteEndpoint: process.env.PUBLIC_APPWRITE_ENDPOINT,
appwriteProjectId: process.env.PUBLIC_APPWRITE_PROJECT_ID,
appwriteProjectId: process.env.PUBLIC_APPWRITE_PROJECT,
},
},
});
```
Now you can use `.env` files to set the environment variables for your project. Retrieve the values for these variables from the Appwrite console.
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` is the endpoint of your Appwrite project, and the `PUBLIC_APPWRITE_PROJECT` is the ID of the project you want to use.
You can get the values for these variables from the Appwrite console.
The `PUBLIC_APPWRITE_ENDPOINT` is the endpoint of your Appwrite project, and the `PUBLIC_APPWRITE_PROJECT` is the ID of the project you want to use.
You can get the values for these variables from the Appwrite console.
{% only_dark %}
![Create project screen](/images/docs/quick-starts/dark/create-project.png)
{% /only_dark %}
{% only_light %}
![Create project screen](/images/docs/quick-starts/create-project.png)
{% /only_light %}
The `APPWRITE_KEY` is an Appwrite API key with the necessary permissions to read and write accounts and sessions.
The `APPWRITE_KEY` is an Appwrite API key with the necessary permissions to create new sessions.
For this tutorial you'll need an API key with the following scopes:
| Category {% width=120 %} | Required scopes | Purpose |
|-----------|---------------------|---------|
| Sessions | `sessions.write` | Allows API key to create, update, and delete sessions. |
| Category {% width=120 %} | Required scopes | Purpose |
| ------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------ |
| Sessions | `sessions.write` | Allows API key to create, update, and delete sessions. |
{% only_dark %}
![Server integrations](/images/docs/quick-starts/dark/integrate-server.png)
{% /only_dark %}
{% only_light %}
![Server integrations](/images/docs/quick-starts/integrate-server.png)
{% /only_light %}
For example, your `.env` might look something similar to this.
```text
```env
APPWRITE_KEY=<YOUR_API_KEY>
PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
PUBLIC_APPWRITE_PROJECT=<YOUR_PROJECT_ID>
```
```

View File

@@ -41,8 +41,8 @@ export default defineEventHandler(async (event) => {
await sendRedirect(event, "/account");
}
await sendRedirect(event, "/signin");
await sendRedirect(event, "/signup");
});
```
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.
When a user visits the home page, they will be redirected to the sign up page if they are not logged in, or to the account page if they are logged in.

View File

@@ -1,16 +1,16 @@
---
layout: tutorial
title: Create sign in page
title: Create sign up 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.
We can now implement our sign up page. Create a `signin.vue` file in the `pages` directory.
```vue
<!-- pages/signin.vue -->
<!-- pages/signup.vue -->
<template>
<form method="post" action="/api/signin">
<form method="post" action="/api/signup">
<input id="email" name="email" placeholder="Email" type="email" />
<input
id="password"
@@ -18,7 +18,7 @@ We can now implement our sign in page. Create a `signin.vue` file in the `pages`
placeholder="Password"
type="password"
/>
<button type="submit">Sign in</button>
<button type="submit">Sign up</button>
</form>
</template>
```
@@ -26,7 +26,7 @@ We can now implement our sign in page. Create a `signin.vue` file in the `pages`
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
// server/api/signup.post.js
import { SESSION_COOKIE, createAdminClient } from "~/server/lib/appwrite";
export default defineEventHandler(async (event) => {

View File

@@ -5,7 +5,7 @@ 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:
Now the end-user is able to sign up, 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 -->
@@ -44,6 +44,6 @@ export default defineEventHandler(async (event) => {
await account.deleteSession("current");
deleteCookie(event, SESSION_COOKIE);
await sendRedirect(event, "/signin");
await sendRedirect(event, "/signup");
});
```

View File

@@ -1,29 +1,29 @@
---
layout: tutorial
title: OAuth2 authentication with SSR
title: OAuth 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 support the OAuth flow, we first redirect the user to the OAuth provider, and then handle the callback from the OAuth provider.
To redirect, add a button to our sign in page that redirects the user to the OAuth2 provider.
To redirect, add a button to our sign up page that redirects the user to the OAuth provider.
```vue
<!-- pages/signin.vue -->
<!-- pages/signup.vue -->
<!-- ... existing sign in form -->
<!-- ... existing sign up form -->
<form action="/api/oauth2" method="post">
<form action="/api/oauth" method="post">
<input type="hidden" name="provider" value="github" />
<button type="submit">Sign in with GitHub</button>
<button type="submit">Sign up with GitHub</button>
</form>
```
Add a new server route to handle the redirect.
```js
// server/routes/api/oauth2.post.js
// server/routes/api/oauth.post.js
import { createAdminClient } from "~/server/lib/appwrite";
export default defineEventHandler(async (event) => {
@@ -31,29 +31,29 @@ export default defineEventHandler(async (event) => {
const { account } = createAdminClient();
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
"github", // OAuth provider
`${config.public.appwriteEndpoint}/api/oauth`, // Success URL: redirect back to the /oauth route
`${config.public.appwriteEndpoint}/signup` // Failure URL: redirect to the sign up page
);
// Redirect the user to the OAuth2 provider authorization page
// Redirect the user to the OAuth 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.
The `createOAuth2Token` method redirects the user to the OAuth provider, and then the OAuth provider redirects the user back to the `/oauth` 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`.
Handle the callback and create a session for the user. Create a new server route at `server/routes/api/oauth.get.js`.
```js
// server/routes/api/oauth2.post.js
// server/routes/api/oauth.post.js
import { SESSION_COOKIE, createAdminClient } 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");
return sendRedirect(event, "/signup");
}
// Create the Appwrite client

View File

@@ -36,7 +36,7 @@ create-svelte version 3.2.0
└ Your project is ready!
```
After the prompt is finished, you can head over to the newly create project.
After the prompt is finished, you can head over to the newly created project.
```sh
cd my-svelte-project

View File

@@ -38,7 +38,7 @@ export const appwrite = {
For example, your `.env` might look something similar to this.
```text
```env
PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
PUBLIC_APPWRITE_PROJECT=642sdddf85b440dc7e5bf
```

View File

@@ -14,7 +14,10 @@ If you're a Svelte developer, the examples in this guide show you how Appwrite c
# 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 Svelte and Appwrite.
Before following this tutorial, have the following prepared:
- A recent version of [Node.js](https://nodejs.org/en/download/) installed on your system.
- A basic knowledge of Svelte and SvelteKit.
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.
Clone the [Demos for svelte](https://github.com/appwrite/demos-for-svelte) examples and follow along with the source code.

View File

@@ -30,13 +30,13 @@ create-svelte version 3.2.0
◇ Add type checking with TypeScript?
│ Yes, using JavaScript with JSDoc comments
◇ Select additional options (use arrow keys/space bar)
Add ESLint for code linting
◇ Select additional options
None
└ Your project is ready!
```
After the prompt is finished, you can head over to the newly create project.
After the prompt is finished, you can head over to the newly created project.
```sh
cd my-svelte-project

View File

@@ -21,45 +21,57 @@ Doing so could create security vulnerabilities.
// 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';
import { PUBLIC_APPWRITE_ENDPOINT, PUBLIC_APPWRITE_PROJECT } from '$env/static/public';
export const SESSION_COOKIE = 'my-custom-session';
export function createAdminClient() {
const client = new Client()
.setEndpoint(PUBLIC_APPWRITE_ENDPOINT)
.setProject(PUBLIC_APPWRITE_PROJECT_ID)
.setKey(APPWRITE_KEY); // Set the Appwrite API key!
const client = new Client()
.setEndpoint(PUBLIC_APPWRITE_ENDPOINT)
.setProject(PUBLIC_APPWRITE_PROJECT)
.setKey(APPWRITE_KEY); // Set the Appwrite API key!
// Return the services we want to use.
return {
get account() {
return new Account(client);
}
};
// Return the services we want to use.
return {
get account() {
return new Account(client);
}
};
}
export function createSessionClient(event) {
const client = new Client()
.setEndpoint(PUBLIC_APPWRITE_ENDPOINT)
.setProject(PUBLIC_APPWRITE_PROJECT_ID);
const client = new Client()
.setEndpoint(PUBLIC_APPWRITE_ENDPOINT)
.setProject(PUBLIC_APPWRITE_PROJECT);
// Extract our custom domain's session cookie from the request
const session = event.cookies.get(SESSION_COOKIE);
if (session) client.setSession(session);
// Extract our custom domain's session cookie from the request
const session = event.cookies.get(SESSION_COOKIE);
if (!session) {
throw new Error("No user session");
}
// Return the services we want to use.
return {
get account() {
return new Account(client);
}
};
client.setSession(session);
// Return the services we want to use.
return {
get account() {
return new Account(client);
}
};
}
```
# Environment variables {% #environment-variables %}
`APPWRITE_KEY`, `PUBLIC_APPWRITE_ENDPOINT` and `PUBLIC_APPWRITE_PROJECT_ID` 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.
For example, your `.env` might look something similar to this.
```text
APPWRITE_KEY=<YOUR_API_KEY>
PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
PUBLIC_APPWRITE_PROJECT=<YOUR_PROJECT_ID>
```
The `PUBLIC_APPWRITE_ENDPOINT` is the endpoint of your Appwrite project, and the `PUBLIC_APPWRITE_PROJECT` is the ID of the project you want to use.
You can get the values for these variables from the Appwrite console.
@@ -83,12 +95,4 @@ For this tutorial you'll need an API key with the following scopes:
{% /only_dark %}
{% only_light %}
![Server integrations](/images/docs/quick-starts/integrate-server.png)
{% /only_light %}
For example, your `.env` might look something similar to this.
```text
APPWRITE_KEY=<YOUR_API_KEY>
PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
PUBLIC_APPWRITE_PROJECT=<YOUR_PROJECT_ID>
```
{% /only_light %}

View File

@@ -7,63 +7,67 @@ 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`:
Create a new file in the `src` directory called `hooks.server.js`:
```js
// src/hooks.server.js
import { createSessionClient } from '$lib/server/appwrite';
export async function handle({ event, resolve }) {
// Use our helper function to create the Appwrite client.
const { account } = createSessionClient(event);
// Let's store the current logged in user in locals,
// for easy access in our other routes.
try {
event.locals.user = await account.get();
} catch {}
// Continue with the request.
return resolve(event);
try {
// Use our helper function to create the Appwrite client.
const { account } = createSessionClient(event);
// Store the current logged in user in locals,
// for easy access in our other routes.
event.locals.user = await account.get();
} catch {}
// 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';
// env.d.ts
import type { Models } from "node-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> | undefined;
}
// interface PageData {}
// interface Platform {}
}
namespace App {
// interface Error {}
interface Locals {
user: Models.User<Models.Preferences> | undefined;
}
// 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`:
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
// src/routes/+page.server.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');
// Access our user from locals.
if (!locals.user) {
// If no user is logged in, redirect to the sign up page.
throw redirect(301, '/signup');
}
// 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.
When a user visits the home page, they will be redirected to the sign up page if they are not logged in, or to the account page if they are logged in.

View File

@@ -1,50 +1,57 @@
---
layout: tutorial
title: Create sign in page
title: Create sign up 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:
We can now implement our sign up page. Create a `+page.svelte` file in the `src/routes/signup` directory:
```svelte
<!-- src/routes/signin/+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>
<!-- src/routes/signup/+page.svelte -->
<form action="?/signup" method="post">
<input id="email" name="email" placeholder="Email" type="email" />
<input id="password" name="password" placeholder="Password" type="password" />
<input id="name" name="name" placeholder="Name" type="text" />
<button type="submit">Sign up</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, createAdminClient } from '$lib/server/appwrite.js';
import { redirect } from '@sveltejs/kit';
// src/routes/signup/+page.server.js
import { SESSION_COOKIE, createAdminClient } from "$lib/server/appwrite.js";
import { redirect } from "@sveltejs/kit";
import { ID, OAuthProvider } from "node-appwrite";
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;
signup: async ({ request, cookies }) => {
// Extract the form data.
const form = await request.formData();
const email = form.get("email");
const password = form.get("password");
const name = form.get("name");
// Create the Appwrite client.
const { account } = createAdminClient();
// Create the Appwrite client.
const { account } = createAdminClient();
// Create the session using the client
const session = await account.createEmailPasswordSession(email, password);
// Create the session using the client
await account.create(ID.unique(), email, password, name);
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: '/'
});
// Set the session cookie with the secret
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');
}
// Redirect to the account page.
throw redirect(301, "/account");
},
};
```

View File

@@ -5,36 +5,37 @@ 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:
Now the end-user is able to sign up, 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, createSessionClient } from '$lib/server/appwrite.js';
import { redirect } from '@sveltejs/kit';
import { SESSION_COOKIE, createSessionClient } 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');
// Logged out users can't access this page.
if (!locals.user) throw redirect(301, "/signup");
// Pass the stored user local to the page.
return {
user: locals.user
};
// 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 } = createSessionClient(event);
default: async (event) => {
// Create the Appwrite client.
const { account } = createSessionClient(event);
// Delete the session on Appwrite, and delete the session cookie.
await account.deleteSession('current');
event.cookies.delete(SESSION_COOKIE);
// Delete the session on Appwrite, and delete the session cookie.
await account.deleteSession("current");
event.cookies.delete(SESSION_COOKIE, { path: "/" });
// Redirect to the sign in page.
throw redirect(301, '/signin');
}
// Redirect to the sign up page.
throw redirect(301, "/signup");
},
};
```
@@ -42,24 +43,24 @@ Create a new file in the `src/routes/account` directory called `+page.svelte` an
```svelte
<script>
export let data;
export let data;
const { user } = 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>
<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 method="post">
<button type="submit">Log out</button>
</form>
```
```

View File

@@ -1,83 +1,85 @@
---
layout: tutorial
title: OAuth2 authentication with SSR
title: OAuth 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 support the OAuth flow, we first redirect the user to the OAuth provider, and then handle the callback from the OAuth provider.
To redirect, add a button to our sign in page that redirects the user to the OAuth2 provider.
To redirect, add a button to our sign up page that redirects the user to the OAuth provider.
```svelte
<!-- src/routes/signin/+page.svelte -->
<!-- src/routes/signup/+page.svelte -->
<!-- ... existing sign in form -->
<!-- ... existing sign up form -->
<form action="/oauth2" method="post">
<input type="hidden" name="provider" value="github" />
<button type="submit">Sign in with GitHub</button>
<form action="?/oauth" method="post">
<button type="submit">Sign up with GitHub</button>
</form>
```
Add a new server route to handle the redirect.
```js
// src/routes/signin/+page.server.js
// src/routes/signup/+page.server.js
// ... existing imports
import { SESSION_COOKIE, createAdminClient } from '$lib/server/appwrite';
import { redirect } from '@sveltejs/kit';
import { OAuthProvider } from 'node-appwrite';
import { SESSION_COOKIE, createAdminClient, createSessionClient } from "$lib/server/appwrite.js";
import { redirect } from "@sveltejs/kit";
import { ID, OAuthProvider } from "node-appwrite";
export const actions = {
// ... existing actions
// ... existing email sign up action
oauth: async (event) => {
const { account } = createAdminClient();
oauth2: async (event) => {
const { account } = createAdminClient();
const redirectUrl = await account.createOAuth2Token(
OAuthProvider.Github,
`${event.url.origin}/oauth`,
`${event.url.origin}/signup`
);
const redirectURL = account.createOAuth2Token(
OAuthProvider.GitHub,
`${event.url.origin}/oauth2`,
`${event.url.origin}/signin`
);
throw redirect(url);
}
throw redirect(301, 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.
The `createOAuth2Token` method redirects the user to the OAuth provider, and then the OAuth provider redirects the user back to the `/oauth` 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`:
Handle the callback and create a session for the user. Create a new server route at `src/routes/oauth/+server.js`:
```js
// src/routes/oauth2/+server.js
// src/routes/oauth/+server.js
import { SESSION_COOKIE, createAdminClient } from '$lib/server/appwrite';
import { SESSION_COOKIE, createAdminClient } 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;
// We should have a `userId` and `secret` query parameters in the URL
const userId = event.url.searchParams.get("userId");
const secret = event.url.searchParams.get("secret");
// Exchange the token `userId` and `secret` for a session
const { account } = createAdminClient();
const session = await account.createSession(userId, secret);
if (!userId || !secret) {
return new Response("Missing `userId` or `secret` query parameters", {
status: 400,
});
}
// 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: '/'
})
});
// Exchange the token `userId` and `secret` for a session
const { account } = createAdminClient();
const session = await account.createSession(userId, secret);
return new Response(null, { status: 302, headers });
// 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 });
}
```