Fix JWT authentication, add custom user service

This commit is contained in:
Norbi Peti 2021-11-16 22:28:56 +01:00
parent ee2d7b4838
commit cfc3d48a7b
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
4 changed files with 87 additions and 30 deletions

View file

@ -11,7 +11,7 @@ import path from 'path';
import {MySequence} from './sequence'; import {MySequence} from './sequence';
import {AuthenticationComponent} from '@loopback/authentication'; import {AuthenticationComponent} from '@loopback/authentication';
import {JWTAuthenticationComponent, UserServiceBindings} from '@loopback/authentication-jwt'; import {JWTAuthenticationComponent, UserServiceBindings} from '@loopback/authentication-jwt';
import {DatabaseDataSource} from './datasources'; import {SzakdolgozatUserService} from './services';
export {ApplicationConfig}; export {ApplicationConfig};
@ -36,7 +36,7 @@ export class SzakdolgozatBackendApplication extends BootMixin(
// Authentication // Authentication
this.component(AuthenticationComponent); this.component(AuthenticationComponent);
this.component(JWTAuthenticationComponent); this.component(JWTAuthenticationComponent);
this.dataSource(DatabaseDataSource, UserServiceBindings.DATASOURCE_NAME) this.service(SzakdolgozatUserService, UserServiceBindings.USER_SERVICE);
this.projectRoot = __dirname; this.projectRoot = __dirname;
// Customize @loopback/boot Booter Conventions here // Customize @loopback/boot Booter Conventions here

View file

@ -1,41 +1,28 @@
import {Count, CountSchema, Filter, FilterExcludingWhere, MODEL_PROPERTIES_KEY, repository, Where,} from '@loopback/repository'; import {Count, CountSchema, Filter, FilterExcludingWhere, repository, Where,} from '@loopback/repository';
import {del, get, getModelSchemaRef, param, patch, post, put, requestBody, response,} from '@loopback/rest'; import {del, get, getModelSchemaRef, param, patch, post, put, requestBody, response,} from '@loopback/rest';
import {User} from '../models'; import {User} from '../models';
import {UserRepository} from '../repositories'; import {UserRepository} from '../repositories';
import { import {
Credentials,
MyUserService,
TokenServiceBindings, TokenServiceBindings,
UserServiceBindings UserServiceBindings
} from '@loopback/authentication-jwt'; } from '@loopback/authentication-jwt';
import {inject, MetadataInspector} from '@loopback/core'; import {inject} from '@loopback/core';
import {TokenService} from '@loopback/authentication'; import {TokenService} from '@loopback/authentication';
import {SecurityBindings, UserProfile} from '@loopback/security'; import {SecurityBindings, UserProfile} from '@loopback/security';
import {genSalt, hash} from 'bcryptjs'; import {genSalt, hash} from 'bcryptjs';
import _ from 'lodash'; import {SzakdolgozatUserService} from '../services';
import {UserRepository as UserRepo} from '@loopback/authentication-jwt';
export class UserController { export class UserController {
constructor( constructor(
@inject(TokenServiceBindings.TOKEN_SERVICE) @inject(TokenServiceBindings.TOKEN_SERVICE)
public jwtService: TokenService, public jwtService: TokenService,
@inject(UserServiceBindings.USER_SERVICE) @inject(UserServiceBindings.USER_SERVICE)
public userService: MyUserService, public userService: SzakdolgozatUserService,
@inject(SecurityBindings.USER, {optional: true}) @inject(SecurityBindings.USER, {optional: true})
public user: UserProfile, public user: UserProfile,
@repository(UserRepo)
public userRepository : UserRepo,
@repository(UserRepository) @repository(UserRepository)
public userDataRepository : UserRepository, public userRepository : UserRepository,
) {} ) { }
static includeToExclude(values: (keyof User)[]): (keyof User)[] {
const metadata = MetadataInspector.getAllPropertyMetadata(MODEL_PROPERTIES_KEY, User);
if (metadata) {
return _.without(Object.keys(metadata), ...values) as (keyof User)[];
}
else throw new Error("No property metadata found");
}
@post('/users') @post('/users')
@response(200, { @response(200, {
@ -62,7 +49,7 @@ export class UserController {
password: password, password: password,
role: 'student' role: 'student'
} as User; } as User;
return this.userDataRepository.create(user); return this.userRepository.create(user);
} }
@post('/users/login', { @post('/users/login', {
@ -92,7 +79,7 @@ export class UserController {
'application/json': { 'application/json': {
schema: getModelSchemaRef(User, {exclude: ['id', 'role', 'name']}) schema: getModelSchemaRef(User, {exclude: ['id', 'role', 'name']})
}, },
}}) credentials: Credentials, }}) credentials: Pick<User, 'email' | 'password'>,
): Promise<{token: string}> { ): Promise<{token: string}> {
// ensure the user exists, and the password is correct // ensure the user exists, and the password is correct
const user = await this.userService.verifyCredentials(credentials); const user = await this.userService.verifyCredentials(credentials);
@ -112,7 +99,7 @@ export class UserController {
async count( async count(
@param.where(User) where?: Where<User>, @param.where(User) where?: Where<User>,
): Promise<Count> { ): Promise<Count> {
return this.userDataRepository.count(where); return this.userRepository.count(where);
} }
@get('/users') @get('/users')
@ -130,7 +117,7 @@ export class UserController {
async find( async find(
@param.filter(User) filter?: Filter<User>, @param.filter(User) filter?: Filter<User>,
): Promise<User[]> { ): Promise<User[]> {
return this.userDataRepository.find(filter); return this.userRepository.find(filter);
} }
@patch('/users') @patch('/users')
@ -149,7 +136,7 @@ export class UserController {
user: User, user: User,
@param.where(User) where?: Where<User>, @param.where(User) where?: Where<User>,
): Promise<Count> { ): Promise<Count> {
return this.userDataRepository.updateAll(user, where); return this.userRepository.updateAll(user, where);
} }
@get('/users/{id}') @get('/users/{id}')
@ -165,7 +152,7 @@ export class UserController {
@param.path.number('id') id: number, @param.path.number('id') id: number,
@param.filter(User, {exclude: 'where'}) filter?: FilterExcludingWhere<User> @param.filter(User, {exclude: 'where'}) filter?: FilterExcludingWhere<User>
): Promise<User> { ): Promise<User> {
return this.userDataRepository.findById(id, filter); return this.userRepository.findById(id, filter);
} }
@patch('/users/{id}') @patch('/users/{id}')
@ -183,7 +170,7 @@ export class UserController {
}) })
user: User, user: User,
): Promise<void> { ): Promise<void> {
await this.userDataRepository.updateById(id, user); await this.userRepository.updateById(id, user);
} }
@put('/users/{id}') @put('/users/{id}')
@ -194,7 +181,7 @@ export class UserController {
@param.path.number('id') id: number, @param.path.number('id') id: number,
@requestBody() user: User, @requestBody() user: User,
): Promise<void> { ): Promise<void> {
await this.userDataRepository.replaceById(id, user); await this.userRepository.replaceById(id, user);
} }
@del('/users/{id}') @del('/users/{id}')
@ -202,6 +189,6 @@ export class UserController {
description: 'User DELETE success', description: 'User DELETE success',
}) })
async deleteById(@param.path.number('id') id: number): Promise<void> { async deleteById(@param.path.number('id') id: number): Promise<void> {
await this.userDataRepository.deleteById(id); await this.userRepository.deleteById(id);
} }
} }

