概要
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);
});