新しい技術を試すにあたって、面倒ではあるが、認証までは一通り使いこなせる必要があると感じている。ただ、認証は結構めんどくさいし、すぐ忘れてしまいがちなので、メモを残しておく。
GraphQLサーバに、認証機能を付与するにあたって、Contextを定義する。このMyContextはGraphGLの各呼び出しに対して、引数のほかに共通的に呼び出される。ここでは認証を想定し、認証されていればMyContextはユーザ情報を含み、認証されていなければnullであるとする。
interface MyContext { user: User | null; }
Resolverにも先ほどMyContextを設定する。/meのエンドポイントの定義の3つめの引数がMyContextとなるので、user情報を含んでいるかどうかをチェックしてエラーを投げている。エラーメッセージを適当に投げるとInternal Server Errorと扱われてしまうので、正確に認証エラーを返すには、GraphQLErrorを使ってカスタムエラーを投げることがある。
const resolvers: Resolvers<MyContext> = { Query: { me: (_parent, _args, {user}) => { if (user == null) throw new GraphQLError('Unauthorized', { extensions: { code: 'UNAUTHORIZED' } }); return user },
もう2か所。ApolloServer生成時にもMyContextを設定しておき、startStandaloneServerのoptionsでは、設定したMyContext生成について定義する必要がある。今回はヘッダーで受け取ったAuthorizationの値からUser型のデータまたはnullを受け取り返すといった処理になっている。各ユーザ情報とAuthorizationの値とをマッチさせる返答することになる。
const server = new ApolloServer<MyContext>({ typeDefs, resolvers, }); const { url } = await startStandaloneServer(server, { listen: { port: 4000 }, context: async ({req}) => { const token = req.headers.authorization || ''; const user = getUser(token); return { user } satisfies MyContext; }, });