Express Apps でのユーザー認証の実装
正規のユーザーを認証することで、悪意のあるユーザーから保護できます。セキュリティ ホールを残さないように、必ずベスト プラクティスを使用してください。
ユーザー認証は、アプリケーションにアクセスしようとするユーザーの身元を確認するプロセスです。これには、ユーザーの信頼性を確認するための資格情報の承認と転送が含まれます。
Express、Bcrypt、MongoDB を使用して、わずか数ステップで簡単なユーザー認証モデルを Node.js に実装できます。
ステップ 1: 開発環境のセットアップ
まず、プロジェクト フォルダーを作成し、次のコマンドを実行してそこにcdします。
mkdir user-authentication
cd user-authentication
次に、次のコマンドを実行して、プロジェクト ディレクトリ内の npm を初期化します。
npm init -y
-y フラグは npm を初期化し、すべてのデフォルトを使用して package.json ファイルを作成します。
このユーザー認証モデルにはいくつかの依存関係が必要です。
それらには次のものが含まれます。
- Express: Express は、Web およびモバイル アプリケーションに堅牢な機能セットを提供する Node.js フレームワークです。 Node.js を使用したバックエンド アプリケーションの構築が容易になります。
- Bcrypt: bcrypt は、bcrypt パスワード ハッシュ関数を実装する npm パッケージです。これにより、プレーンなパスワード文字列からハッシュを作成できます。
- Mongoose: Mongoose は、MongoDB オブジェクト データ モデリング ライブラリです。これにより、アプリと MongoDB データベース間のやり取りが簡素化されます。
- dotenv: dotenv は、環境変数を .env ファイルから process.env にロードする依存関係のないパッケージです。
- Validator: validator は、さまざまな文字列検証関数が含まれるパッケージです。
- Body-parser: body-parser パッケージは、ハンドラーの前にミドルウェア内のリクエスト本文を解析します。
以下を実行してパッケージをインストールします。
npm install express bcrypt mongoose dotenv validator body-parser
次に、プロジェクトのルート ディレクトリにapp.js ファイルを作成し、以下のコード ブロックを追加して基本的な Express サーバーを作成します。
// app.js
const express = require('express');
const app = express();
const bodyParser = require("body-parser");
const port = 3000;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.listen(port, ()=>{
console.log(`App is listening on port ${port}`);
});
このコードは、express 関数を呼び出して Express アプリケーション インスタンスを作成します。次に、body-parser ミドルウェアを使用して、受信したリクエストの本文を解析します。次に、Express インスタンスの listen メソッドを呼び出し、ポート変数を引数として渡すことにより、ポート 3000 でトラフィックの待機を開始します。
ステップ 2: アプリケーションをデータベースに接続する
プロジェクトのルート ディレクトリに .env ファイルを作成し、MongoDB 認証情報をそのファイルに保存します。これにより、悪意のあるユーザーにデータベースへのアクセスを許可する可能性のあるコード内でデータベースの資格情報が公開されることが回避されます。
次に、app.js ファイルに移動し、mongoose をインポートします。
const mongoose = require("mongoose");
次に、 import dotenv を呼び出し、その上で config メソッドを呼び出します。
require("dotenv").config();
dotenv で config メソッドを呼び出すと、環境変数が process.env にロードされます。
最後に、mongoose で connect メソッドを呼び出し、MongoDB URI を引数として渡します。
mongoose.connect(process.env.MONGODB_URI).then(() => {
console.log('Connected to Database Successfully')
})
ステップ 3: ユーザーモデルの作成
プロジェクトのルート ディレクトリに、「models」フォルダを作成します。ここにマングース モデルを保存します。
mkdir models
次に、「userModel」ファイルを作成し、次のインポートを追加します。
const mongoose = require('mongoose')
const { isEmail } = require('validator')
isEmail は、指定された文字列が電子メールの場合に true を返す検証関数です。マングース検証をユーザー モデルに適用するために必要になります。
次に、次のコードをuserModel ファイルに追加します。
// models/userModel
const userSchema = mongoose.Schema({
email: {
type: String,
required: [true, 'Email is required'],
validate: {
validator: isEmail,
message: props => `${props.value} is not a valid email`
}
},
password: {
type: String,
required: [true, 'Password is required'],
validate: {
validator: function (value) {
return value.length >= 6
},
message: () => 'Password must be at least six characters long'
}
}
})
module.exports = mongoose.model('User', userSchema)
上記のコードは、mongoose.Schema メソッドの値を格納する userSchema 変数を作成します。 mongoose.Schema メソッドは、プロパティを MongoDB コレクションにマップし、そのコレクション内のドキュメントの形状を定義します。 mongoose スキーマには、メールとパスワードの 2 つのプロパティがあり、これらが認証要件になります。
email プロパティは文字列タイプであり、必須が true に設定されています。リクエスト本文に電子メールプロパティが含まれていない場合、付随するエラー メッセージ「電子メールが必要です」が表示されます。最後に、mongoose カスタム検証を使用して、validator プロパティがisEmail 関数を参照します。この関数は、電子メールとしての文字列の有効性に基づいて true または false を返します。次に、message プロパティは電子メールの値 (props) を受け取り、意味のあるエラー メッセージを構築します。
パスワード プロパティは必須の文字列タイプであり、「パスワードが必要です」というエラー メッセージが表示されます。 バリデータ関数は、パスワードが 6 文字以上の場合に true を返す匿名関数です。
最後の行では、mongoose の model メソッドを呼び出して、mangoose モデルを作成してエクスポートします。最初の引数としてモデル名 (User) を渡し、 2 番目の引数としてスキーマ (userSchema) を指定します。
ステップ 4: サインインおよびサインアップ ルートの実装
プロジェクトのルート ディレクトリに、routes フォルダーを作成します。
mkdir routes
ルート フォルダーにuserRoutes.js ファイルを作成し、次のインポートを追加します。
// routes/userRoutes.js
const express = require("express");
const User = require("../models/userModel");
const bcrypt = require("bcrypt");
express で Router メソッドを呼び出して、Express Router インスタンスを作成します。
const router = express.Router();
次に、以下のコード ブロックを userRoute.js ファイルに追加して、サインアップ ルートを作成します。
router.post("/sign-up", async (req, res) => {
try {
// Extract email and password from the req.body object
const { email, password } = req.body;
// Check if the email is already in use
let userExists = await User.findOne({ email });
if (userExists) {
res.status(401).json({ message: "Email is already in use." });
return;
}
// Define salt rounds
const saltRounds = 10;
// Hash password
bcrypt.hash(password, saltRounds, (err, hash) => {
if (err) throw new Error("Internal Server Error");
// Create a new user
let user = new User({
email,
password: hash,
});
// Save user to database
user.save().then(() => {
res.json({ message: "User created successfully", user });
});
});
} catch (err) {
return res.status(401).send(err.message);
}
});
上記のコード ブロックでは、まず、req.body オブジェクトから電子メールとパスワードを構造化解除しました。次に、電子メールはユーザーごとに一意である必要があるため、ユーザーがすでにその電子メールを使用しているかどうかを確認します。電子メールがすでに使用されている場合は、401 ステータス コードを返してコードの実行を停止します。
プレーンなパスワードをデータベースに保存すると、悪意のあるハッカーがデータベースにアクセスできる可能性があるため、セキュリティ上の大きな脅威となります。パスワードをデータベースに格納する前にハッシュ化する必要があります。そのため、ハッカーにパスワードが発見されたとしても、ユーザーに危険が及ぶことはありません。ハッシュ化は、指定された「キー」を別の値に変換するプロセスです。ハッシュ化は一方向関数です。つまり、暗号化とは異なり、ハッシュ化された値から元の値を取得することはできません。
bcrypt を使用すると、bcrypt のハッシュ メソッドを呼び出してユーザー パスワードをハッシュします。ハッシュ メソッドは、ハッシュされる文字列、ソルト ラウンド、コールバック関数の 3 つのパラメータを取ります。ユーザーのパスワード、前に作成した SaltRounds 変数、およびコールバックを渡します。
ソルト ラウンドとは、単一の bcrypt ハッシュを計算するのに必要な時間を指します。ソルト ラウンドの数が多いほど、ハッシュ ラウンドの数も多くなります。
ハッシュ メソッドがエラーをスローすると、「内部サーバー エラー」がスローされます。それ以外の場合は、パスワード プロパティを成功したハッシュに設定し、ユーザー インスタンスの save メソッドを呼び出してデータベースに保存します。
次に、以下のコード ブロックを userRoute.js ファイルに追加して、サインイン ルートを作成します。
router.post("/sign-in", async (req, res) => {
try {
// Extract email and password from the req.body object
const { email, password } = req.body;
// Check if user exists in database
let user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ message: "Invalid Credentials" });
}
// Compare passwords
bcrypt.compare(password, user.password, (err, result) => {
if (result) {
return res.status(200).json({ message: "User Logged in Successfully" });
}
console.log(err);
return res.status(401).json({ message: "Invalid Credentials" });
});
} catch (error) {
res.status(401).send(err.message);
}
});
module.exports = router;
上記のコード ブロックでは、まず、req.body オブジェクトから電子メールとパスワードを構造解除します。次に、データベースにユーザーが存在するかどうかを確認します。ユーザーがデータベースに存在しない場合は、401 ステータス コードが返されます。
次に、bcrypt の比較メソッドを使用して、ユーザーが指定したパスワードとデータベースから取得したハッシュ化されたパスワードを渡します。 2 つを比較して、一致するかどうかを確認します。パスワードが一致すると、ステータス コード 200 と成功メッセージが返されます。それ以外の場合は、401 ステータス コードとエラー メッセージが返されます。
最後に、ルーターをapp.js ファイルにインポートし、アプリケーションレベルのミドルウェアとして使用します。
これでユーザー認証モデルが完成しました。これで、ユーザーは安全にサインアップしてアプリケーションにサインインできるようになりました。
ユーザー認証の重要性
ユーザー認証により、正当なユーザーのみがアプリケーションにアクセスできるようになります。データが何らかの形で個人的または非公開である場合は、認証されていないユーザーがアクセスできないようにする措置を講じる必要があります。