mirror of
https://github.com/LukeHagar/website.git
synced 2025-12-09 12:57:48 +00:00
feat: nextjs ssr auth tutorial
This commit is contained in:
@@ -22,7 +22,8 @@
|
||||
flutter: 'icon-flutter',
|
||||
nuxt: 'icon-nuxt',
|
||||
stripe: 'icon-stripe',
|
||||
refine: 'aw-icon-refine'
|
||||
refine: 'aw-icon-refine',
|
||||
'next.js': 'icon-nextjs'
|
||||
};
|
||||
|
||||
const getIcon = (tutorial: MappedTutorial) => {
|
||||
|
||||
10
src/routes/docs/tutorials/nextjs-ssr-auth/+layout.svelte
Normal file
10
src/routes/docs/tutorials/nextjs-ssr-auth/+layout.svelte
Normal 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 />
|
||||
11
src/routes/docs/tutorials/nextjs-ssr-auth/+layout.ts
Normal file
11
src/routes/docs/tutorials/nextjs-ssr-auth/+layout.ts
Normal 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
|
||||
};
|
||||
};
|
||||
6
src/routes/docs/tutorials/nextjs-ssr-auth/+page.ts
Normal file
6
src/routes/docs/tutorials/nextjs-ssr-auth/+page.ts
Normal 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');
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Server-side authentication with Next.js
|
||||
description: Add SSR authentication to your Next.js app with Appwrite
|
||||
step: 1
|
||||
difficulty: beginner
|
||||
readtime: 20
|
||||
framework: Next.js
|
||||
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 Next.js developer, the examples in this guide show you how Appwrite can help you add authentication to Next.js apps faster.
|
||||
|
||||
# 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.
|
||||
|
||||
If you're inspired and wish to follow along, make sure you've followed [Start with Next.js](https://appwrite.io/docs/quick-starts/sveltekit) first.
|
||||
You can also clone the [demos-for-react](https://github.com/appwrite/demos-for-react/tree/main/nextjs) examples and follow along by looking at the code.
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Create project
|
||||
description: Add authentication to a Next.js project using Appwrite.
|
||||
step: 2
|
||||
---
|
||||
|
||||
|
||||
Create a react project using [Next.js](https://kit.svelte.dev/docs/creating-a-project).
|
||||
|
||||
```sh
|
||||
npx create-next-app@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
|
||||
What is your project named? my-app
|
||||
Would you like to use TypeScript? No
|
||||
Would you like to use ESLint? No
|
||||
Would you like to use Tailwind CSS? No
|
||||
Would you like to use `src/` directory? Yes
|
||||
Would you like to use App Router? (recommended) Yes
|
||||
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.
|
||||
|
||||
```sh
|
||||
cd my-app
|
||||
npm install
|
||||
```
|
||||
|
||||
## Adding `node-appwrite` to Your Next.js App
|
||||
|
||||
Appwrite provides a Node SDK that can be used in your Next.js 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.
|
||||
|
||||
```sh
|
||||
npm install node-appwrite
|
||||
```
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Initialize SDK
|
||||
description: Add authentication to a Next.js 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 { parseCookie } from "next/dist/compiled/@edge-runtime/cookies";
|
||||
|
||||
export const SESSION_COOKIE = "a_session";
|
||||
|
||||
export function createAppwriteClient(headers) {
|
||||
const client = new Client()
|
||||
.setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT!)
|
||||
.setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID!);
|
||||
|
||||
// Set the API key for the client, bypassing rate limiting and enabling
|
||||
client.setKey(process.env.APPWRITE_KEY!);
|
||||
|
||||
// Extract the session from cookies and use it for the client
|
||||
const cookies = parseCookie(headers.get("cookie") ?? "");
|
||||
const session = cookies.get(SESSION_COOKIE);
|
||||
if (session) client.setSession(session);
|
||||
|
||||
return {
|
||||
get account() {
|
||||
return new Account(client);
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
`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).
|
||||
|
||||
You can get the values for these variables from the Appwrite console. The `PUBLIC_APPWRITE_ENDPOINT` is the endpoint of your Appwrite server, and the `PUBLIC_APPWRITE_PROJECT` is the ID of the project you want to use. The `APPWRITE_KEY` is an Appwrite API key with the necessary permissions to read and write to the database.
|
||||
|
||||
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_API_KEY>
|
||||
PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
|
||||
PUBLIC_APPWRITE_PROJECT=<YOUR_PROJECT_ID>
|
||||
```
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Get the logged in user
|
||||
description: Add authentication to a Next.js project using Appwrite.
|
||||
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`:
|
||||
```js
|
||||
// ... your createAppwriteClient function
|
||||
|
||||
export async function getLoggedInUser(account) {
|
||||
try {
|
||||
return await account.get();
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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 { createAppwriteClient, getLoggedInUser } from "@/lib/server/appwrite";
|
||||
|
||||
import { headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default async function Home() {
|
||||
const { account } = createAppwriteClient(headers());
|
||||
|
||||
const user = await getLoggedInUser(account);
|
||||
if (!user) redirect("/signin");
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Create sign in 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:
|
||||
|
||||
```jsx
|
||||
// src/app/sigin/+page.jsx
|
||||
|
||||
import {
|
||||
createAppwriteClient,
|
||||
getLoggedInUser,
|
||||
} from "@/lib/server/appwrite";
|
||||
|
||||
|
||||
export default async function SignInPage() {
|
||||
const { account } = createAppwriteClient(headers());
|
||||
|
||||
const user = await getLoggedInUser(account);
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```jsx
|
||||
// src/app/sigin/+page.jsx
|
||||
|
||||
// previous imports ...
|
||||
|
||||
import { SESSION_COOKIE } from "@/lib/server/appwrite";
|
||||
import { cookies, headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
// the SignInPage component ...
|
||||
|
||||
async function signInWithEmail(formData) {
|
||||
"use server";
|
||||
|
||||
const email = formData.get("email");
|
||||
const password = formData.get("password");
|
||||
|
||||
const { account } = createAppwriteClient(headers());
|
||||
|
||||
const session = await account.createEmailPasswordSession(email, password);
|
||||
|
||||
cookies().set(SESSION_COOKIE, session.secret, {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
sameSite: "strict",
|
||||
secure: true,
|
||||
});
|
||||
|
||||
redirect("/account");
|
||||
}
|
||||
```
|
||||
|
||||
The `signInWithEmail` function is an async function that takes the form data as an argument. It uses the `createAppwriteClient` function to create an 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.
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Create account page
|
||||
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:
|
||||
|
||||
```jsx
|
||||
// src/app/account/page.jsx
|
||||
import {
|
||||
SESSION_COOKIE,
|
||||
createAppwriteClient,
|
||||
getLoggedInUser,
|
||||
} from "@/lib/server/appwrite";
|
||||
import { redirect } from "next/navigation";
|
||||
import { cookies, headers } from "next/headers";
|
||||
|
||||
async function signOut() {
|
||||
"use server";
|
||||
|
||||
const { account } = createAppwriteClient(headers());
|
||||
|
||||
cookies().delete(SESSION_COOKIE);
|
||||
await account.deleteSession("current");
|
||||
|
||||
redirect("/signin");
|
||||
}
|
||||
|
||||
export default async function HomePage() {
|
||||
const { account } = createAppwriteClient(headers());
|
||||
|
||||
const user = await getLoggedInUser(account);
|
||||
if (!user) redirect("/signin");
|
||||
|
||||
return (
|
||||
<>
|
||||
<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={signOut} method="post">
|
||||
<button type="submit">Log 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.
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: Adding OAuth2 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.
|
||||
|
||||
Add a new server action. Navigate to `src/lib/server` and create a new file `oauth2.js`:
|
||||
|
||||
```js
|
||||
// src/lib/server/oauth2.js
|
||||
|
||||
import { SESSION_COOKIE, createAppwriteClient } from "@/lib/server/appwrite";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
async function signInWithGithub(formData) {
|
||||
"use server";
|
||||
|
||||
const provider = formData.get('provider') ?? 'github';
|
||||
|
||||
const { account } = createAppwriteClient();
|
||||
const redirectURL = await account.createOAuth2Token(
|
||||
provider,
|
||||
'<YOUR_WEBSITE_DOMAIN>/oauth2',
|
||||
'<YOUR_WEBSITE_DOMAIN>/signin'
|
||||
);
|
||||
|
||||
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.
|
||||
|
||||
To redirect, add a button to our sign in page that redirects the user to the OAuth2 provider.
|
||||
|
||||
```jsx
|
||||
// src/app/signin/page.jsx
|
||||
|
||||
// ... existing imports
|
||||
import { signInWithGithub } from "@/lib/server/oauth2";
|
||||
|
||||
// ... 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`:
|
||||
|
||||
```js
|
||||
// src/app/oauth2/route.js
|
||||
|
||||
import { SESSION_COOKIE, createAppwriteClient } from "@/lib/server/appwrite";
|
||||
import { cookies } from "next/headers";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function GET(request) {
|
||||
const userId = request.nextUrl.searchParams.get("userId");
|
||||
const secret = request.nextUrl.searchParams.get("secret");
|
||||
|
||||
const { account } = createAppwriteClient(request.headers);
|
||||
const session = await account.createSession(userId, secret);
|
||||
|
||||
cookies().set(SESSION_COOKIE, session.secret, {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
sameSite: "strict",
|
||||
secure: true,
|
||||
});
|
||||
|
||||
return NextResponse.redirect(`${request.nextUrl.origin}/account`);
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
layout: tutorial
|
||||
title: All set
|
||||
description: Add authentication to a Next.js project using Appwrite.
|
||||
step: 8
|
||||
---
|
||||
If you want to see the complete source code with styling, see the [demos-for-react](https://github.com/appwrite/demos-for-react/tree/main/nextjs/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).
|
||||
Reference in New Issue
Block a user