Fix JWT authentication, add custom user service
This commit is contained in:
parent
ee2d7b4838
commit
cfc3d48a7b
4 changed files with 87 additions and 30 deletions
|
@ -11,7 +11,7 @@ import path from 'path';
|
|||
import {MySequence} from './sequence';
|
||||
import {AuthenticationComponent} from '@loopback/authentication';
|
||||
import {JWTAuthenticationComponent, UserServiceBindings} from '@loopback/authentication-jwt';
|
||||
import {DatabaseDataSource} from './datasources';
|
||||
import {SzakdolgozatUserService} from './services';
|
||||
|
||||
export {ApplicationConfig};
|
||||
|
||||
|
@ -36,7 +36,7 @@ export class SzakdolgozatBackendApplication extends BootMixin(
|
|||
// Authentication
|
||||
this.component(AuthenticationComponent);
|
||||
this.component(JWTAuthenticationComponent);
|
||||
this.dataSource(DatabaseDataSource, UserServiceBindings.DATASOURCE_NAME)
|
||||
this.service(SzakdolgozatUserService, UserServiceBindings.USER_SERVICE);
|
||||
|
||||
this.projectRoot = __dirname;
|
||||
// Customize @loopback/boot Booter Conventions here
|
||||
|
|
|
@ -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 {User} from '../models';
|
||||
import {UserRepository} from '../repositories';
|
||||
import {
|
||||
Credentials,
|
||||
MyUserService,
|
||||
TokenServiceBindings,
|
||||
UserServiceBindings
|
||||
} from '@loopback/authentication-jwt';
|
||||
import {inject, MetadataInspector} from '@loopback/core';
|
||||
import {inject} from '@loopback/core';
|
||||
import {TokenService} from '@loopback/authentication';
|
||||
import {SecurityBindings, UserProfile} from '@loopback/security';
|
||||
import {genSalt, hash} from 'bcryptjs';
|
||||
import _ from 'lodash';
|
||||
import {UserRepository as UserRepo} from '@loopback/authentication-jwt';
|
||||
import {SzakdolgozatUserService} from '../services';
|
||||
|
||||
export class UserController {
|
||||
constructor(
|
||||
@inject(TokenServiceBindings.TOKEN_SERVICE)
|
||||
public jwtService: TokenService,
|
||||
@inject(UserServiceBindings.USER_SERVICE)
|
||||
public userService: MyUserService,
|
||||
public userService: SzakdolgozatUserService,
|
||||
@inject(SecurityBindings.USER, {optional: true})
|
||||
public user: UserProfile,
|
||||
@repository(UserRepo)
|
||||
public userRepository : UserRepo,
|
||||
@repository(UserRepository)
|
||||
public userDataRepository : 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");
|
||||
}
|
||||
public userRepository : UserRepository,
|
||||
) { }
|
||||
|
||||
@post('/users')
|
||||
@response(200, {
|
||||
|
@ -62,7 +49,7 @@ export class UserController {
|
|||
password: password,
|
||||
role: 'student'
|
||||
} as User;
|
||||
return this.userDataRepository.create(user);
|
||||
return this.userRepository.create(user);
|
||||
}
|
||||
|
||||
@post('/users/login', {
|
||||
|
@ -92,7 +79,7 @@ export class UserController {
|
|||
'application/json': {
|
||||
schema: getModelSchemaRef(User, {exclude: ['id', 'role', 'name']})
|
||||
},
|
||||
}}) credentials: Credentials,
|
||||
}}) credentials: Pick<User, 'email' | 'password'>,
|
||||
): Promise<{token: string}> {
|
||||
// ensure the user exists, and the password is correct
|
||||
const user = await this.userService.verifyCredentials(credentials);
|
||||
|
@ -112,7 +99,7 @@ export class UserController {
|
|||
async count(
|
||||
@param.where(User) where?: Where<User>,
|
||||
): Promise<Count> {
|
||||
return this.userDataRepository.count(where);
|
||||
return this.userRepository.count(where);
|
||||
}
|
||||
|
||||
@get('/users')
|
||||
|
@ -130,7 +117,7 @@ export class UserController {
|
|||
async find(
|
||||
@param.filter(User) filter?: Filter<User>,
|
||||
): Promise<User[]> {
|
||||
return this.userDataRepository.find(filter);
|
||||
return this.userRepository.find(filter);
|
||||
}
|
||||
|
||||
@patch('/users')
|
||||
|
@ -149,7 +136,7 @@ export class UserController {
|
|||
user: User,
|
||||
@param.where(User) where?: Where<User>,
|
||||
): Promise<Count> {
|
||||
return this.userDataRepository.updateAll(user, where);
|
||||
return this.userRepository.updateAll(user, where);
|
||||
}
|
||||
|
||||
@get('/users/{id}')
|
||||
|
@ -165,7 +152,7 @@ export class UserController {
|
|||
@param.path.number('id') id: number,
|
||||
@param.filter(User, {exclude: 'where'}) filter?: FilterExcludingWhere<User>
|
||||
): Promise<User> {
|
||||
return this.userDataRepository.findById(id, filter);
|
||||
return this.userRepository.findById(id, filter);
|
||||
}
|
||||
|
||||
@patch('/users/{id}')
|
||||
|
@ -183,7 +170,7 @@ export class UserController {
|
|||
})
|
||||
user: User,
|
||||
): Promise<void> {
|
||||
await this.userDataRepository.updateById(id, user);
|
||||
await this.userRepository.updateById(id, user);
|
||||
}
|
||||
|
||||
@put('/users/{id}')
|
||||
|
@ -194,7 +181,7 @@ export class UserController {
|
|||
@param.path.number('id') id: number,
|
||||
@requestBody() user: User,
|
||||
): Promise<void> {
|
||||
await this.userDataRepository.replaceById(id, user);
|
||||
await this.userRepository.replaceById(id, user);
|
||||
}
|
||||
|
||||
@del('/users/{id}')
|
||||
|
@ -202,6 +189,6 @@ export class UserController {
|
|||
description: 'User DELETE success',
|
||||
})
|
||||
async deleteById(@param.path.number('id') id: number): Promise<void> {
|
||||
await this.userDataRepository.deleteById(id);
|
||||
await this.userRepository.deleteById(id);
|
||||
}
|
||||
}
|
||||
|
|
1
backend/src/services/index.ts
Normal file
1
backend/src/services/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './user.service';
|
69
backend/src/services/user.service.ts
Normal file
69
backend/src/services/user.service.ts
Normal 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;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue