I have a user auth development on in nest and I have hit another road block
So the user stores fine and has everything needed with bcrypt working great
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import { Document } from 'mongoose';
export type UserDocument = User & Document;
#Schema()
export class User {
#Prop()
firstname: string;
#Prop()
lastname: string;
#Prop()
jobtitle: string;
#Prop()
startdate: string
#Prop()
password: string;
#Prop()
username: string;
}
export const UserSchema = SchemaFactory.createForClass(User);
I have a auth service and auth controller
import { Injectable, NotAcceptableException } from '#nestjs/common';
import { UsersService } from '../users/users.service';
import * as bcrypt from 'bcrypt';
import { JwtService } from '#nestjs/jwt';
#Injectable()
export class AuthService {
constructor(private readonly usersService: UsersService, private jwtService: JwtService) { }
async validateUser(username: string, password: string): Promise<any> {
const user = await this.usersService.getUser({ username });
if (!user) return null;
const passwordValid = await bcrypt.compare(password, user.password)
if (!user) {
throw new NotAcceptableException('could not find the user');
}
if (user && passwordValid) {
return user;
}
return null;
}
async login(user: any) {
const payload = { username: user.username, sub: user._id };
return {
access_token: this.jwtService.sign(payload),
};
}
}
import { Controller, Request, Post, UseGuards } from '#nestjs/common';
import { AuthService } from './auth.service';
import { AuthGuard } from '#nestjs/passport';
#Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}
// #UseGuards(AuthGuard('local'))
#Post('auth/login')
async login(#Request() req) {
return this.authService.login(req.user);
}
}
When I go to login function it gives me the error:
ERROR [ExceptionsHandler] Cannot read properties of undefined (reading 'username')
For context :
import { Injectable } from '#nestjs/common';
import { InjectModel } from '#nestjs/mongoose';
import { Model } from 'mongoose';
import { User } from 'users/interfaces/users.interface';
#Injectable()
export class UsersService {
constructor(#InjectModel('Users') private readonly userModel: Model<User>) {}
//Get all users
async getUsers(): Promise<User[]> {
const users = await this.userModel.find().exec();
return users
}
//Get single user
async getUser(query: object ): Promise<User> {
return this.userModel.findOne(query);
}
async addUser(
firstname: string,
lastname: string,
jobtitle: string,
startdate: string,
password: string,
username: string): Promise<User> {
return this.userModel.create({
firstname,
lastname,
jobtitle,
startdate,
password,
username
});
}
}
and the local Strategy
import { Strategy } from 'passport-local';
import { PassportStrategy } from '#nestjs/passport';
import { Injectable, UnauthorizedException } from '#nestjs/common';
import { AuthService } from './auth.service';
#Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super();
}
async validate(username: string, password: string): Promise<any> {
const user = await this.authService.validateUser(username, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
Related
I have a user collection in the database and i want to retrive a user with specific username
I have written this method but this is returning all users
findByUsername(username: string) {
return this.userModel.find({
'username' : username})
}
Why is this query not working
Controller
#Get('find/:username')
getUserById(#Param("username") username : string) : any {
console.log(username);
return this.usersService.findByUsername(username);
}
This is my user entity
import { Schema, SchemaFactory } from "#nestjs/mongoose";
import { ApiProperty } from "#nestjs/swagger";
export type UserDocument = User & Document;
#Schema()
export class User {
#ApiProperty()
id: string;
#ApiProperty()
username: string;
#ApiProperty()
email : string
#ApiProperty()
password: string;
}
export const UserSchema = SchemaFactory.createForClass(User);
This is the service
import { Injectable } from "#nestjs/common";
import { InjectModel } from "#nestjs/mongoose";
import { Model } from "mongoose";
import { use } from "passport";
import {User,UserDocument} from '../users/entities/user.entity'
// This should be a real class/interface representing a user entity
#Injectable()
export class UsersService {
constructor(
#InjectModel(User.name) private readonly userModel : Model<User> )
{}
findById(userId: string) {
}
findByUsername(username: string) {
return this.userModel.find({"username": username}).exec();
}
Try this:
findByUsername(username: string) {
return this.userModel.find({username: username}).exec();
}
or simplified version:
findByUsername(username: string) {
return this.userModel.find({username}).exec();
}
Briefly, the cause is the 'username' field typed with quotes and missing .exec() method at the end of the chain.
Also, schema should be prepared for Mongoose by decorating fields with the #Prop() decorator:
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
#Schema()
export class User {
#ApiProperty()
#Prop()
id: string;
#ApiProperty()
#Prop()
username: string;
#ApiProperty()
#Prop()
email : string
#ApiProperty()
#Prop()
password: string;
}
export const UserSchema = SchemaFactory.createForClass(User);
You can use the findOne method in Mongoose:
findByUsername(username: string) {
return this.userModel.findOne({ username })
}
I'm trying to implement JWT into my project. I've followed the steps as outline in https://www.npmjs.com/package/#nestjs/jwt#usage
auth.module.ts
import { Module } from '#nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { TypeOrmModule } from '#nestjs/typeorm';
import { JwtModule } from '#nestjs/jwt';
import { AuthRepository } from './auth.repository';
#Module({
imports: [
JwtModule.register({ secret: process.env.JWT_SECRET || 'ABCDE12345' }),
TypeOrmModule.forFeature([AuthRepository]),
],
exports: [TypeOrmModule, AuthService],
providers: [AuthService],
controllers: [AuthController],
})
export class AuthModule {}
auth.service.ts
import { Injectable, NotFoundException, UnauthorizedException } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { AuthEntity } from './auth.entity';
import { LoginDTO } from './dto/login.dto';
import * as bcrypt from 'bcrypt';
import { JwtService } from '#nestjs/jwt';
import crypto from 'crypto';
import { AuthRepository } from './auth.repository';
// export interface FindWhereData {
// readonly email: string;
// readonly password: string;
// }
export interface LoginResponse {
readonly token: string;
readonly refresh_token: string;
}
#Injectable()
export class AuthService {
constructor(
#InjectRepository(AuthRepository)
private authRepository: AuthRepository,
private jwtService: JwtService
) {}
/**
* Login user service
*
* #param doc
*/
public async login(doc: LoginDTO): Promise<LoginResponse> {
// verify user email
const user = await this.authRepository.findOne({ email: doc.email });
if (!user) {
throw new NotFoundException('Could not find user');
}
// verify password
const passwordsMatch = await this.passwordsAreEqual(doc.password, user.password);
if (!passwordsMatch) {
throw new UnauthorizedException('Incorrect login credentials');
}
// generate JWT
const token = await this.jwtService.signAsync({ id: user.id });
// create the refresh token
const refreshToken = crypto.randomBytes(256).toString('hex');
// store the refresh token
return {
token: token,
refresh_token: refreshToken,
};
}
/**
* Generate a hashed password
*
* #param password
*/
public async hashPassword(password: string): Promise<string> {
const salt = await bcrypt.genSalt();
return await bcrypt.hash(password, salt);
}
/**
* Compare password against an encrypted string
*
* #param password
* #param encryptedPassword
*/
public async passwordsAreEqual(password: string, encryptedPassword: string): Promise<boolean> {
return await bcrypt.compare(password, encryptedPassword);
}
/**
* Find a record by column attribute and value
*
* #param queryObject
*
*/
public async findWhere(queryObject): Promise<AuthEntity> {
const authEntity = await this.authRepository.findOne({ where: queryObject });
if (!authEntity) {
return null;
}
return authEntity;
}
public async findOne(id: string): Promise<AuthEntity> {
return this.authRepository.findOne(id);
}
}
auth.repository.ts
import { EntityRepository, Repository } from "typeorm";
import { AuthEntity } from "./auth.entity";
#EntityRepository(AuthEntity)
export class AuthRepository extends Repository<AuthEntity> {}
app.module.ts
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { RouterModule } from 'nest-router';
import { routes } from './routes';
import { ConfigModule } from '#nestjs/config';
import configuration from './config/configuration';
import { TypeOrmModule } from '#nestjs/typeorm';
import { Connection } from 'typeorm';
import { AuthModule } from './auth/auth.module';
#Module({
imports: [
RouterModule.forRoutes(routes),
ConfigModule.forRoot({
load: [configuration],
}),
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.POSTGRES_HOST || 'localhost',
port: 5432,
username: process.env.POSTGRES_USERNAME || 'postgres',
password: process.env.POSTGRES_PASSWORD || 'password',
database: process.env.POSTGRES_DATABASE || 'service-auth',
autoLoadEntities: true,
synchronize: true,
}),
AuthModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {
constructor(private readonly connection: Connection) {
console.log('connection status: ' + connection.isConnected);
}
}
auth.service.spec.ts
import { JwtModule, JwtService } from '#nestjs/jwt';
import { Test, TestingModule } from '#nestjs/testing';
import { AuthEntity } from './auth.entity';
import { AuthRepository } from './auth.repository';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let authService: AuthService;
let authRepository: AuthRepository;
const mockAuthRepository = () => ({
login: jest.fn(),
});
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
AuthService,
// JwtModule,
{
provide: getRepositoryToken(AuthRepository),
useFactory: mockAuthRepository,
},
{
provide: JwtService,
useValue: {
signAsync: jest.fn(() => 'token'),
}
}
]
}).compile();
authService = await module.get<AuthService>(AuthService);
authRepository = await module.get<AuthRepository>(AuthRepository);
});
/**
* Test that the service is defined
*/
it('should be defined', () => {
expect(authService).toBeDefined();
});
});
When I run npm run test I get the following error message:
FAIL src/auth/auth.service.spec.ts
● AuthService › should be defined
Nest can't resolve dependencies of the AuthService (AuthRepository, ?). Please make sure that the argument JwtService at index [1] is available in the RootTestModule context.
Potential solutions:
- If JwtService is a provider, is it part of the current RootTestModule?
- If JwtService is exported from a separate #Module, is that module imported within RootTestModule?
#Module({
imports: [ /* the Module containing JwtService */ ]
})
I know the error is probably pretty clear to seasoned Node/Nest developer but I cannot figure out what the RootTestModule is and how to get JwtModule imported.
I believe I have followed the setup correctly but adding this JwtModule to the AuthService is causing the service to be undefined in my unit tests.
Repo
https://github.com/marcuschristiansen/nestjs-auth
You should be adding a custom provider for the JwtService so that you can mock it. A custom provider could look like
{
provide: JwtService,
useValue: {
signAsync: jest.fn(() => 'token'),
}
}
To tell Nest to inject an object that has a signAsync() method that when called returns the string 'token' so that it will always be the same in your tests.
This object goes in the providers array, just like the AuthRepository mock
I'm following a Angular and .net course.
I'm trying to set the photourl to a method.
I'm getting the following error\
Argument of type date is not assignable to parameter of type string
The error is on the following line.
this.changeMemberPhoto(this.currentUser.photoUrl);
The argument is the issue
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { BehaviorSubject } from 'rxjs';
import { map } from 'rxjs/operators';
import { JwtHelperService} from '#auth0/angular-jwt';
import { environment } from 'src/environments/environment';
import { User } from '../_models/user';
#Injectable({
providedIn: 'root'
})
export class AuthService {
baseUrl = environment.apiUrl + 'auth/';
jwtHelpter = new JwtHelperService();
decodedToken: any;
currentUser: User;
photoUrl = new BehaviorSubject<string>('../../assets/user.png');
currentPhotoUrl = this.photoUrl.asObservable();
constructor(private http: HttpClient) {}
changeMemberPhoto(photoUrl: string) {
this.photoUrl.next(photoUrl);
}
login(model: any) {
return this.http.post(this.baseUrl + 'login', model).pipe(
map((response: any) => {
const user = response;
if (user) {
localStorage.setItem('token', user.token);
localStorage.setItem('user', JSON.stringify(user.user));
this.decodedToken = this.jwtHelpter.decodeToken(user.token);
this.currentUser = user.user;
this.changeMemberPhoto(this.currentUser.photoUrl);
}
})
);
}
register(model: any) {
return this.http.post(this.baseUrl + 'register', model);
}
loggedIn() {
const token = localStorage.getItem('token');
return !this.jwtHelpter.isTokenExpired(token);
}
}
The code is identical to the instructors, so I don't know what the issue is.
I've included the User class as requested
import { Photo } from './photo';
export interface User {
id: number;
username: string;
knownAs: string;
age: number;
gender: string;
created: Date;
lastActive: Date;
photoUrl: Date;
city: string;
country: string;
interests?: string;
introduction?: string;
lookingFor?: string;
photos?: Photo[];
}
photoUrl is a date in your class and is a string in your changeMemberPhoto method
In User class, change it to :
photoUrl: string;
I need help with storing registration data in Firebase.
I want to have email name of current logged user stored in Firebase database.
Pls help me. I am writhing code where user can write on profile of onother user.
//auth service
import { Injectable } from "#angular/core";
import { AngularFireAuth } from "#angular/fire/auth";
import { Observable } from "rxjs";
import "rxjs/add/operator/map";
#Injectable()
export class AuthService {
constructor(private afAuth: AngularFireAuth) {}
login(email: string, password: string) {
return new Promise((resolove, reject) => {
this.afAuth.auth
.signInWithEmailAndPassword(email, password)
.then(userData => resolove(userData), err => reject(err));
});
}
getAuth() {
return this.afAuth.authState.map(auth => auth);
}
logout() {
this.afAuth.auth.signOut();
}
register(email: string, password: string) {
return new Promise((resolove, reject) => {
this.afAuth.auth
.createUserWithEmailAndPassword(email, password)
.then(userData => resolove(userData), err => reject(err));
});
}
}
**register component**
import { Component, OnInit } from "#angular/core";
import { AuthService } from "../../service/auth.service";
import { Router } from "#angular/router";
#Component({
selector: "app-register",
templateUrl: "./register.component.html",
styleUrls: ["./register.component.css"]
})
export class RegisterComponent implements OnInit {
email: string;
password: string;
constructor(private authService: AuthService, private router: Router) {}
ngOnInit() {}
onSubmit() {
this.authService
.register(this.email, this.password)
.then(res => {
this.router.navigate(["/"]);
})
.catch(err => console.log(err.message));
}
}
I am working on an angular-application with authentication. The application has a dashboard which shows the username of the logged in user. The problem is, that when you login with a new user, it still shows the username of the last logged in user.
So it doesen't update the username (observable) when a user logs in.
I think the problem is that I get the username in the ngOnInit methode.
How can I update all user related data?
header.component.ts (username should be shown)
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../../../core/auth.service';
import { Apollo } from 'apollo-angular';
import gpl from 'graphql-tag';
const registeredUser = gpl`
query registeredUser {
registeredUser {
name
}
}
`
#Component({
selector: 'dashboard-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
private user$;
constructor(
private authService : AuthService,
private apollo : Apollo) { }
ngOnInit() {
this.user$ = this.apollo.watchQuery<any>({
query: registeredUser
})
.valueChanges;
}
logout() {
this.authService.logout();
}
}
AuthService
import { Injectable } from '#angular/core';
import { JwtHelper } from 'angular2-jwt';
import { Apollo } from 'apollo-angular';
import gpl from 'graphql-tag';
import { Router } from '#angular/router';
const register = gpl`
mutation($name: String!, $email_mobile: String!, $password: String!) {
register(name: $name, email_mobile: $email_mobile, password: $password) {
success
token
user {
name
email
mobile
}
errors {
message
key
}
}
}
`;
const login = gpl`
mutation($email_mobile: String!, $password: String!) {
login(email_mobile: $email_mobile, password: $password) {
success
token
user {
name
email
mobile
}
errors {
message
key
}
}
}
`;
#Injectable()
export class AuthService {
constructor(
private jwtHelper: JwtHelper,
private apollo: Apollo,
private router: Router) { }
isLoggedIn(): boolean {
const token = localStorage.getItem('token');
if(!token)
return false;
// Check whether the token is expired and return
// true or false
return !this.jwtHelper.isTokenExpired(token);
}
async register(name: string, email_mobile: string, password: string) {
let regInfo = {};
await this.apollo.mutate({
mutation: register,
variables: {
name,
email_mobile,
password
}
}).subscribe(({data}) => {
const regData = data.register;
if(regData.success) {
// set token to Local Storage
localStorage.setItem("token", regData.token);
this.router.navigate(['/dashboard']);
} else {
regInfo["errors"] = regData.errors;
}
regInfo["success"] = regData.success;
});
return regInfo;
}
async login(email_mobile: string, password: string) {
let regInfo = {};
await this.apollo.mutate({
mutation: login,
variables: {
email_mobile,
password
}
}).subscribe(({data}) => {
const regData = data.login;
if(regData.success) {
// set token to Local Storage
localStorage.setItem("token", regData.token);
regInfo["user"] = regData.user;
this.router.navigate(['/dashboard']);
} else {
regInfo["errors"] = regData.errors;
}
regInfo["success"] = regData.success;
});
return regInfo;
}
logout(){
localStorage.removeItem("token");
}
}
You should subscribe to changes in your Apollo class, not just take them one off. Your code retrieves the variable, but does not stay to listen in on the pipe, when another user flies by.
Your code
this.user$ = this.apollo.watchQuery<any>({
query: registeredUser
})
.valueChanges;
What I envision
this.apollo.valueChanges.subscribe(
({ data }) => {
this.user$ = [...data.user];
}
);
And remember kids, please unsusbcribe from your pipes!
Read more about GraphQL Subscriptions here!
https://alligator.io/angular/graphql-subscriptions/