February 11, 2024
私たちはいくつかのAPIに精通しており、各APIには次のような独自の認証方法があることに気づいたかもしれません
これらの認証方法にはさまざまなレベルのセキュリティと複雑さがあり、アプリケーションの要件、セキュリティ上の考慮事項、ユーザーエクスペリエンスなどの要素によって選択方法が異なります。
この記事では、graphql API に JWT 認証を実装します。データベースについては、graphql サーバーに MongoDB と Apollo サーバーを使用します。
JWT は認証に広く使われている方法です。これには、エンコードされたユーザー情報を含むトークンを生成し、クライアントとサーバーの間で後続のリクエスト用に渡す方法が含まれます。
JSON Web トークン (JWT) には、ヘッダー、ペイロード、署名という 3 つの主要コンポーネントがあります。それぞれについて簡単に説明します。
JSON Web トークン (JWT) にはいくつかの利点があり、Web アプリケーションでの認証や情報交換によく使われています。
こちらがGitHubのリンクです https://github.com/icon-gaurav/mastering-graphql-with-nodejs/tree/jwt-authentication
これを複製して、私の説明に従うことができます。
ユーザーが電子メール、ユーザー名、パスワードを持ち、電子メールとパスワードを使用してユーザーを認証し、ユーザーに対応するJWTトークンを生成する簡単な認証を行います。その後、この JWT トークンを使用して API を認証します。
実装を段階的に行いましょう
使用します bcrypt ライブラリを使用してパスワードを暗号化し、ハッシュされたパスワードとログインプロセスでユーザーが入力したパスワードを比較します
npm install --save bcrypt jsonwebtoken
ユーザースキーマを定義し、そのスキーマを MongoDB と同期する方法はすでにわかっています。もう一度やり直したい場合は、こちらの記事へのリンクをご覧ください [MongoDB を使用したグラフィカル API]
// mongodb schema for user object
const userSchema = new Schema({
username:String,
email:String,
password:String
});
// defining user model
const User = mongoose.model("User", userSchema);
Register user mutationは、データベースに新しいユーザーを作成し、プレーンテキストのパスワードを、以下を使用してハッシュ文字列に変換します bcrypt。
// register user mutation
registerUser: async (_parent: any, args: any) => {
const {email, password, username} = args;
// we are storing hashed password to the database
const newUser = new User(
{
email,
password: bcrypt.hashSync(password, bcrypt.genSaltSync(10)),
username: username ?? email
})
return await newUser.save();
}
を生成するログインミューテーションを実装する jwt ユーザーから提供された認証情報に基づくユーザー用
// login mutation
login: async (_parent: any, args: any, _context: any) => {
const {email, password} = args;
const requestedUser = await User.findOne({email: email});
/*
we are using bcrypt to compare 2 passwords as we stored hashed password and not the plain text
for security reasons
*/
if (requestedUser && bcrypt.compareSync(password, requestedUser?.password as string)) {
// user has provided correct email and password
// generate the signed jwt token
const token = jwt.sign(requestedUser, "myprivatekey", {expiresIn: '2h'})
// return the auth payload
return {
token,
user: requestedUser
}
}else{
return new Error('Email or password is incorrect!')
}
}
ログインミューテーションへの応答として JWT トークンがあります。この JWT トークンを使用すると、E メールとパスワードを何度も入力しなくても API を認証できます。
の JWT トークンの検証 jwt.io
リゾルバーにユーザー情報があるかどうかを確認するチェックポイントを作成して、リゾルバーを保護します。「はい」の場合は認証され、そうでない場合は認証エラーが発生します。
// securing user related post resolver
posts: async (_parent: any, _args: any, context: any) => {
// fetch the user from the context
const {user} = context;
if (user) {
return Post.find();
} else {
return new Error("Unauthenticated!")
}
},
リゾルバーでユーザー情報をチェックしているので、どうにかして渡さなければなりません。そこで、コンテキストを使用してユーザーをすべてのリゾルバーに渡し、JWT トークンが有効かどうかを検証する JWT ミドルウェアを使用します。
このミドルウェアの主な機能は、 jwt 有効かどうか。JWT が有効な場合はペイロードを返し、それ以外の場合は null を返します。
// jwt validation check middleware
const jwtValidationMiddleware = (token: string) => {
if (token) {
return jwt.verify(token?.split(' ')?.[1], "myprivatekey")
}
}
これで JWT ペイロードができました。次のステップは、ユーザー情報をリゾルバーに渡すことです。
JWT ペイロード情報、つまりユーザー情報をすべてのリゾルバーに渡して、ユーザーの認証を確認します。
// context code
startStandaloneServer(server, {
context: async ({req, res}) => ({
user: jwtValidationMiddleware(req?.headers?.authorization as string),
}),
listen: {port: 4000}
})
.then(({url}: any) => {
console.log(`🚀 Server listening at: ${url}`);
// connect the mongodb database
// Database url from atlas cloud
const DATABASE_URL: string = `mongodb+srv://gauravbytes:Zah5jnclaMXbzANl@gauravbytes.buvimdx.mongodb.net/?retryWrites=true&w=majority`
mongoose.connect(DATABASE_URL)
.then(() => {
console.log('Database connected successfully')
})
.catch((e: any) => {
console.log('Error connecting : ', e?.message)
})
});
ステップ 1: ユーザーの投稿を何も取得せずに取得してみる jwt トークンが渡されました。エラーが発生するはずです
ステップ 2: ログインミューテーションを使用して新しい JWT トークンを生成する
ステップ3: トークンを使用して、このトークンをBearerヘッダーとして渡し、投稿を取得してみます。今度は投稿のリストが表示されます。
おめでとうございます。Graphql APIにJWT認証を正常に実装し、動作しています。