View file

@ -0,0 +1 @@
export * from './user.service';

View file

@ -0,0 +1,69 @@
// Copyright IBM Corp. 2020. All Rights Reserved.
// Node module: @loopback/authentication-jwt
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
import {UserService} from '@loopback/authentication';
import {repository} from '@loopback/repository';
import {HttpErrors} from '@loopback/rest';
import {securityId, UserProfile} from '@loopback/security';
import {compare} from 'bcryptjs';
import {User, UserWithRelations} from '../models';
import {UserRepository} from '../repositories';
/**
* A pre-defined type for user credentials. It assumes a user logs in
* using the email and password. You can modify it if your app has different credential fields
*/
export type Credentials = {
email: string;
password: string;
};
export class SzakdolgozatUserService implements UserService<User, Credentials> {
constructor(
@repository(UserRepository) public userRepository: UserRepository,
) { }
async verifyCredentials(credentials: Credentials): Promise<User> {
const invalidCredentialsError = 'Invalid email or password.';
const foundUser = await this.userRepository.findOne({
where: {email: credentials.email},
});
if (!foundUser) {
throw new HttpErrors.Unauthorized(invalidCredentialsError);
}
const passwordMatched = await compare(
credentials.password,
foundUser.password,
);
if (!passwordMatched) {
throw new HttpErrors.Unauthorized(invalidCredentialsError);
}
return foundUser;
}
convertToUserProfile({email, id, name}: User): UserProfile {
return {
[securityId]: id!.toString(),
name,
id,
email,
};
}
//function to find user by id
async findUserById(id: number): Promise<User & UserWithRelations> {
const userNotfound = 'invalid user';
const foundUser = await this.userRepository.findById(id);
if (!foundUser) {
throw new HttpErrors.Unauthorized(userNotfound);
}
return foundUser;
}
}