0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【TypeScript】Pothosで認証ガードを実装する

0
Posted at

概要

PothosはTypeScriptでの、GraphQLスキーマビルダーのライブラリになります。Pothosのドキュメントで紹介されているAuth pluginを使用すれば、関数ごとに認証ガードがかけられます。
今回はこのAuth pluginを使ってみたので、その実装のメモ書きになります。

前提

  • 使用したpothos/plugin-scope-authのバージョンは4.1.6です。
  • GraphQL Serverのライブラリはhono/graphql-serverを使用します。バージョンは0.7.0です。

実装サンプル

まずはリクエストヘッダーの情報をcontextにセットする処理を作成します。今回はjwtトークンを前提として、アクセスしたユーザーのIDを取得することとします。

import { Context } from "hono";
import { sign, verify } from "hono/jwt";

export type EnvBindings = {
  JWT_SECRET: string;
};

export type Variables = {
  loginUserAccountId?: string;
};

export type GraphQLContext = Context<{
  Bindings: EnvBindings;
  Variables: Variables;
}>;

// Authenticationヘッダーのトークンからコンテキストに認証情報をセット
export const setAuthInfoToContext = async (
  context: GraphQLContext,
  authHeader?: string,
) => {
  if (authHeader?.startsWith("Bearer ")) {
    const token = authHeader.slice(7);
    try {
      const payload = (await verify(
        token,
        context.env.JWT_SECRET,
        "HS256",
      )) as UserAccountJwtPayload;
      context.set("loginUserAccountId", payload.userAccountId);
    } catch {
      // 無効なトークンはundefinedのまま
    }
  }
};

次にスキーマのbuilder設定です。今回はAuthScopesにはcontextのid設定を参照して、ユーザーidが検証済みかのフラグを設定します。

import SchemaBuilder from "@pothos/core";
import ScopeAuthPlugin from "@pothos/plugin-scope-auth";

import { GraphQLContext } from "@/type/context";

export const builder = new SchemaBuilder<{
  Context: GraphQLContext;
  AuthScopes: {
    isAuthenticated: boolean;
  };
}>({
  plugins: [ScopeAuthPlugin],
  scopeAuth: {
    authScopes: async (context) => ({
      isAuthenticated: !!context.var.loginUserAccountId, // ユーザーIDが設定されてればとりあえず認証済みとみなす
    }),
  },
});

builderからクエリを設定する際に認証ガードをかけたい場合は、以下のようにauthScopesに条件を設定します。


export const setQueries = () => {
  builder.queryType({
    fields: (t) => ({
      getUserInfoFromAuthHeader: t.field({
        type: UserAccountAuthResponse,
        authScopes: {
          isAuthenticated: true, // 認証ガードをかける場合
        },
        resolve: async (_root, _args, context) => {
          return await getUserAccountByUserAccountId(
            context.env,
            context.var.loginUserAccountId!,
          );
        },
      }),
    }),
  });
};

最後にGraphQLエンドポイントの処理です。ここでリクエストヘッダーからコンテキストへの設定処理を呼び出します。

app.use("/graphql", async (c, next) => {
  // スキーマをベースにhandlerを生成する処理(詳細は記載割愛)
  const handler = getGraphqlHandler(c.env.GRAPHIQL_ENABLE === "true");
  // 認証情報があればコンテキストにセットする
  setAuthInfoToContext(c, c.req.header("Authorization"));
  return await handler(c, next);
});
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?