はじめに
ユーザーデータを保護し、情報への安全なアクセスを可能にする場合、どのアプリケーションまたはシステムでも認証が重要であることは明白な現実です。認証とは、何かが真実、正当、または有効であることを確立または実証する手順です。
前提条件
このチュートリアルは実践的なデモンストレーションです。フォローするには、次のものが揃っていることを確認してください。
- NestJSはNode.jsフレームワークであるため、システムで実行されているNode.js
- MongoDBがインストールされています
NestJSとは何ですか?
Nest(NestJS)は、スケーラブルで効率的なアプリケーションを構築するためのNode.jsサーバー側アプリケーションフレームワークです。
TypeScriptで記述され、Express上に構築されています。これは、それ自体は優れていますが、構造が不足している非常に最小限のフレームワークです。オブジェクト指向プログラミング、関数型プログラミング、関数型リアクティブプログラミングなどのプログラミングパラダイムを組み合わせています。
これは、バックエンドに多くの構造が必要な場合に使用するフレームワークです。その構文と構造は、フロントエンドフレームワークであるAngularJSと非常によく似ています。また、AngularJSと同じように、TypeScript、サービス、依存性注入を使用します。
モジュールとコントローラーを採用しており、コマンドラインインターフェイスを使用してファイルのコントローラーを作成できます。
NestJSモジュールを使用すると、関連するコントローラーとサービスプロバイダーを単一のコードファイルにグループ化できます。簡単に言えば、NestJSモジュールは @Moduleを含むTypeScriptファイルです。 注釈()。このデコレータは、NestJSフレームワークに、どのコントローラ、サービスプロバイダー、およびその他の関連リソースがインスタンス化され、後でアプリコードによって使用されるかを通知します。
セッションベースの認証とは何ですか?
セッションベースの認証は、ログインに成功した後、サーバーがセッションを作成するユーザー認証の方法であり、セッションIDはCookieまたはブラウザのローカルストレージに保存されます。
その後のリクエストで、Cookieはサーバーに保存されているセッションIDに対して検証されます。一致するものがある場合、リクエストは有効であると見なされ、処理されます。
この認証方法を使用するときは、次のセキュリティのベストプラクティスを念頭に置くことが重要です。
- ブルートフォース攻撃を無効にするために、長くてランダムなセッションID(128ビットが推奨される長さ)を生成します
- 機密データやユーザー固有のデータの保存は避けてください
- すべてのセッションベースのアプリでHTTPS通信を必須にする
- 安全でHTTPのみの属性を持つCookieを作成する
セッションベースの認証を行う理由
セッションベースの認証は、シンプルで安全であり、ストレージサイズが制限されているため、ほとんどの認証方法よりも安全です。また、同じルートドメイン内のWebサイトに最適なオプションであると考えられています。
プロジェクトの設定
Nest CLIをグローバルにインストールして、プロジェクトのセットアップを開始します。すでにNestJSCLIがインストールされている場合は、これを行う必要はありません。
Nest CLIは、Nestアプリケーションをセットアップ、開発、および保守するためのコマンドラインインターフェイスツールです。
npm i -g @nestjs/cli
それでは、次のコマンドを実行してプロジェクトを設定しましょう。
nest new session-based-auth
上記のコマンドは、いくつかの定型文を含むNestアプリケーションを作成し、アプリケーションを実行するために必要なモジュールをインストールするために、好みのパッケージマネージャーを選択するように求めます。デモンストレーションのために、このチュートリアルでは npmを使用します 。 Enterキーを押して、 npmを続行します 。
すべてがうまくいけば、端末の以下のスクリーンショットのような出力が表示されるはずです。
インストールが完了したら、プロジェクトディレクトリに移動し、次のコマンドを使用してアプリケーションを実行します。
npm run start:dev
上記のコマンドは、アプリケーションを実行し、変更を監視します。プロジェクトのsrc
フォルダ構造は次のようになります。
└───src
│ └───app.controller.ts
│ └───app.modules.ts
│ └───app.service.ts
│ └───main.ts
依存関係のインストール
アプリケーションがセットアップされたので、必要な依存関係をインストールしましょう。
npm install --save @nestjs/passport passport passport-local
上記のコマンドは、人気のあるnest.js認証ライブラリであるPassport.jsをインストールします。
また、以下のコマンドを使用して戦略のタイプをインストールします。
passport-local
の型定義が含まれています 。
npm install --save-dev @types/passport-local
NestJSでMongoDBデータベースを設定する
データベースをセットアップして接続するには、次のコマンドを使用してMongooseパッケージとNestJSラッパーをインストールします。
npm install --save @nestjs/mongoose mongoose
Mongoose NestJSラッパーは、NestJSアプリケーションでMongooseを使用するのに役立ち、承認されたTypeScriptサポートを提供します。
次に、 app.module.ts
にアクセスします。 、 mongoose
をインポートします @ nestjs / mongoose
のモジュール 。次に、 forRoot()
を呼び出します メソッド、Mongooseモジュールによって提供されるメソッドであり、データベースのURL文字列を渡します。
app.module.ts
でデータベース接続を設定する サーバーが起動するとすぐに、アプリケーションがデータベースに接続するのに役立ちます。アプリケーションを実行した後、最初にロードされるモジュールであるためです。
app.module.ts
import { Module } from "@nestjs/common"
import { MongooseModule } from "@nestjs/mongoose"
import { AppController } from "./app.controller"
import { AppService } from "./app.service"
@Module({
imports: [
MongooseModule.forRoot(
"mongodb+srv://<username>:<password>@cluster0.kngtf.mongodb.net/session-auth?retryWrites=true&w=majority"
),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
ユーザーモジュールの作成
関心の分離については、コードをクリーンで整理されたものにするために、次のコマンドを実行して、NestJSCLIを使用するユーザー専用のモジュールを作成します。
nest g module users
上記のコマンドは、 users
を作成します users.module.ts
のあるフォルダー app.module.ts
を更新します
また、 users.service.ts
を作成します およびusers.controller.ts
次のコマンドを使用するファイル:
nest g service users
nest g controller users
ネストCLIを使用せずに手動でフォルダとファイルを作成できますが、CLIを使用すると、必要なフォルダが自動的に更新され、作業が楽になります。
ユーザースキーマの作成
次のステップはUserSchemaを作成することですが、最初に users.model.ts
を追加します UserSchema
を作成するファイル
これは、アプリケーションの src
の形である必要があります 今すぐフォルダ。
└───src
│ └───users
│ │ └───users.controller.ts
│ │ └───users.model.ts
│ │ └───users.module.ts
│ │ └───users.service.ts
│ └───app.controller.ts
│ └───app.module.ts
│ └───app.service.ts
│ └───main.ts
UserSchema
を作成するには 、 users.model.ts
のmongooseパッケージからすべてをmongooseとしてインポートします 。次に、ユーザーモデルの青写真である新しいマングーススキーマを呼び出し、ユーザーオブジェクトとデータを定義するJavaScriptオブジェクトを渡します。
users.model.ts
import * as mongoose from "mongoose"
export const UserSchema = new mongoose.Schema(
{
username: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
},
{ timestamps: true }
)
export interface User extends mongoose.Document {
_id: string;
username: string;
password: string;
}
また、MongoDBコレクションの作成に役立つドキュメントであるmongooseを拡張するモデルのインターフェイスを作成します。
users.module.ts
にアクセスしてください MongooseModule
をインポートします imports配列内。次に、 forFeature()
を呼び出します MongooseModule
によって提供されるメソッド 、名前とスキーマを受け取るオブジェクトの配列を渡します。
これにより、依存性注入の助けを借りて、どこでもファイルを共有できるようになります。
users.module.ts
import { Module } from "@nestjs/common"
import { MongooseModule } from "@nestjs/mongoose"
import { UsersController } from "./users.controller"
import { UserSchema } from "./users.model"
import { UsersService } from "./users.service"
@Module({
imports: [MongooseModule.forFeature([{ name: "user", schema: UserSchema }])],
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
users.module.ts
内 、 UsersService
をエクスポートします 別のモジュールでアクセスできるようにします。
users.module.ts
import { Module } from "@nestjs/common"
import { MongooseModule } from "@nestjs/mongoose"
import { UsersController } from "./users.controller"
import { UserSchema } from "./users.model"
import { UsersService } from "./users.service"
@Module({
imports: [MongooseModule.forFeature([{ name: "user", schema: UserSchema }])],
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}
通常、ビジネスロジックを別のクラスにカプセル化することをお勧めします。このようなクラスはサービスと呼ばれます。このクラスの仕事は、コントローラーの要求を処理し、ビジネスロジックを実行することです。
users.service.ts
内 ファイル、インポート Model
mongoose
から 、ユーザー
users.model.ts
から 、および InjectModel
@ nestjs / mongoose
から 。次に、 UsersService
にメソッドを追加します ユーザー名とパスワードを受け取り、メソッド insertUser()
を呼び出すクラス 。
users.service.ts
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { User } from './users.model';
@Injectable()
export class UsersService {
constructor(@InjectModel('user') private readonly userModel: Model<User>) {}
async insertUser(userName: string, password: string) {
const username = userName.toLowerCase();
const newUser = new this.userModel({
username,
password,
});
await newUser.save();
return newUser;
}
}
これで、 UsersService
クラスの準備ができたら、コントローラーに注入する必要があります。ただし、最初に、ユーザーのパスワードを安全に保管する方法について説明しましょう。
登録手順の最も重要な側面はユーザーのパスワードです。これはプレーンテキストで保存してはなりません。強力なパスワードを作成するのはユーザーの責任ですが、パスワードを安全に保つことは開発者としてのあなたの義務です。データベースの侵害が発生した場合、ユーザーのパスワードが公開されます。また、プレーンテキストで保存するとどうなりますか?私はあなたが答えを知っていると信じています。これに対処するには、bcryptを使用してパスワードをハッシュします。
したがって、 bcrypt
をインストールします および@types / bcrypt
次のコマンドを使用します:
npm install @types/bcrypt bcrypt
それが邪魔にならないように、コントローラーをセットアップします。まず、 UsersService
をインポートします クラスとbcrypt
からのすべて 。次に、ユーザーを追加できるコンストラクターとメソッドを追加します。着信投稿リクエストを処理し、 addUser
と呼びます 、パスワードをハッシュする関数本体を使用します。
users.controller.ts
import { Body, Controller, Post } from '@nestjs/common';
import { UsersService } from './users.service';
import * as bcrypt from 'bcrypt';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
//post / signup
@Post('/signup')
async addUser(
@Body('password') userPassword: string,
@Body('username') userName: string,
) {
const saltOrRounds = 10;
const hashedPassword = await bcrypt.hash(userPassword, saltOrRounds);
const result = await this.usersService.insertUser(
userName,
hashedPassword,
);
return {
msg: 'User successfully registered',
userId: result.id,
userName: result.username
};
}
}
登録はapp.module.ts
で行われます UsersModule
を追加することで実現されるファイル @Module()
へ app.module.ts
のデコレータのインポート配列 。
app.module.ts
import { Module } from "@nestjs/common"
import { MongooseModule } from "@nestjs/mongoose"
import { AppController } from "./app.controller"
import { AppService } from "./app.service"
import { UsersModule } from "./users/users.module"
@Module({
imports: [
MongooseModule.forRoot(
"mongodb+srv://<username>:<password>@cluster0.kngtf.mongodb.net/session-auth?retryWrites=true&w=majority"
),
UsersModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
おめでとう!登録は完了です。これで、ユーザー名とパスワードを使用してユーザーを登録できます。
ここで、登録が邪魔にならないようにして、 getUser
を追加します UsersService
への関数 findOne
を使用 ユーザー名でユーザーを検索する方法。
users.service.ts
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { User } from './users.model';
@Injectable()
export class UsersService {
constructor(@InjectModel('user') private readonly userModel: Model<User>) {}
async insertUser(userName: string, password: string) {
const username = userName.toLowerCase();
const newUser = new this.userModel({
username,
password,
});
await newUser.save();
return newUser;
}
async getUser(userName: string) {
const username = userName.toLowerCase();
const user = await this.userModel.findOne({ username });
return user;
}
}
認証モジュールの作成
ユーザーの場合と同様に、すべての認証/検証専用の認証モジュールとサービスを作成します。これを行うには、次のコマンドを実行します。
nest g module auth
nest g service auth
上記により、認証フォルダー auth.module.ts
が作成されます。 、および auth.service.ts
、 auth.module.ts
を更新します およびapp.module.ts
ファイル。
この時点で、アプリケーションの形状は src
フォルダは次のようになります。
└───src
│ └───auth
│ │ └───auth.module.ts
│ │ └───auth.service.ts
│ └───users
│ │ └───users.controller.ts
│ │ └───users.model.ts
│ │ └───users.module.ts
│ │ └───users.service.ts
│ └───app.controller.ts
│ └───app.module.ts
│ └───app.service.ts
│ └───main.ts
上記の生成コマンドは、 app.module.ts
を更新します 、以下のコードスニペットのようになります:
app.module.ts
import { Module } from "@nestjs/common"
import { MongooseModule } from "@nestjs/mongoose"
import { AppController } from "./app.controller"
import { AppService } from "./app.service"
import { UsersModule } from "./users/users.module"
import { AuthModule } from './auth/auth.module';
@Module({
imports: [UsersModule, AuthModule, MongooseModule.forRoot(
//database url string
'mongodb://localhost:27017/myapp'
)],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
ユーザーの認証
auth.module.ts
に移動します ファイルを作成し、 UsersModule
を追加します imports配列で、 UsersService
へのアクセスを有効にします users.module.ts
からエクスポート ファイル。
auth.module.ts
import { Module } from "@nestjs/common"
import { UsersModule } from "src/users/users.module"
import { AuthService } from "./auth.service"
@Module({
imports: [UsersModule],
providers: [AuthService],
})
export class AuthModule {}
auth.service.ts
で ファイル、コンストラクターを呼び出して、 UsersService
を挿入できるようにします 、およびユーザー名とパスワードを取得する検証方法を追加します。
基本的な検証を追加するには、ユーザーがデータベースに存在するかどうかを確認し、指定されたパスワードをデータベース内のパスワードと比較して、一致することを確認します。存在する場合は、 request.user
でユーザーを返します オブジェクト—それ以外の場合は、nullを返します。
auth.service.ts
import { Injectable, NotAcceptableException } from '@nestjs/common';
import { UsersService } from 'src/users/users.service';
import * as bcrypt from 'bcrypt';
@Injectable()
export class AuthService {
constructor(private readonly usersService: UsersService) {}
async validateUser(username: string, password: string): Promise<any> {
const user = await this.usersService.getUser(username);
const passwordValid = await bcrypt.compare(password, user.password)
if (!user) {
throw new NotAcceptableException('could not find the user');
}
if (user && passwordValid) {
return {
userId: user.id,
userName: user.username
};
}
return null;
}
}
さらに進んで、新しいファイルを作成し、 local.strategy.ts
という名前を付けます。 。このファイルは、 Passport.js
からの戦略を表します 、以前にインストールした、それがローカル戦略
。その中で、 Strategy
である戦略を渡します。 passport-local
から 。
コンストラクターを作成し、 AuthService
を挿入します 、 super()
を呼び出します 方法;必ずsuper()
を呼び出してください メソッド。
local.strategy.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-local';
import { AuthService } from './auth.service';
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private readonly authService: AuthService) {
super();
}
async validate(username: string, password: string): Promise<any> {
const userName = username.toLowerCase();
const user = await this.authService.validateUser(userName, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
auth.module.ts
に戻ります ファイル。次に、 PassportModule
を追加します インポートしてLocalStrategy
プロバイダーへ。
auth.module.ts
import { Module } from "@nestjs/common"
import { PassportModule } from "@nestjs/passport"
import { UsersModule } from "src/users/users.module"
import { AuthService } from "./auth.service"
import { LocalStrategy } from "./local.strategy"
@Module({
imports: [UsersModule, PassportModule],
providers: [AuthService, LocalStrategy],
})
export class AuthModule {}
次に、ログインルートを users.controller.ts
に追加します :
users.controller.ts
import {
Body,
Controller,
Post,
Request,
} from '@nestjs/common';
import * as bcrypt from 'bcrypt';
import { UsersService } from './users.service';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
//post / signup
@Post('/signup')
async addUser(
@Body('password') userPassword: string,
@Body('username') userName: string,
) {
const saltOrRounds = 10;
const hashedPassword = await bcrypt.hash(userPassword, saltOrRounds);
const result = await this.usersService.insertUser(
userName,
hashedPassword,
);
return {
msg: 'User successfully registered',
userId: result.id,
userName: result.username
};
}
//Post / Login
@Post('/login')
login(@Request() req): any {
return {User: req.user,
msg: 'User logged in'};
}
}
これらがすべて整ったので、ログインルートをトリガーするものがないため、ユーザーにログインすることはできません。ここでは、Guardsを使用してそれを実現します。
ファイルを作成し、 local.auth.guard.ts
という名前を付けます 、次にクラス LocalAuthGuard
AuthGuard
を拡張します NestJS / passport
から 、戦略の名前を入力し、戦略の名前 local
を渡します。 。
local.auth.guard.ts。
import { Injectable } from "@nestjs/common"
import { AuthGuard } from "@nestjs/passport"
@Injectable()
export class LocalAuthGuard extends AuthGuard("local") {}
UseGuard
を追加します users.controller.ts
のログインルートへのデコレータ ファイルを作成し、 LocalAuthGuard
を渡します 。
users.controller.ts
import {
Body,
Controller,
Post,
UseGuards,
Request,
} from '@nestjs/common';
import * as bcrypt from 'bcrypt';
import { LocalAuthGuard } from 'src/auth/local.auth.guard';
import { UsersService } from './users.service';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
//post / signup
@Post('/signup')
async addUser(
@Body('password') userPassword: string,
@Body('username') userName: string,
) {
const saltOrRounds = 10;
const hashedPassword = await bcrypt.hash(userPassword, saltOrRounds);
const result = await this.usersService.insertUser(
userName,
hashedPassword,
);
return {
msg: 'User successfully registered',
userId: result.id,
userName: result.username
};
}
//Post / Login
@UseGuards(LocalAuthGuard)
@Post('/login')
login(@Request() req): any {
return {User: req.user,
msg: 'User logged in'};
}
}
最後に、登録されたユーザー名とパスワードでユーザーにログインできます。
認証ルートを保護する
これで、ユーザー認証が正常に設定されました。次に、認証されたユーザーのみにアクセスを制限することにより、不正アクセスからルートを保護します。 users.controller.ts
に移動します ファイルを作成し、別のルートを追加します—「protected」という名前を付けて、 req.user
を返すようにします オブジェクト。
users.controller.ts
import {
Body,
Controller,
Get,
Post,
UseGuards,
Request,
} from '@nestjs/common';
import * as bcrypt from 'bcrypt';
import { LocalAuthGuard } from 'src/auth/local.auth.guard';
import { UsersService } from './users.service';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
//signup
@Post('/signup')
async addUser(
@Body('password') userPassword: string,
@Body('username') userName: string,
) {
const saltOrRounds = 10;
const hashedPassword = await bcrypt.hash(userPassword, saltOrRounds);
const result = await this.usersService.insertUser(
userName,
hashedPassword,
);
return {
msg: 'User successfully registered',
userId: result.id,
userName: result.username
};
}
//Post / Login
@UseGuards(LocalAuthGuard)
@Post('/login')
login(@Request() req): any {
return {User: req.user,
msg: 'User logged in'};
}
// Get / protected
@Get('/protected')
getHello(@Request() req): string {
return req.user;
}
}
上記のコードの保護されたルートは、ログインしているユーザーがすでにログインを失ったためにリクエストを行ったときに、ユーザーの詳細を返すのではなく、空のオブジェクトを返します。
これを分類するために、ここでセッションベースの認証が役立ちます。
セッションベースの認証では、ユーザーがログインすると、ユーザーはセッションに保存されるため、ログイン後のユーザーによる後続の要求は、セッションから詳細を取得し、ユーザーに簡単なアクセスを許可します。ユーザーがログアウトすると、セッションは期限切れになります。
セッションベースの認証を開始するには、次のコマンドを使用してexpress-sessionおよびNestJSタイプをインストールします。
npm install express-session @types/express-session
インストールが完了したら、 main.ts
に移動します アプリケーションのルートであるファイルを作成し、そこで構成を行います。
passport
からすべてをインポートします およびexpress-session
、次にパスポート初期化とパスポートセッションを追加します。
秘密鍵を環境変数に保持することが望ましいです。
main.ts
import { NestFactory } from "@nestjs/core"
import { AppModule } from "./app.module"
import * as session from "express-session"
import * as passport from "passport"
async function bootstrap() {
const app = await NestFactory.create(AppModule)
app.use(
session({
secret: "keyboard",
resave: false,
saveUninitialized: false,
})
)
app.use(passport.initialize())
app.use(passport.session())
await app.listen(3000)
}
bootstrap()
新しいファイルauthenticated.guard.ts
を追加します 、 auth
フォルダ。そして、リクエストを行うユーザーのセッションがあるかどうかをチェックする新しいGuardを作成します— authenticatedGuard
という名前を付けます 。
authenticated.guard.ts
import { CanActivate, ExecutionContext, Injectable } from "@nestjs/common"
@Injectable()
export class AuthenticatedGuard implements CanActivate {
async canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest()
return request.isAuthenticated()
}
}
上記のコードでは、リクエストはコンテキストから取得され、認証されているかどうかがチェックされます。 isAuthenticated()
passport.js
から取得 自動的;それは言う。 「ねえ!このユーザーのセッションは存在しますか?存在する場合は、続行してください。」
ログインをトリガーするには、 users.controller.ts
で ファイル:
- インポート
認証済みコード>
authenticated.guard.ts
から; -
useGuard
を追加します保護された
へのデコレータ ルート;そして、 -
AuthenticatedGuard
を渡します 。
users.controller.ts
import {
Body,
Controller,
Get,
Post,
UseGuards,
Request,
} from '@nestjs/common';
import * as bcrypt from 'bcrypt';
import { AuthenticatedGuard } from 'src/auth/authenticated.guard';
import { LocalAuthGuard } from 'src/auth/local.auth.guard';
import { UsersService } from './users.service';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
//signup
@Post('/signup')
async addUser(
@Body('password') userPassword: string,
@Body('username') userName: string,
) {
const saltOrRounds = 10;
const hashedPassword = await bcrypt.hash(userPassword, saltOrRounds);
const result = await this.usersService.insertUser(
userName,
hashedPassword,
);
return {
msg: 'User successfully registered',
userId: result.id,
userName: result.username
};
}
//Post / Login
@UseGuards(LocalAuthGuard)
@Post('/login')
login(@Request() req): any {
return {User: req.user,
msg: 'User logged in'};
}
//Get / protected
@UseGuards(AuthenticatedGuard)
@Get('/protected')
getHello(@Request() req): string {
return req.user;
}
}
この時点では、 express-session
のみを構成しているため、失敗します。 しかし、それを実装しませんでした。
ユーザーがログインするときは、ユーザーがセッションで他のルートにアクセスできるように、ユーザーをセッションに保存する必要があります。
覚えておくべきことの1つは、デフォルトでは express-session
ライブラリはセッションをWebサーバーのメモリに保存します。
セッションに入る前に、ユーザーをシリアル化する必要があります。セッションが終了したら、ユーザーを逆シリアル化します。
したがって、シリアライザーとデシリアライザーのauthフォルダーに新しいファイルを作成し、 session.serializer.ts
という名前を付けます。 。
この時点で、アプリケーションの形状 src
フォルダは次のようになります。
└───src
│ └───auth
│ │ └───auth.module.ts
│ │ └───auth.service.ts
│ │ └───authenticated.guard.ts
│ │ └───local.auth.guard.ts
│ │ └───local.strategy.ts
│ │ └───session.serializer.ts
│ └───users
│ │ └───users.controller.ts
│ │ └───users.model.ts
│ │ └───users.module.ts
│ │ └───users.service.ts
│ └───app.controller.ts
│ └───app.module.ts
│ └───app.service.ts
│ └───main.ts
session.serializer.ts
import { Injectable } from "@nestjs/common"
import { PassportSerializer } from "@nestjs/passport"
@Injectable()
export class SessionSerializer extends PassportSerializer {
serializeUser(user: any, done: (err: Error, user: any) => void): any {
done(null, user)
}
deserializeUser(
payload: any,
done: (err: Error, payload: string) => void
): any {
done(null, payload)
}
}
auth.module.ts
に戻ります ファイル、 SessionSerializer
を提供します 、 register
を追加します PassportModule
へのメソッド 。
auth.module.ts
import { Module } from "@nestjs/common"
import { PassportModule } from "@nestjs/passport"
import { UsersModule } from "src/users/users.module"
import { AuthService } from "./auth.service"
import { LocalStrategy } from "./local.strategy"
import { SessionSerializer } from "./session.serializer"
@Module({
imports: [UsersModule, PassportModule.register({ session: true })],
providers: [AuthService, LocalStrategy, SessionSerializer],
})
export class AuthModule {}
Add some codes within the LocalAuthGuard
in the local.auth.guard.ts
ファイル。
Call the login
method in super
and pass in the request to trigger the actual login by creating a session. If you want to use sessions, you must remember to trigger the super.login()
。
local.auth.guard.ts
import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {
async canActivate(context: ExecutionContext) {
const result = (await super.canActivate(context)) as boolean;
const request = context.switchToHttp().getRequest();
await super.logIn(request);
return result;
}
}
If you log in now, you will see the session ID stored in a cookie, which is just a key to the session store, and the cookie gets saved in the browser. The cookie is automatically attached to the rest of the request.
Now that the session is working, you can access the protected route; it will return the expected user’s details.
Logout Users
As mentioned earlier, once a user logs out, you destroy all sessions.
To log out a user, go to the users.controller.ts
file, add a logout route, and call the req.session.session()
方法。 You can return a message notifying that the user’s session has ended.
import {
Body,
Controller,
Get,
Post,
UseGuards,
Request,
} from '@nestjs/common';
import * as bcrypt from 'bcrypt';
import { AuthenticatedGuard } from 'src/auth/authenticated.guard';
import { LocalAuthGuard } from 'src/auth/local.auth.guard';
import { UsersService } from './users.service';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
//signup
@Post('/signup')
async addUser(
@Body('password') userPassword: string,
@Body('username') userName: string,
) {
const saltOrRounds = 10;
const hashedPassword = await bcrypt.hash(userPassword, saltOrRounds);
const result = await this.usersService.insertUser(
userName,
hashedPassword,
);
return {
msg: 'User successfully registered',
userId: result.id,
userName: result.username
};
}
//Post / Login
@UseGuards(LocalAuthGuard)
@Post('/login')
login(@Request() req): any {
return {User: req.user,
msg: 'User logged in'};
}
//Get / protected
@UseGuards(AuthenticatedGuard)
@Get('/protected')
getHello(@Request() req): string {
return req.user;
}
//Get / logout
@Get('/logout')
logout(@Request() req): any {
req.session.destroy();
return { msg: 'The user session has ended' }
}
}
So, once you log out, it returns a message notifying you that the user session has ended. The code for this tutorial is hosted here on my Github repository.
Test Your Application
You have successfully implemented user signup, authentication, and protected the route to enable authorized access only.
It’s time to test the application. If everything is in order, your server should be running. Else, restart your server with the following command:
npm run start:dev
Head over to your Postman. And let’s finally test our application.
Sign Up As a User
Log In As a User
Logged-in User’s Cookie ID
Request the Protected Route
User Logout
Alternatively, Implement User Authentication with LoginRadius
LoginRadius provides a variety of registration and authentication services to assist you in better connecting with your consumers.
On any web or mobile application, LoginRadius is the developer-friendly Identity Platform that delivers a complete set of APIs for authentication, identity verification, single sign-on, user management, and account protection capabilities like multi-factor authentication.
To implement LoginRadius in your NestJS application, follow this tutorial:NestJS User Authentication with LoginRadius API.
Conclusion
おめでとう! In this tutorial, you've learned how to implement session-based authentication in a NestJS application with the MongoDB database. You've created and authenticated a user and protected your routes from unauthorized access.
You can access the sample code used in this tutorial on GitHub.
注: Session storage is saved by default in 'MemoryStore,' which is not intended for production use. So, while no external datastore is required for development, once in production, a data store such as Redis or another is suggested for stability and performance. You can learn more about session storage here.