From 20ab23585641d7b5a26e956c3171f55189476e31 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Sun, 13 Nov 2022 11:18:57 -0800 Subject: [PATCH] docs: add auth tips --- .../1.get-started/3.tips/4.authorization.md | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 docs/content/1.get-started/3.tips/4.authorization.md diff --git a/docs/content/1.get-started/3.tips/4.authorization.md b/docs/content/1.get-started/3.tips/4.authorization.md new file mode 100644 index 0000000..1e884ee --- /dev/null +++ b/docs/content/1.get-started/3.tips/4.authorization.md @@ -0,0 +1,104 @@ +--- +title: Authorization +--- + +# Authorization + +The `createContext` function is called for each incoming request so here you can add contextual information about the calling user from the request object. + +## Create context from request headers + +```ts [server/trpc/context.ts] +import type { H3Event } from 'h3' +import { inferAsyncReturnType } from '@trpc/server' +import { decodeAndVerifyJwtToken } from './somewhere/in/your/app/utils' + +export async function createContext({ + req, + res, +}: H3Event) { + // Create your context based on the request object + // Will be available as `ctx` in all your resolvers + + // This is just an example of something you might want to do in your ctx fn + async function getUserFromHeader() { + if (req.headers.authorization) { + const user = await decodeAndVerifyJwtToken( + req.headers.authorization.split(' ')[1], + ) + return user + } + return null + } + const user = await getUserFromHeader() + + return { + user, + } +} +type Context = inferAsyncReturnType +``` + +## Option 1: Authorize using resolver + +```ts +import { TRPCError, initTRPC } from '@trpc/server' +import type { Context } from '../context' + +export const t = initTRPC.context().create() + +const appRouter = t.router({ + // open for anyone + hello: t.procedure + .input(z.string().nullish()) + .query(({ input, ctx }) => `hello ${input ?? ctx.user?.name ?? 'world'}`), + // checked in resolver + secret: t.procedure.query(({ ctx }) => { + if (!ctx.user) { + throw new TRPCError({ code: 'UNAUTHORIZED' }) + } + return { + secret: 'sauce', + } + }), +}) +``` + +## Option 2: Authorize using middleware + +```ts +import { TRPCError, initTRPC } from '@trpc/server' + +export const t = initTRPC.context().create() + +const isAuthed = t.middleware(({ next, ctx }) => { + if (!ctx.user?.isAdmin) { + throw new TRPCError({ code: 'UNAUTHORIZED' }) + } + return next({ + ctx: { + user: ctx.user, + }, + }) +}) + +// you can reuse this for any procedure +export const protectedProcedure = t.procedure.use(isAuthed) + +t.router({ + // this is accessible for everyone + hello: t.procedure + .input(z.string().nullish()) + .query(({ input, ctx }) => `hello ${input ?? ctx.user?.name ?? 'world'}`), + admin: t.router({ + // this is accessible only to admins + secret: protectedProcedure.query(({ ctx }) => { + return { + secret: 'sauce', + } + }), + }), +}) +``` + +This page is entirely based on [authorization docs](https://trpc.io/docs/v10/authorization) of tRPC with a minimal change made to work with Nuxt.