LoginSignup
0
0

Nest.jsとSession認証を使ったロール別の認証を実装しよう!

Posted at

概要

Nest.jsのguardを使ってセッションで認証したユーザの

  • 管理者
  • 一般

の2種類のロール別の認証を実装する方法について解説します

前提

  • Nest.jsのアプリケーションを作成済み
  • セッション認証を実装済み

セッション認証の実装方法について知りたい方は以下の記事を参考にしてください

Modelの設定

UserにRoleという権限を付与するようschema定義します

schema.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id              Int             @id @default(autoincrement())
  name            String          @db.VarChar(255)
  employee_number String          @unique @db.VarChar(8)
  email           String          @unique
  password        String          @db.VarChar(255)
  role            Role            @default(ADMIN)
  is_active       Boolean         @default(true)
  is_verified     Boolean         @default(false)
  is_superuser    Boolean         @default(false)
  createdAt       DateTime        @default(now())
  updatedAt       DateTime        @updatedAt
}

enum Role {
  ADMIN
  GENERAL
}

guardの実装

管理者のみ許可するAdminAuthGuardとログインユーザを許可するUserAuthGuardの2種類のguardを実装します

管理者のみ許可する場合

request内のsessionオブジェクトからユーザが存在している上に管理者のロールを持っているかどうかを確認します
管理者ユーザであることが確認できたらユーザのオブジェクトを返します

admin-auth.guard.ts
import {
  Injectable,
  CanActivate,
  ExecutionContext,
  UnauthorizedException,
  ForbiddenException,
} from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AdminAuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    const user = request.session && request.session.user;
    if (!user) {
      throw new UnauthorizedException(
        'ログインしていないため、この操作を実行する権限がありません',
      );
    }
    if (user.role !== 'ADMIN') {
      throw new ForbiddenException(
        '管理者ユーザではないため、この操作を実行する権限がありません',
      );
    }
    return user;
  }
}

ログインユーザを許可する場合

request内のsessionオブジェクトからユーザが存在しているかどうかを確認します
管理者ユーザであることが確認できたらユーザのオブジェクトを返します

user-auth.guard.ts
import {
  Injectable,
  CanActivate,
  ExecutionContext,
  UnauthorizedException,
} from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class UserAuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    const user = request.session && request.session.user;
    if (!user) {
      throw new UnauthorizedException(
        'ログインしていないため、この操作を実行する権限がありません',
      );
    }
    return user;
  }
}

実際に検証してみよう!

ユーザを一覧表示する簡単なAPIを使って検証します
まず、ログインせずにAPIを実行しようとするとエラーメッセージを返すことが確認できます

スクリーンショット 2024-04-17 16.33.34.png

UseGuardsとAdminAuthGuardを使って管理者ユーザのみ使用できるか確認します

user.controller.ts
import {
  Get,
  Controller,
  HttpStatus,
  UseGuards,
} from '@nestjs/common';
import { ApiTags, ApiResponse } from '@nestjs/swagger';
import { UserService } from './user.service';
import { AdminAuthGuard } from 'src/guards/admin-auth.guard';

@ApiTags('users')
@Controller('admin/users')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get()
  @UseGuards(AdminAuthGuard)
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'ユーザを一覧表示',
    content: {
      'application/json': {
        example: [
          {
            id: 1,
            name: 'テストユーザゼロイチ',
            employee_number: '00000001',
            email: 'test_user_01@example.com',
            is_active: true,
            is_verified: true,
            createdAt: '2024-03-08T00:33:27.790Z',
            updatedAt: '2024-03-08T00:33:27.790Z',
          },
        ],
      },
    },
  })
  findAll() {
    return this.userService.findAll();
  }
}

管理者ユーザでログインします
スクリーンショット 2024-04-17 16.34.44.png

以下のように管理者でログインしたらユーザが一覧表示されることを確認できました
スクリーンショット 2024-04-17 16.35.05.png

一般ユーザでログインします
スクリーンショット 2024-04-17 16.36.18.png

権限エラーを返すことが確認できました
スクリーンショット 2024-04-17 16.36.40.png

UseGuardsとUserAuthGuardを使って管理者ユーザのみ使用できるか確認します

user.controller.ts
import {
  Get,
  Controller,
  HttpStatus,
  UseGuards,
} from '@nestjs/common';
import { ApiTags, ApiResponse } from '@nestjs/swagger';
import { UserService } from './user.service';
import { UserAuthGuard } from 'src/guards/user-auth.guard';

@ApiTags('users')
@Controller('admin/users')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get()
  @UseGuards(UserAuthGuard)
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'ユーザを一覧表示',
    content: {
      'application/json': {
        example: [
          {
            id: 1,
            name: 'テストユーザゼロイチ',
            employee_number: '00000001',
            email: 'test_user_01@example.com',
            is_active: true,
            is_verified: true,
            createdAt: '2024-03-08T00:33:27.790Z',
            updatedAt: '2024-03-08T00:33:27.790Z',
          },
        ],
      },
    },
  })
  findAll() {
    return this.userService.findAll();
  }
}

一般ユーザでログインします
スクリーンショット 2024-04-17 16.36.18.png

以下のように一般ユーザでログインしたらユーザが一覧表示されることを確認できました
スクリーンショット 2024-04-17 16.35.05.png

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0