[Advent Calendar 2021][Day13]HasuraGraphQLの認可機能を使って権限を制御する

はじめに
Advent Calendar 2021のDay13。 HasuraGraphQLの認可機能を使って権限を制御していきます。 すでにanonymousで権限の設定は行っていますが、認証されたユーザーのみがInsertできるようにするなど、FirebaseAuthenticationとHasuraを組み合わせて実現していきます。
Firebase AuthenticationとHasura GraphQL
Firebase AuthenticationとHasura GraphQLの連携に関する記事は、Hasura公式でもありますし、Qiitaなどでも沢山の記事があがっています。
参考
https://hasura.io/blog/authentication-and-authorization-using-hasura-and-firebase/
JWTについて詳しい記事
参考
https://hasura.io/docs/latest/graphql/core/auth/authentication/jwt.html
今回は、Nuxt3+Hasura+FirebaseAuthenticationの組み合わせですが、基本的な考え方は前述の記事の内容と同じです。 少し試してわかったのは、Nuxt3だとFirebase Realtime Databaseではバグがあるようできちんと動きませんでした。 なので、Firestoreを使うことにします。
docker-composeを修正する
まずはローカルで動作させるようにするので、docker-composeを修正します。
HASURA_GRAPHQL_JWT_SECRET
を設定して、HasuraのJWT認証を有効にする。
- docker-compose.yml
HASURA_GRAPHQL_DEV_MODE: "true" HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log ## uncomment next line to set an admin secret+ HASURA_GRAPHQL_ADMIN_SECRET: myadminsecretkey+ HASURA_GRAPHQL_JWT_SECRET: ${HASURA_GRAPHQL_JWT_SECRET}+ HASURA_GRAPHQL_UNAUTHORIZED_ROLE: anonymousvolumes: db_data: null
.envを追加して、docker-composeが読み込むよう変数を設定します。
- .env
HASURA_GRAPHQL_JWT_SECRET='{"type":"RS256","jwk_url": "https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com", "audience": "<project_id>", "issuer": "https://securetoken.google.com/<project_id>"}'
<project_id>
の部分はプロジェクトIDに置き換えます。
codegenにAdminSecretを設定する
上述でADMIN_SECRETを設定したので、codegenでも生成できるようシークレットを設定しておきます。
version: 3endpoint: http://localhost:8080+admin_secret: myadminsecretkeymetadata_directory: metadataactions: kind: synchronous
hasuraのuser権限を設定する
権限については、user
というroleを追加します。
userは、insertができるようになります。また自分がinsertしたレコードに対しては、updateとdeleteができるようになります。
各テーブルには、uid
というカラムがあります。
insertされたときに、そのレコードのuid
をHasura側で自動的に設定します。
update
とdelete
は、そのレコードのuid
が自分のものであることを確認してから実行します。
deleteも同様にuid
を確認するよう修正します。
設定が完了したら、metadataをexportしておきます。
cd hasurahasura metadata export
カスタムクレイムを設定する
userが作成されたイベントをcloud functionsで受け取り、カスタムクレイムを設定します。 firebase cliをインストールして初期化します。firestoreを利用します。
npm install -g firebase-tools
mkdir firebase
cd firebase
firebase init
- firebase/functions/index.js
Firestoreで生成されたユーザーのデータを設定します。フロント側では、該当データをリッスンしてカスタムクレイムを読み出すよう対応します。
const functions = require("firebase-functions");const admin = require("firebase-admin");const {getFirestore, doc, setDoc} = require( "firebase/firestore");admin.initializeApp(functions.config().firebase);const db = getFirestore();// On sign up.exports.processSignUp = functions.auth.user().onCreate((user) => { const customClaims = { "https://hasura.io/jwt/claims": { "x-hasura-default-role": "user", "x-hasura-allowed-roles": ["user"], "x-hasura-user-id": user.uid, }, }; return admin .auth() .setCustomUserClaims(user.uid, customClaims) .then(() => { setDoc(doc(db, "users", user.uid), { refreshTime: new Date().getTime(), }); }) .catch((error) => { console.log(error); });});
firestoreのルールを設定する
firebaseコンソールからFirestoreのルールを設定します。/users/{userId}
に対して認証されたユーザのみがアクセスできるようにします。
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Make sure the uid of the requesting user matches name of the user
// document. The wildcard expression {userId} makes the userId variable
// available in rules.
match /users/{userId} {
allow read, update, delete: if request.auth != null && request.auth.uid == userId;
allow create: if request.auth != null;
}
}
}
firebase functionをデプロイする
firebase deploy --only functions
これは、リージョンやメモリなど細かく設定しておいたほうが良いですが、一旦はこれでよいです。
hasuraカスタムクレイムを取得する
結構差分が出ちゃったので、画面ショットで。 やっていることは、
- firestoreのimport,インスタンス取得
- cloud functionsでカスタムクレイムが設定されてから、getIdTokenでカスタムクレイムを取得する
- 各所でfirestoreのunsubscribe
まとめ
これらを行うことによって、以下の仕組みが整いました。
- FirebaseAuthenticationで認証
- Hasuraでロールによる権限制御
次回は、カレンダー登録機能を作っていきます。
