Skip to content

Commit 476d9aa

Browse files
author
hirsch88
committed
Add new hooks
1 parent e08d5d4 commit 476d9aa

File tree

10 files changed

+134
-18
lines changed

10 files changed

+134
-18
lines changed

.env.example

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,4 @@ DB_SEEDS_DIR=./src/database/seeds
3434
#
3535
# Auth0
3636
#
37-
AUTH0_HOST=https://w3tecch.auth0.com
38-
AUTH0_OAUTH=/oauth
39-
AUTH0_API=/api/v2
40-
AUTH0_CLIENT_ID=auth0_client_id
41-
AUTH0_CLIENT_SECRET=auth0_client_secret
42-
AUTH0_AUDIENCE=https://my.auth0.com/api/v2/
43-
AUTH0_GRANT_TYPE=client_credentials
37+
AUTH0_HOST=http://localhost:3333

src/api/controllers/UserController.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { injectable, inject } from 'inversify';
2-
import { Controller, Get, Post, Put, Delete, RequestParam, RequestBody, Response } from 'inversify-express-utils';
2+
import { Controller, Get, Post, Put, Delete, RequestParam, RequestBody, Response, Request } from 'inversify-express-utils';
33
import { my } from 'my-express';
44
import { Log } from '../../core/log';
55
import { UserService } from '../services/UsersService';
66
import { Types } from '../../constants/Types';
77
import { authenticate } from '../middlewares/authenticate';
8+
import { populateUser } from '../middlewares/populateUser';
89

910
const log = new Log('api:ctrl.UserController');
1011

