Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update auth documentation for app router #1894

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
88 changes: 51 additions & 37 deletions www/src/pages/en/usage/next-auth.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ When you want an authentication system in your Next.js application, NextAuth.js

## Context Provider

<Callout type="warning">
This section is only applicable if you decide to use the `pages` router with
Next. To understand how to use sessions with the (reccomended) new `app`
router, please read the section below about retreiving a session on the
server-side.
</Callout>

In your app's entrypoint, you'll see that your application is wrapped in a [SessionProvider](https://next-auth.js.org/getting-started/client#sessionprovider):

```tsx:pages/_app.tsx
Expand Down Expand Up @@ -39,27 +46,33 @@ const User = () => {

## Retrieving session server-side
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this should be split to 2 sections (pages and app)

the old pages setup is still relevant afaik?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure i understand? The pages setup is still very relevant but i thought a warning might be a good call. In theory, the server-side retrieval section serves as the "other section" that explains how auth works in app router situations.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you removed the gSSP part of the docs explaining how to retrieve session on the server in pages router.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand what you mean now.


Sometimes you might want to request the session on the server. To do so, prefetch the session using the `getServerAuthSession` helper function that `create-t3-app` provides, and pass it down to the client using `getServerSideProps`:
If you have configured your application to use Next's `/app` router, the recomended behavior is to request the session on the server rather than the client. This is how `create-t3-app` is configured by default.

```tsx:pages/users/[id].tsx
import { getServerAuthSession } from "../server/auth";
import { type GetServerSideProps } from "next";
In your app's entrypoint you will notice that the auth session has been prefetched using the `getServerAuthSession` helper function that `create-t3-app` provides.

export const getServerSideProps: GetServerSideProps = async (ctx) => {
const session = await getServerAuthSession(ctx);
return {
props: { session },
};
};
```tsx:app/page.tsx
import {getServerAuthSession} from "~/server/auth"

const User = () => {
const { data: session } = useSession();
// NOTE: `session` wont have a loading state since it's already prefetched on the server
export default async function Home(){
const session = await getServerAuthSession();
}
```

While `session` is initially equal to null, if you were to login using Discord and log `session` to the console you would notice that it takes the following form:

...
```json
{
user: {
name: string,
email: string,
image: string,
id: string,
}
}
```

To add additional providers, callbacks or configure authentication in any manner you can edit the `server/auth.ts` file as necessary. Additional information about authentication configuration options are available [here](https://next-auth.js.org/configuration/options).

## Inclusion of `user.id` on the Session

Create T3 App is configured to utilise the [session callback](https://next-auth.js.org/configuration/callbacks#session-callback) in the NextAuth.js config to include the user's ID within the `session` object.
Expand Down Expand Up @@ -93,39 +106,36 @@ The same pattern can be used to add any other data to the `session` object, such

## Usage with tRPC

When using NextAuth.js with tRPC, you can create reusable, protected procedures using [middleware](https://trpc.io/docs/v10/middlewares). This allows you to create procedures that can only be accessed by authenticated users. `create-t3-app` sets all of this up for you, allowing you to easily access the session object within authenticated procedures.
When using NextAuth.js with tRPC, you can create reusable, protected procedures using [middleware](https://trpc.io/docs/server/middlewares). This allows you to create procedures that can only be accessed by authenticated users. `create-t3-app` sets all of this up for you, allowing you to easily access the session object within authenticated procedures.

This is done in a two step process:

1. Grab the session from the request headers using the [`getServerSession`](https://next-auth.js.org/configuration/nextjs#getServerSession) function. The advantage of using `getServerSession` instead of the regular `getSession` is that it's a server-side only function and doesn't trigger unnecessary fetch calls. `create-t3-app` creates a helper function that abstracts this peculiar API away so that you don't need to import both your NextAuth.js options as well as the `getServerSession` function every time you need to access the session.
1. Grab the session from the request headers using the [`getServerSession`](https://next-auth.js.org/configuration/nextjs#getServerSession) function. The advantage of using `getServerSession` instead of the regular `getSession` is that it's a server-side only function and doesn't trigger unnecessary fetch calls. `create-t3-app` creates a helper function that abstracts this peculiar API away so that you don't need to import your NextAuth.js options to every single route where you would like to access the session.

```ts:server/auth.ts
export const getServerAuthSession = (ctx: {
req: GetServerSidePropsContext["req"];
res: GetServerSidePropsContext["res"];
}) => {
return getServerSession(ctx.req, ctx.res, authOptions);
};
export const getServerAuthSession = () => getServerSession(authOptions);
```

Using this helper function, we can grab the session and pass it through to the tRPC context:

```ts:server/api/trpc.ts
import { getServerAuthSession } from "../auth";

export const createContext = async (opts: CreateNextContextOptions) => {
const { req, res } = opts;
const session = await getServerAuthSession({ req, res });
return await createContextInner({
export const createTRPCContext = async (opts: { headers: Headers }) => {
const session = await getServerAuthSession();

return {
db,
session,
});
...opts,
};
};
```

2. Create a tRPC middleware that checks if the user is authenticated. We then use the middleware in a `protectedProcedure`. Any caller to these procedures must be authenticated, or else an error will be thrown which can be appropriately handled by the client.

```ts:server/api/trpc.ts
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
if (!ctx.session || !ctx.session.user) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
Expand All @@ -135,21 +145,25 @@ export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
session: { ...ctx.session, user: ctx.session.user },
},
});
})
});
```

The session object is a light, minimal representation of the user and only contains a few fields. When using the `protectedProcedures`, you have access to the user's id which can be used to fetch more data from the database.

```ts:server/api/routers/user.ts
const userRouter = router({
me: protectedProcedure.query(async ({ ctx }) => {
const user = await prisma.user.findUnique({
where: {
id: ctx.session.user.id,
},
```ts:server/api/routers/post.ts
export const postRouter = createTRPCRouter({

// code above...

getLatest: protectedProcedure.query(({ ctx }) => {
return ctx.db.post.findFirst({
orderBy: { createdAt: "desc" },
where: { createdBy: { id: ctx.session.user.id } },
});
return user;
}),

// code below...

});
```

Expand Down