· 7 years ago · Jul 20, 2018, 12:40 PM
1import * as Koa from 'koa';
2import * as Router from 'koa-router';
3import * as bodyParser from 'koa-bodyparser';
4import * as jwt from 'jsonwebtoken';
5// dotenv - пакет, который читает `.env` файл и Ñеттит `key-value` из файла в `process.env`
6import { config } from 'dotenv';
7import { compare } from 'bcrypt';
8
9// Секретный ключ Ð´Ð»Ñ Ñ…ÐµÑˆÐ¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸ наоборот, можете Ñгенерить его на любом Ñами и заÑеттить в `.env` файл `SECRET=some_key`
10const { SECRET } = process.env;
11
12const compareAsync = (data: string, encrypted: string): Promise<boolean> => {
13 return new Promise((resolve, reject) => {
14 compare(data, encrypted, (error: Error, same: boolean) => {
15 if (error) {
16 reject(error);
17 }
18
19 resolve(same);
20 });
21 });
22};
23
24const app = new Koa();
25
26app.use(bodyParser());
27
28const router = new Router({
29 prefix: '/api'
30});
31
32router.post('/login', async (ctx: Context) => {
33 // Юзер логинитÑÑ Ð¸ шлет Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° `/api/login`
34 // Ñ username + password
35 const { username, password } = ctx.request.body;
36
37 // ПопытаемÑÑ Ð²Ñ‹Ñ‚Ð°Ñ‰Ð¸Ñ‚ÑŒ юзера из базы Ñ Ñ‚Ð°ÐºÐ¸Ð¼ `username`
38 const { rows } = await client.query(`
39 SELECT * FROM users WHERE username = $1
40 `, [username]);
41
42 // ЕÑли такого Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð½ÐµÑ‚Ñƒ
43 if (rows.length === 0) {
44 ctx.body = {
45 message: `User with username '${username}' doesn't exist`
46 };
47 // Вернем ÑÑ‚Ð°Ñ‚ÑƒÑ `UNPROCESSABLE_ENTITY`
48 return ctx.status = 422;
49 }
50
51 const user = rows[0];
52 // СверÑем пароли
53 const same = await compareAsync(password, user.password);
54
55 if (!same) {
56 ctx.body = {
57 message: 'Invalid credentials'
58 };
59 return ctx.status = 422;
60 }
61
62 // ЕÑли такой юзер ÑущеÑтвует, то берем его `id` и хешируем в токене
63 const { id } = user;
64 const token = jwt.sign({
65 id
66 }, SECRET_KEY, {
67 expiresIn: 864e5 // 1 день
68 });
69
70 // Далее Ñеттим токен в куках Ñ Ñ„Ð»Ð°Ð³Ð¾Ð¼ `httpOnly`, так безопаÑнее так как клиент не имеет доÑтуп к ним
71 ctx.cookies.set('Authorization', `Bearer ${token}`, {
72 httpOnly: true,
73 expires: new Date(new Date().valueOf() + 864e5),
74 secure: true // 1 день
75 });
76 ctx.body = {
77 id,
78 email,
79 username
80 };
81});
82
83import { verify } from 'jsonwebtoken';
84import { config } from 'dotenv';
85
86const { SECRET } = process.env;
87
88export const authMiddleware = async (ctx: Context, next: () => Promise<void>): Promise<void> => {
89 const authorization = ctx.cookies.get('Authorization');
90
91 if (!authorization) {
92 return ctx.status = 401;
93 }
94
95 const token: string = authorization.split(' ')[1];
96
97 try {
98 verify(token, SECRET);
99
100 return next();
101 } catch (e) {
102 return ctx.status = 401;
103 }
104};
105
106import { authMiddleware } from './authMiddleware.ts';
107
108......
109
110router.get('/users', authMiddleware, async (ctx) .....)