@@ -16,18 +17,24 @@ const log = new Log('api:ctrl.UserController');
1617
* @class UserController
1718
*/
1819
@injectable()
19-
@Controller('/v1/user')
20+
@Controller('/v1/user', authenticate)
2021
export class UserController {
2122

2223
constructor( @inject(Types.UserService) private userService: UserService) { }
2324

24-
@Get('/', authenticate)
25+
@Get('/')
2526
public async findAll( @Response() res: my.Response): Promise<any> {
2627
log.debug('findAll');
2728
const users = await this.userService.findAll();
2829
return res.found(users.toJSON());
2930
}
3031

32+
@Get('/me', populateUser)
33+
public async findMe( @Request() req: my.Request, @Response() res: my.Response): Promise<any> {
34+
log.debug('findMe');
35+
return res.found(req.user);
36+
}
37+
3138
@Get('/:id')
3239
public async findOne( @Response() res: my.Response, @RequestParam('id') id: string): Promise<any> {
3340
log.debug('findOne ', id);
Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as request from 'request';
12
import { my } from 'my-express';
23
import { Log } from '../../core/log';
34

@@ -6,14 +7,56 @@ const log = new Log('api:middleware.authenticate');
67
/**
78
* authenticate middleware
89
* -----------------------
9-
* This middleware can be used to check if all credentials are given and
10-
* verify them.
10+
* This middleware secures your resources with the auth0 authentication.
1111
*
1212
* @param req
1313
* @param res
1414
* @param next
1515
*/
1616
export const authenticate = (req: my.Request, res: my.Response, next: my.NextFunction) => {
17-
log.info('authenticate');
18-
next();
17+
const token = getToken(req);
18+
19+
if (token === null) {
20+
log.warn('No token given');
21+
return res.failed(403, 'You are not allowed to request this resource!');
22+
}
23+
log.debug('Token is provided');
24+
25+
// Request user info at auth0 with the provided token
26+
request.post({
27+
url: `${process.env.AUTH0_HOST}/tokeninfo`,
28+
form: {
29+
id_token: token
30+
}
31+
}, (error: any, response: request.RequestResponse, body: any) => {
32+
33+
// Verify if the requests was successful and append user
34+
// information to our extended express request object
35+
if (!error && response.statusCode === 200) {
36+
req.tokeninfo = JSON.parse(body);
37+
log.info(`Retrieved user ${req.tokeninfo.email}`);
38+
return next();
39+
}
40+
41+
// Catch auth0 exception and return it as it is
42+
log.warn(`Could not retrieve the user, because of`, body);
43+
res.failed(response.statusCode || 401, body);
44+
45+
});
46+
47+
};
48+
49+
/**
50+
* Returns the access token of the given request header
51+
*/
52+
const getToken = (req: my.Request): string | null => {
53+
const authorization = req.headers.authorization;
54+
55+
// Retrieve the token form the Authorization header
56+
if (authorization && authorization.split(' ')[0] === 'Bearer') {
57+
return authorization.split(' ')[1];
58+
}
59+
60+
// No token was provided by the client
61+
return null;
1962
};
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import * as request from 'request';
2+
import { my } from 'my-express';
3+
import { Log } from '../../core/log';
4+
import { UserService } from '../services/UsersService';
5+
import { Types } from '../../constants/Types';
6+
import container from '../../container';
7+
8+
const log = new Log('api:middleware.populateUser');
9+
10+
/**
11+
* populateUser middleware
12+
* -----------------------
13+
* This middleware gets the logged-in user form the database and store it in
14+
* the request object
15+
*
16+
* @param req
17+
* @param res
18+
* @param next
19+
*/
20+
export const populateUser = (req: my.Request, res: my.Response, next: my.NextFunction) => {
21+
22+
if (!req.tokeninfo || !req.tokeninfo.user_id) {
23+
return res.failed(400, 'Missing token information!');
24+
}
25+
26+
const userService = container.get<UserService>(Types.UserService);
27+
userService.findByUserId(req.tokeninfo.user_id)
28+
.then((user) => {
29+
req.user = user.toJSON();
30+
log.debug(`populated user with the id=${req.user.id} to the request object`);
31+
next();
32+
})
33+
.catch((error) => {
34+
log.warn(`could not populate user to the request object`);
35+
next(error);
36+
});
37+
};

src/api/models/User.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ export class User extends Bookshelf.Model<User> {
1414
return await User.where<User>({ id: id }).fetch();
1515
}
1616

17+
public static async fetchByUserId(userId: string): Promise<User> {
18+
return await User.where<User>({ auth_0_user_id: userId }).fetch();
19+
}
20+
1721
public get tableName(): string { return Tables.Users; }
1822
public get hasTimestamps(): boolean { return true; }
1923

src/api/repositories/UserRepository.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ export class UserRepository {
3535
return User.fetchById(id);
3636
}
3737

38+
/**
39+
* Retrieves one user entity of the database
40+
*
41+
* @static
42+
* @param {number} id of the user
43+
* @returns {Promise<User>}
44+
*/
45+
public static async findByUserId(userId: string): Promise<User> {
46+
return User.fetchByUserId(userId);
47+
}
48+
3849
/**
3950
* Creates a new user entity in the database and returns
4051
* the new created entity

src/api/services/UsersService.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,21 @@ export class UserService {
5050
return user;
5151
}
5252

53+
/**
54+
* Returns the user with the given user_id or throws a Not-Found exception
55+
*
56+
* @param {number} id of the user
57+
* @returns {Promise<User>}
58+
*/
59+
public async findByUserId(userId: string): Promise<User> {
60+
let user = await this.userRepo.findByUserId(userId);
61+
if (user === null) {
62+
log.warn(`User with the userId=${userId} was not found!`);
63+
throw new NotFoundException(userId);
64+
}
65+
return user;
66+
}
67+
5368
/**
5469
* We will validate the data and create a new user and
5570
* return it, so the client get its new id

src/core/api/extendExpressResponse.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export const extendExpressResponse = (req: my.Request, res: my.Response, next: e
5353
* 400-500 - Failed
5454
* This is used when a request has failed
5555
*/
56-
res.failed = (status: number, message: string, error: any) => {
56+
res.failed = (status: number, message: string, error?: any) => {
5757
return res.status(status).json(bodyFailed(message, error));
5858
};
5959

@@ -76,11 +76,11 @@ export function bodySuccessful<T>(data: T, options: my.ResponseOptions = {}): an
7676
/**
7777
* This body parse is used for error messages to the client
7878
*/
79-
export function bodyFailed(message: string, error: any): any {
79+
export function bodyFailed(message: string, error?: any): any {
8080
return {
8181
success: false,
8282
message: message,
83-
error: error
83+
...{ error: error }
8484
};
8585
}
8686

src/database/factories/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@ factory.define(User, (faker: Faker.FakerStatic) => {
1616
const gender = faker.random.number(1);
1717
const fn = faker.name.firstName(gender);
1818
const ln = faker.name.lastName(gender);
19+
const e = faker.internet.email(fn, ln);
1920
return {
2021
firstName: fn,
2122
lastName: ln,
22-
email: faker.internet.email(fn, ln)
23+
email: e,
24+
auth0UserId: 'auth0|' + e,
25+
picture: faker.internet.avatar()
2326
};
2427
});
2528

src/database/migrations/20170120183349_create_users_table.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ exports.up = (db: Knex): Promise<any> => {
99
table.string('first_name').notNullable();
1010
table.string('last_name').notNullable();
1111
table.string('email').notNullable().unique();
12+
table.string('auth_0_user_id').unique();
13+
table.string('picture');
1214

1315
table.timestamp('updated_at').defaultTo(db.fn.now());
1416
table.timestamp('created_at').defaultTo(db.fn.now());

0 commit comments

Comments
 (0)