JWT(JsonWebToken)を使った認証付きGraphQL APIを作る

以前にGraphQL+MongoDB+graphql-composeという構成のGraphQL APIの実装についてご紹介させていただきました。

この構成のGraphQL APIにユーザ認証が必要になりましたのでJWT(JsonWebToken)を使った認証機構を追加しました。

JWT認証を作る

JWTのパッケージをインストールします。

$ yarn add jsonwebtoken

auth.jsを作成しJWT認証を実装していきます。

import jwt from 'jsonwebtoken';

const authentication = (app) => {
    app.use(/^(?!\/login).*$/, async (req, res, next) => {
        if(req.headers.authorization === undefined || req.headers.authorization.split(' ')[0] !== 'Bearer'){
            const status = 401;
            const message = 'Format Error';
            res.status(status).json({ status, message });
            return
        }

        jwt.verify(req.headers.authorization.split(' ')[1], "secret", (err) => {
            if(err){
                return res.json({
                    success: false,
                    message: 'Token Error'
                });
            }
            next();
        });
    });
}

export default authentication;

内容を見ていきます。
まず、/login以外のURLで認証が必要になるように設定します。

const authentication = (app) => {
	app.use(/^(?!\/login).*$/, async (req, res, next) => {
	});
}

JWTをリクエストヘッダに含んだ状態でアクセスしてもらうのでヘッダにトークンが存在しない(もしくはフォーマットが違う)場合はフォーマットエラーを返します。

if(req.headers.authorization === undefined || req.headers.authorization.split(' ')[0] !== 'Bearer'){
            const status = 401;
            const message = 'Format Error';
            res.status(status).json({ status, message });
            return
        }

ヘッダにトークンが含まれていればトークンをチェックして、可否を返します。
今回は”secret”をシークレットワードとしています。

jwt.verify(req.headers.authorization.split(' ')[1], "secret", (err) => {
            if(err){
                return res.json({
                    success: false,
                    message: 'Token Error'
                });
            }
            next();
        });

server.js

server.jsを修正します。

先ほど実装したauth.jsをimportしましょう。

import jwt from 'jsonwebtoken';
import authentication from './utils/auth.js';

/loginをPOSTで設定し、リクエストパラメータ(user_id, password)を受け取って認証します。
今回はサンプルなので固定値でチェックし、ログインできればJWTを作成して返却します。

app.post('/login', (req, res) => {
    const { user_id, password } = req.body;

    if( user_id === "user" && password === "password" ){
        const token = jwt.sign({ user_id, password }, "secret", { expiresIn: "1h" });
        res.status(200).json({ token });
    }else{
        res.status(401).json({ status: 401,  message: "Login Error" });
    }
});

authentication(app)graphqlの前に追加することで/graphqlはログインが必要となります。

// 追加
authentication(app);

app.use(
    '/graphql',
    bodyParser.json(),
    graphqlExpress({ schema, graphiql: true })
);

サーバを起動しPostmanからログインAPIを叩いてみますが・・・。

TypeError: Cannot destructure property `user_id` of 'undefined' or 'null'.

パラメータが取得できません。。。

bodyParser

毎回忘れるのですが、bodyParserのurlencodedを設定しないとパラメータ取得ができません。
以下をserver.jsに追加します。

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

もう一度Postmanで試します。

これでログイン認証に成功し、JWTを取得することができました。

取得したJWTをヘッダに設定してGraphQLのQueryをPOSTしてみます。

無事、データを取得することができました。

ヘッダを設定しない場合は以下のようにエラーとなりますので、これで認証付きGraphQL APIの完成です。

まとめ

GraphQLにアクセスする前にログインAPIを叩くのではなくGraphQLで認証する方法もあるようです。

そちらについても機会があれば検証を行い、記事にしてみたいと思います。

今回使用したコードはこちらです。

https://github.com/aizulab-igarashi/graphql_compose_auth

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です