I have a problem, I'm trying to refactor my nodeJs server with nestJs.
The idea here is to check when starting the server if the 'Roles' collection already exists and contains entries, if not, we generate the roles in the mongoDB collection.
I have this folder stucture : mongoose schemas in a 'models' folder and an index.js that imports them:
./models/role.model.js
const mongoose = require("mongoose");
const Role = mongoose.model(
"Role",
new mongoose.Schema({
name: String
})
);
module.exports = Role;
./models/index.js
const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
const db = {};
db.mongoose = mongoose;
db.user = require("./user.model");
db.role = require("./role.model");
db.ROLES = ["user", "admin", "moderator"];
module.exports = db;
and then, in server.js, i import index.js as db then i define Role as db.role and i use an init function with 'Role.estimatedDocumentCount' to count the number of documents and do some logic:
./server.js
const db = require("./models");
const Role = db.role;
[...]
function init() {
Role.estimatedDocumentCount((err, count) => {
if (!err && count === 0) {
new Role({
name: "user"
}).save(err => {
if (err) {
console.log("error", err);
}
console.log("added 'user' to roles collection");
});
}
});
}
It's working super fine on my nodeJs server, but problems comes with the new nestJs server, basically, i think it's a type issue, and i'm not sure if my method is legit in a nestjs environement, assuming i'm an absolute begginer in it... This is what i tried:
./schemas/role.schema.ts
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import { Document } from 'mongoose';
export type RoleDocument = Role & Document;
#Schema()
export class Role {
#Prop()
name: string;
}
export const RoleSchema = SchemaFactory.createForClass(Role);
./schemas/index.ts
import mongoose from 'mongoose';
import { User } from './user.schema'
import { Role } from './role.schema'
mongoose.Promise = global.Promise;
export const db = {
mongoose: mongoose,
user: User,
role: Role,
ROLES: ["user", "admin", "moderator"]
};
./main.ts
[...]
import { db } from './auth/schemas';
const Role = db.role;
[...]
function init() {
Role.estimatedDocumentCount((err, count) => {
if (!err && count === 0) {
new Role({
name: "user"
}).save(err => {
if (err) {
console.log("error", err);
}
console.log("added 'user' to roles collection");
});
}
});
}
But it's not working, i get this error:
And when i check the type on the nodeJs server i have this:
But on nestJs server i get another type, this is why i think my issue comes from type, or maybe that method can't work the same way than on nodeJs for some reason...
EDIT: i also tried getting directly the roleSchema in my main.ts file, i'm getting a more interesting type, but it's not working either:
import { RoleSchema } from './auth/schemas/role.schema';
function init() {
RoleSchema.estimatedDocumentCount((err, count) => {
if (!err && count === 0) {
new Role({
name: "user"
}).save(err => {
if (err) {
console.log("error", err);
}
console.log("added 'user' to roles collection");
});
}
});
}
EDIT2: reply to gianfranco
auth.module.ts
import { Module } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
import { JwtModule } from '#nestjs/jwt';
import { MongooseModule } from '#nestjs/mongoose';
import { PassportModule } from '#nestjs/passport';
import { JwtStrategy } from './strategies/jwt.strategy';
import { LocalStrategy } from './strategies/local.strategy';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { UserSchema } from './schemas/user.schema';
import { RoleSchema } from './schemas/role.schema';
#Module({
imports: [
ConfigModule.forRoot(),
MongooseModule.forFeature([{ name: 'User', schema: UserSchema },{ name: 'Role', schema: RoleSchema}]),
PassportModule,
JwtModule.register({
secret: process.env.JWT_SECRET,
signOptions: { expiresIn: '60s' },
}),
],
providers: [AuthService, LocalStrategy, JwtStrategy],
controllers: [AuthController],
exports: [AuthService],
})
export class AuthModule {}
auth.controller.ts
import {
Body,
Controller,
Post,
Get,
Res,
Request,
UseGuards,
ValidationPipe,
} from '#nestjs/common';
import express, { Response } from 'express';
import { AuthService } from './auth.service';
import { AuthCredentialsDto } from './dto/auth-credentials.dto';
import { LocalAuthGuard } from './guards/auth.guard';
import { JwtAuthGuard } from './guards/jwt-auth.guard';
#Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}
#Post('signup')
async signUp(
#Body(ValidationPipe) authCredentialsDto: AuthCredentialsDto,
): Promise<void> {
return await this.authService.signUp(authCredentialsDto);
}
#UseGuards(LocalAuthGuard)
#Post('signin')
async signIn(#Request() req) {
return this.authService.signIn(req.user);
}
#UseGuards(JwtAuthGuard)
#Get('me')
getMe(#Request() req) {
return req.user;
}
}
Thank you very much for your help!
The key was to implement the logic in auth.service.ts instead of setting it in main.ts.Thanks for your help!
Related
i'm trying to register user into my mongodb. Under Postman everything works but my passwords aren't encrypted. I can see that later. But the main problem is, when i want to subscribe a new user, my user is not saved in my mongodb. There is my code :
my register.component.ts
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { AuthService } from 'src/app/service/auth.service';
#Component({
selector: 'app-register',
templateUrl: './register.component.html',
styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit {
signupForm : FormGroup
constructor(private fb:FormBuilder, private auth: AuthService) {
this.signupForm = this.fb.group({
'username':['',Validators.required],
'email':['',Validators.required],
'password':['',Validators.required]
})
}
ngOnInit(): void {
}
signup(){
this.auth.signup(this.signupForm.value).subscribe(res=>{
alert('User register Succesfull');
this.signupForm.reset
}, err=>{
alert(err)
})
}
}
authservice.component.ts
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
#Injectable({
providedIn: 'root'
})
export class AuthService {
constructor(private http: HttpClient) { }
signup(data:any):Observable<any>{
return this.http.post(environment.apiBaseUrl+'/auth/register', data)
}
}
environment.ts
export const environment = {
production: false,
apiBaseUrl: 'http://localhost:3000'
};
authroutes.js
const router = require('express').Router();
const User = require('../models/user');
const bcrypt = require('bcrypt');
const bodyParser = require('body-parser')
router.post('/register',(req, res) => {
bcrypt.hash(req.body.password, 10,(err, hash)=>{
if(err){
return res.json({success:false, message:"hash error"})
}else {
const user = new User({
username:req.body.username,
email:req.body.email,
password:req.body.password,
})
user.save()
.then((_)=>{
res.json({success:true, message:'Account has been created'})
})
.catch((err)=>{
if(err.code ===11000){
return res.json({success:false, message:'email already exist'})
}
res.json({success:false, message:'authentication failed'})
})
}});
})
router.post('/login',(req,res)=>{
res.json('login work')
})
module.exports = router
Hi awesome developers,
I'm trying to implement Authentication using passport-local and Nestjs with reference to https://progressivecoder.com/how-to-implement-nestjs-passport-authentication-using-local-strategy/.
I have implemented exactly same but Nestjs always returns 401 Unauthorized even with valid user. I can't seem to find what I am missing.
Code Structure
Authentication Module
User Module
Here's the code:
Authentication Module:
authentication.module.ts
import { Module } from '#nestjs/common';
import { PassportModule } from '#nestjs/passport';
import { UserModule } from 'src/user/user.module';
import { AuthenticationController } from './controllers/authentication.controller';
import { AuthenticationService } from './services/authentication.service';
import { LocalStrategy } from './strategies/local.strategy';
#Module({
imports:[UserModule, PassportModule],
controllers: [AuthenticationController],
providers: [AuthenticationService, LocalStrategy]
})
export class AuthenticationModule {}
authentication.controller.ts
import { Controller, Post, Request, UseGuards } from '#nestjs/common';
import { AuthGuard } from '#nestjs/passport';
import { UserService } from 'src/user/services/user.service';
#Controller('authentication')
export class AuthenticationController {
constructor(private userService: UserService){}
#UseGuards(AuthGuard('local'))
#Post('signin')
async signin(#Request() req){
return req.user;
}
}
authentication.service.ts
import { Injectable } from '#nestjs/common';
import { UserService } from 'src/user/services/user.service';
#Injectable()
export class AuthenticationService {
constructor(private userService: UserService) {}
async validateUser(email: string, password: string): Promise<any> {
const user = await this.userService.readUserByEmail(email);
if (user && user.password === password) {
const { password, ...result } = user;
return result;
}
return null;
}
}
local.strategy.ts
import { Injectable, UnauthorizedException } from "#nestjs/common";
import { PassportStrategy } from "#nestjs/passport";
import { Strategy } from "passport-local";
import { AuthenticationService } from "../services/authentication.service";
#Injectable()
export class LocalStrategy extends PassportStrategy(Strategy){
constructor(private authenticationService: AuthenticationService){
super();
}
async validate(username: string, password: string): Promise<any> {
const user = await this.authenticationService.validateUser(username, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
User Module:
user.module.ts
import { Module } from '#nestjs/common';
import { UserController } from './controllers/user.controller';
import { UserService } from './services/user.service';
#Module({
controllers: [UserController],
providers: [UserService],
exports: [UserService]
})
export class UserModule {}
user.controller.ts
Skipping user controller as it is irrelevent
user.service.ts
import { Injectable } from '#nestjs/common';
import { prisma } from 'src/main';
import { CreateUserDTO } from '../dto/create-user.dto';
import { UpdateUserDTO } from '../dto/update-user.dto';
#Injectable()
export class UserService {
private readonly users = [
{
id: "1",
name: "Ajitesh",
email: "ajitesh.k-s#lloydsbanking.com",
password: "secret"
}
]
//....other methods
async readUserByEmail(email: string){
return this.users.find(user => user.email === email);
}
}
Request:
{
"email": "ajitesh#example.com",
"password": "secret"
}
Thanks in advance.
passport-local expects req.body to be populated with username and password fields. If you plan to use something else for the username, like email, then you need to tell passport about that in your strategy using the usernameField option in super
import { Injectable, UnauthorizedException } from "#nestjs/common";
import { PassportStrategy } from "#nestjs/passport";
import { Strategy } from "passport-local";
import { AuthenticationService } from "../services/authentication.service";
#Injectable()
export class LocalStrategy extends PassportStrategy(Strategy){
constructor(private authenticationService: AuthenticationService){
super({
usernameField: 'email',
});
}
async validate(username: string, password: string): Promise<any> {
const user = await this.authenticationService.validateUser(username, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
I am trying to add actual value to the database using nestjs with jest test cases.
The main purpose of test cases is to actually check the database values written and retrived from the database. So I'm not mocking any save or find method as I want to make the actual connection to the database and retrive the records from there. So without mocking the save() and find() methods I'm not able get the test cases running. The compilation fails without it.
I am stuck with users.service.spec.ts file in which I want to use UserDocument and it's methods create(),findAll().
//user.entity.ts
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import { Document } from 'mongoose';
export type UserDocument = User & Document;
#Schema()
export class User {
#Prop({ unique: true })
username: string;
#Prop()
age: number;
#Prop()
password: string;
}
export const UserSchema = SchemaFactory.createForClass(User);
// User.module.ts
import { Module } from '#nestjs/common';
import { UsersService } from './users.service';
import { MongooseModule } from '#nestjs/mongoose'
import { User, UserSchema } from './schemas/user.schema'
import { UsersController } from './users.controller';
#Module({
imports: [MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])],
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService]
})
export class UsersModule {}
// users.controller.ts
import { Controller, Get, Post, Body, Patch, Param, Delete,UseGuards } from '#nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
#Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
#Post()
create(#Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
#Get()
findAll() {
return this.usersService.findAll();
}
}
//users.service.ts
import { Injectable } from '#nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { User, UserDocument } from './schemas/user.schema';
import { Model } from 'mongoose';
import { InjectModel } from '#nestjs/mongoose';
#Injectable()
export class UsersService {
constructor(#InjectModel(User.name) private userModel: Model<UserDocument>) {}
async create(createUserDto: CreateUserDto): Promise<User> {
const createdUser = new this.userModel(createUserDto);
return await createdUser.save();
}
async findAll(): Promise<User[]> {
return await this.userModel.find().exec();
}
}
//users.service.spec.ts
import { Test, TestingModule } from '#nestjs/testing'
import { UsersService } from './users.service'
import { AppConfigModule } from '../config/app-config.module'
import { AppConfigService } from '../config/app-config.service'
import { ConfigModule, ConfigService } from '#nestjs/config'
import { MongooseModule, getModelToken } from '#nestjs/mongoose'
import { PaginationQuery } from 'src/helpers/pagination/pagination-query.dto'
import { User, UserSchema, UserDocument } from './entities/user.entity'
describe('UsersService', () => {
let service: UsersService
let userDocument: UserDocument
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
isGlobal: true,
cache: true
}),
MongooseModule.forRootAsync({
imports: [AppConfigModule],
inject: [AppConfigService],
useFactory: (config: AppConfigService) => ({
uri: `mongodb://${config.TESTDATABASE_HOST}:${config.TESTDATABSE_PORT}`,
dbName: config.TESTDATABASE_NAME
})
})
],
providers: [
UsersService,
{
provide: getModelToken(User.name),
useValue: UserDocument // <----------- Not able to understand what will go here
}
]
}).compile()
service = module.get<UsersService>(UsersService)
userDocument = module.get<UserDocument>(UserDocument) // <---- As this is type here not accepting value
})
it('should be defined', () => {
expect(service).toBeDefined()
})
it('create test data', async () => {
expect(
await service.create({
name: 'demouser',
email: 'demo#gmail.com',
password: 'demo#123',
role: 'user'
})
).toEqual({
statusCode: 201,
message: 'User created successfully',
data: {
isActive: true,
role: 'user',
password:
'$2b$10$VjwR0Wjf6vTaRjQlsizB5OLY04NJOcRyC/pPLbTPTQnWTDIrBU.Sq',
email: 'demo#gmail.com',
name: 'demouser',
_id: expect.any(Number)
}
})
})
})
As I've highlighted in the spec file, UserDocument is the type defined in the users.entity.ts file, the value UserDocument is not accepted in the userDocument = module.get<UserDocument>(UserDocument) line.
I resolved my Problem Error.
Adding this import, it resolved my error.
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])
I can add the Actual value to the database
//users.service.spec.ts
import { Test, TestingModule } from '#nestjs/testing'
import { UsersService } from './users.service'
import { AppConfigModule } from '../config/app-config.module'
import { AppConfigService } from '../config/app-config.service'
import { ConfigModule } from '#nestjs/config'
import { MongooseModule, getModelToken } from '#nestjs/mongoose'
import { User, UserSchema } from './entities/user.entity'
import { isValidObjectId } from 'mongoose'
import { HttpException } from '#nestjs/common'
const testUser = {
name: 'demouser',
email: 'demo#gmail.com',
password: 'demo#123',
role: 'user'
}
describe('UsersService', () => {
let service: UsersService
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
isGlobal: true,
cache: true
}),
MongooseModule.forRootAsync({
imports: [AppConfigModule],
inject: [AppConfigService],
useFactory: (config: AppConfigService) => ({
uri: `mongodb://${config.TESTDATABASE_HOST}:${config.TESTDATABSE_PORT}`,
dbName: config.TESTDATABASE_NAME
})
}),
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])//-------import model here
],
providers: [UsersService]
}).compile()
service = module.get<UsersService>(UsersService)
})
it('should be defined', () => {
expect(service).toBeDefined()
})
})
userDocument = module.get<UserDocument>(UserDocument) must be
userDocument = module.get<UserDocument>(getModelToken(User.name))
since you're trying to get the stub that you have defined for getModelToken(User.name) provider
In useValue: UserDocument you must use some stub/mock object like this one: https://github.com/jmcdo29/testing-nestjs/blob/6c77b2a5001a48673c77c9659d985a083354579f/apps/sequelize-sample/src/cats/cats.service.spec.ts#L17-L20 since you're trying to not use the real model.
I am looking for a response from userService .. but it is returning a null value..
I have consoled the data in userSerice it's showing here.
may be my controller is giving a response before receiving value from userService.
how can I solve this?
usercontroller
import {
Controller,
Post,
Body,
Get,
Param,
Patch,
Delete,
} from '#nestjs/common';
import { UserService } from './user.service';
#Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}
#Post('/login')
async login(
#Body('email') userEmail,
#Body('password') userPassword
) {
const token = await this.userService.Login(userEmail, userPassword)
console.log(token, 'token')
return token;
}
}
Userservice :
import { Injectable, NotFoundException, UnauthorizedException } from '#nestjs/common';
import { InjectModel } from '#nestjs/mongoose';
import * as jwt from 'jsonwebtoken';
import { Model } from 'mongoose';
import { User } from './user.model';
import * as bcrypt from 'bcrypt'
import { resolve } from 'dns';
#Injectable()
export class UserService {
constructor(
#InjectModel('User') private readonly userModel: Model<User>,
) { }
async Login(email: string, password: string) {
const user = await this.userModel.findOne({ email });
if (!user) {
console.log("User does exist on the database.");
throw new UnauthorizedException();
}
bcrypt.compare(password, user.password, function (err, result) {
if (!result) {
throw new UnauthorizedException();
}
const authJwtToken = jwt.sign({ name: user.name, email: user.email, role: user.role }, "testSecreate");
const response = { name: user.name, email: user.email, role: user.role, token: authJwtToken }
console.log(response)
return response;
});
}
}
Tushar has n all right answer, but it's still mixing promises and callbacks, which I think should be avoided if possible. You can use this instead to not have any callbacks and just use async/await and promises throughout the method
import {
Injectable,
NotFoundException,
UnauthorizedException,
} from "#nestjs/common";
import { InjectModel } from "#nestjs/mongoose";
import * as jwt from "jsonwebtoken";
import { Model } from "mongoose";
import { User } from "./user.model";
import * as bcrypt from "bcrypt";
#Injectable()
export class UserService {
constructor(#InjectModel("User") private readonly userModel: Model<User>) {}
async Login(email: string, password: string) {
const user = await this.userModel.findOne({ email });
if (!user) {
console.log("User does exist on the database.");
throw new UnauthorizedException();
}
const result = await bcrypt.compare(password, user.password);
if (!result) {
throw new UnauthorizedException();
}
const authJwtToken = await jwt.sign(
{ name: user.name, email: user.email, role: user.role },
"testSecreate"
);
const response = {
name: user.name,
email: user.email,
role: user.role,
token: authJwtToken,
};
console.log(response);
return response;
}
}
Now, the console.log(response) will fire before console.log('token', token) in your controller method, and the flow will look synchronous while actually being asynchronous in nature.
import { Injectable, NotFoundException, UnauthorizedException } from '#nestjs/common';
import { InjectModel } from '#nestjs/mongoose';
import * as jwt from 'jsonwebtoken';
import { Model } from 'mongoose';
import { User } from './user.model';
import * as bcrypt from 'bcrypt'
import { resolve } from 'dns';
#Injectable()
export class UserService {
constructor(
#InjectModel('User') private readonly userModel: Model<User>,
) { }
async Login(email: string, password: string) {
const user = await this.userModel.findOne({ email });
if (!user) {
console.log("User does exist on the database.");
throw new UnauthorizedException();
}
await bcrypt.compare(password, user.password, function (err, result) {
if (!result) {
throw new UnauthorizedException();
}
const authJwtToken = await jwt.sign({ name: user.name, email: user.email, role: user.role }, "testSecreate");
const response = { name: user.name, email: user.email, role: user.role, token: authJwtToken }
console.log(response)
return response;
});
}
}
NOTE
The asynchronous method will be executed in parallel with your main program, so your console.log will be done before the callback function inside bcrypt.compare. You will see always 'oops, it was false'.
I have developed a MEAN stack application the backend works fine and the frontend but i want to go much further by implementing role based redirection after login i have 5 roles thats is admin, teacher, nurse, sportsman, and janitor i want each of them to access his/her own page after successful login and should use the same login page
I have tried some several solution online but so far nothing has come to help me this is the fifth time am asking this question without a real solution, please help me solve this its my final year project
here is my routes.ts
```import { Routes } from '#angular/router';
import { UserComponent } from './user/user.component';
import { SignUpComponent } from './user/sign-up/sign-up.component';
import { SignInComponent } from './user/sign-in/sign-in.component';
import { AuthGuard } from './auth/auth.guard';
import { AdminComponent } from './user/admin/admin.component';
import { AdminSportComponent} from './user/admin-sport/admin-sport.component'
import { AdminSuaHubComponent } from './user/admin-sua-hub/admin-sua-hub.component';
import { AdminCictComponent } from './user/admin-cict/admin-cict.component';
import { ViewadminsectionsComponent } from './user/viewadminsections/viewadminsections.component'
import { from } from 'rxjs';
export const appRoutes: Routes = [
{
path: 'user',component:SignInComponent
},
{
path:'signup', component:SignUpComponent, canActivate:[AuthGuard]
},
{
path:'admin', component:AdminComponent,
canActivate:[AuthGuard],
data:{
roles:['admin']
}
},
{
path:'viewsectionadmins',
component:ViewadminsectionsComponent,
canActivate:[AuthGuard],
data:{
roles:['admin']
}
},
{
path:'admincict',
component:AdminCictComponent,
canActivate:[AuthGuard],
data:{
roles:['admincict']
}
},
{
path:'adminsport',
component:AdminSportComponent,
canActivate:[AuthGuard],
data:{
roles:['adminsport']
}
},
{
path:'adminsuahub',
component: AdminSuaHubComponent,
canActivate:[AuthGuard],
data:{
roles:['adminsuahub']
}
},
{
path: '',component:SignInComponent
},
{
path:'',redirectTo:'/user', pathMatch:'full'
}
];```
Here is my app.module.ts
```import { BrowserModule } from '#angular/platform-browser';
import { NgModule, forwardRef } from '#angular/core';
import{ FormsModule } from '#angular/forms';
import{ RouterModule } from '#angular/router'
import { HttpClientModule, HTTP_INTERCEPTORS } from '#angular/common/http'
//components
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { UserComponent } from './user/user.component';
import { SignUpComponent } from './user/sign-up/sign-up.component';
import { from } from 'rxjs';
//routes
import { appRoutes } from './routes';
import { SignInComponent } from './user/sign-in/sign-in.component';
import { UserService } from './shared/user.service';
import { AuthGuard } from './auth/auth.guard';
import { AuthInterceptor } from './auth/auth.interceptor';
import { AdminComponent } from './user/admin/admin.component';
import { AdminSportComponent } from './user/admin-sport/admin-sport.component';
import { AdminSuaHubComponent } from './user/admin-sua-hub/admin-sua-hub.component';
import { AdminCictComponent } from './user/admin-cict/admin-cict.component';
import { ViewadminsectionsComponent } from './user/viewadminsections/viewadminsections.component';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
#NgModule({
declarations: [
AppComponent,
UserComponent,
SignUpComponent,
SignInComponent,
AdminComponent,
AdminSportComponent,
AdminSuaHubComponent,
AdminCictComponent,
ViewadminsectionsComponent
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
RouterModule.forRoot(appRoutes),
HttpClientModule,
BrowserAnimationsModule
],
providers: [{
provide:forwardRef(() => {HTTP_INTERCEPTORS}),
useClass:forwardRef(() =>{ AuthInterceptor}),
multi:true
},AuthGuard,UserService],
bootstrap: [AppComponent]
})
export class AppModule { }```
Here is my User.js
``` const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const userSchema = new mongoose.Schema({
fullname:{
type:String,
required:'Full name cant be empty',
min:6,
max:255
},
email:{
type:String,
required:'Email cant be Empty',
max:255,
unique:true
},
University:{
type:String,
default:"Sokoine University of Agriculture"
},
College:{
type:String,
required:'College cant be Empty'
},
Department:{
type:String,
required:'department cant be empty'
},
password:{
type:String,
required:'pasword cant be empty',
max :1024,
minlength: [6,'password must be atlest 6 character long']
},
admintype:{
type:String,
enum :['HOD','CICT','Sports','SUASAB','Admin']
},
date:{
type:Date,
default:Date.now
},
saltSecret:String
});
//custom validation
userSchema.path('email').validate((val) => {
emailRegex = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
return emailRegex.test(val);
},'Invalid E-mail. ');
//events
userSchema.pre('save', function(next) {
bcrypt.genSalt(10, (err, salt)=> {
bcrypt.hash(this.password, salt, (err, hash) => {
this.password = hash;
this.saltSecret = salt;
next();
});
});
});
//methods
userSchema.methods.verifyPassword = function(password){
return bcrypt.compareSync(password, this.password);
};
userSchema.methods.generateJwt = function() {
return jwt.sign({ _id:this._id},
process.env.JWT_SECRET,
{expiresIn:process.env.JWT_EXP});
}
module.exports = mongoose.model('User',userSchema)```
Here is my user.controller.js
```const mongoose = require('mongoose');
const User = mongoose.model('User');
const passport = require('passport');
const _ = require('lodash');
module.exports.register = (req,res, next) => {
const user = new User();
user.fullname = req.body.fullname;
user.email = req.body.email;
user.College = req.body.College;
user.Department = req.body.Department;
user.password = req.body.password;
user.admintype = req.body.admintype;
user.save((err, doc) => {
if(!err) { res.send(doc)}
else
{
if(err.code == 11000)
res.status(422).send(['Duplicate email Address Found.'])
else
return next(err);
}
})
}
module.exports.authenticate = (req, res, next ) => {
//calll for passport authentication
passport.authenticate('local', (err, user, info) => {
//error form paasport middleware
if(err) return res.status(400).json(err);
//registered user
else if (user) return res.status(200).json({ "token":user.generateJwt() });
//unknown user or wrong password
else return res.status(404).json(info);
})(req, res);
}
module.exports.userProfile = (req, res, next) =>{
User.findOne({ _id:req._id},
(err,user) =>{
if(!user)
return res.status(404).json({ status: false, message : 'User Record not Found. '});
else
return res.status(200).json({ status:true , user : _.pick(user, ['fullname','email','university','College','Department','admintype'])});
} );
//get admins
}```
here is my auth.guard.ts
```import { Injectable } from '#angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '#angular/router';
import { Observable } from 'rxjs';
import { UserService } from '../shared/user.service';
import { Router } from '#angular/router';
#Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private userService:UserService,private router: Router) {}
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): boolean {
if(!this.userService.isloggedIn()){
this.router.navigateByUrl('/user');
this.userService.deleteToken();
return false;
}
return true;
}
}```
here is my user.model.ts
```export class User {
fullname:string;
email:string;
university:string;
College:string;
Department:string;
password:string;
admintype:string;
}```
here is my user.service.ts
```import { Injectable } from '#angular/core';
import { User } from './user.model';
import{ HttpClient, HttpHeaders } from '#angular/common/http';
import{ environment } from '../../environments/environment';
import { from } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class UserService {
selectedUser: User = {
fullname:'',
email:'',
university:'',
College:'',
Department:'',
password:'',
admintype:''
}
noAuthHeader = { headers: new HttpHeaders({ 'NoAuth': 'True'})};
constructor(private http: HttpClient) { }
//http methods
postUser(user:User)
{
return this.http.post(environment.apiBaseUrl+ '/register' ,user)
}
login(authCredentials)
{
return this.http.post(environment.apiBaseUrl+ '/authenticate',authCredentials,this.noAuthHeader);
}
getUserProfile()
{
return this.http.get(environment.apiBaseUrl + '/userProfile');
}
//helper methods
setToken(token:string)
{
localStorage.setItem('token',token);
}
getToken()
{
localStorage.getItem('token');
}
deleteToken()
{
localStorage.removeItem('token');
}
getUserPayload(){
var token = localStorage.getItem('token');
if(token)
{
var userPayload = atob(token.split('.')[1]);
return JSON.parse(userPayload);
}
else
return null;
}
isloggedIn()
{
var userPayload = this.getUserPayload();
if (userPayload)
{
return userPayload.exp > Date.now() / 1000;
}
}
getUserRole()
{
}
}```
here is my sign-in.component.ts
```import { Component, OnInit } from '#angular/core';
import { NgForm } from '#angular/forms';
import { UserService } from 'src/app/shared/user.service';
import { Router } from '#angular/router';
#Component({
selector: 'app-sign-in',
templateUrl: './sign-in.component.html',
styleUrls: ['./sign-in.component.css']
})
export class SignInComponent implements OnInit {
constructor( private userService:UserService, private router:Router) { }
model = {
email:'',
password:''
};
emailRegex = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
serverErrorMessages : string;
ngOnInit() {
if(this.userService.isloggedIn())
{
this.router.navigateByUrl('/admin');
}
}
onSubmit(form :NgForm)
{
this.userService.login(form.value).subscribe(
res =>{
this.userService.setToken(res['token']);
this.router.navigateByUrl('/admin');
},
err =>{
this.serverErrorMessages = err.message;
});
}
}```
so far i have no errors but am in need of implementing role based redirection
i would suggest you to create a field in mongodb schema as admin with boolean value as true or false depending upon the user, add different fields if any other roles exists,
on login check the user admin status and accordingly inform other parts of your application by subscribing the observables in your service file
call the method in your service file on OnInit method of the component and check whether the user is admin or not if not admin hide the things you don't want to show
and to stop a user from going to routes that are meant for admin add a guard which will navigate the user to homepage if he is not an admin by checking the same observable that you created in the service file.
i hope it helps