w3tecch / express-typescript-boilerplate

A delightful way to building a RESTful API with NodeJs & TypeScript by @w3tecch
MIT License
3.32k stars 903 forks source link

How to create login and logout feature? #269

Open saadabdurrazaq opened 4 months ago

saadabdurrazaq commented 4 months ago

Middleware: import jwt from 'jsonwebtoken'; import { ExpressMiddlewareInterface, Middleware } from 'routing-controllers';

import { Logger, LoggerInterface } from '../../decorators/Logger';

@Middleware({ type: 'before' }) export class JwtAuthMiddleware implements ExpressMiddlewareInterface {

constructor(
    @Logger(__filename) private log: LoggerInterface
) { }

public use(req: any, res: any, next: (err?: any) => any): void {
    const headers = req.headers as { authorization?: string } || req.headers['0'];
    const authHeader = headers.authorization;
    const authorization = req.header('authorization');

    console.log('authorization', authorization);
    // console.log('Headers:', req.headers);
    this.log.info('show token', headers);
    // console.log('res', res);
    if (!authHeader) {
        return res.status(401).send({ message: 'No token provided' });
    }

    const token = authHeader.split(' ')[1];
    if (!token) {
        return res.status(401).send({ message: 'Invalid token' });
    }

    jwt.verify(token, 'your-secret-key', (err, user) => {
        if (err) {
            return res.status(403).send({ message: 'Token is not valid' });
        }
        req.user = user;
        next();
    });
}

}

Controller: import { IsNotEmpty } from 'class-validator'; import { Body, JsonController, Post, Req, Res } from 'routing-controllers'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';

import { Logger, LoggerInterface } from '../../decorators/Logger'; import { AuthService } from '../services/AuthService'; // import { UserService } from '../services/UserService'; import { UserResponse } from './UserController';

class LoginBody { @IsNotEmpty() public username: string;

@IsNotEmpty()
public password: string;

}

@JsonController('/auth') @OpenAPI({ security: [{ basicAuth: [] }] }) export class AuthController {

constructor(
    // private userService: UserService,
    private authService: AuthService,
    @Logger(__filename) private log: LoggerInterface
) { }

@Post('/login')
@ResponseSchema(UserResponse)
public async login(@Body() body: LoginBody, @Res() res: any): Promise<any> {
    this.log.info(`Received login request with body: ${JSON.stringify(body)}`);

    const { user, token } = await this.authService.validateUser(body.username, body.password);

    if (!user) {
        return res.status(401).send({ message: 'Invalid username or password' });
    }

    res.status(200).send({
        status: 200,
        message: 'Login success',
        access_token: token,
        token_type: 'Bearer',
        user: {
            username: user.username,
        },
    });
}

@Post('/logout')
public async logout(@Req() req: any, @Res() res: any): Promise<void> {
    const token = req.headers.authorization.split(' ')[1];
    await this.authService.invalidateToken(token);
    res.status(200).send({ message: 'Logged out successfully' });
}

}

Service import bcrypt from 'bcrypt'; import jwt from 'jsonwebtoken'; import { Service } from 'typedi';

import { Logger, LoggerInterface } from '../../decorators/Logger'; import { User } from '../models/User'; import { UserService } from './UserService';

@Service() export class AuthService {

constructor(
    private userService: UserService,
    @Logger(__filename) private log: LoggerInterface
) { }

public async validateUser(username: string, password: string): Promise<{ user: User | null, token?: string }> {
    this.log.info(`Validating user: ${username}, ${password}`);
    const user = await this.userService.findByUsername(username);
    if (user && await bcrypt.compare(password, user.password)) {
        const token = this.createToken(user);
        return { user, token };
    }
    return { user: undefined };
}

public createToken(user: User): string {
    return jwt.sign({ id: user.id, username: user.username }, 'your-secret-key', { expiresIn: '1h' });
}

public async invalidateToken(token: string): Promise<void> {
    // Implement token invalidation logic here (e.g., adding the token to a blacklist)
}

}

Repository import knex, { Knex } from 'knex'; import { EntityRepository, Repository } from 'typeorm';

import knexConfig from '../../../src/knexfile'; import { User } from '../models/User';

const environment = process.env.NODE_ENV || 'development'; const DB: Knex = knex(knexConfig[environment]); @EntityRepository(User) export class UserRepository extends Repository { public async findOneByUsername(username: string): Promise<User | undefined> { const user = await DB('user') .where({ username }) .first();

    return user;
}

}

But I always failed