diff --git a/backend/karaoqueue-backend/package.json b/backend/karaoqueue-backend/package.json index ea925d6..4e5f004 100644 --- a/backend/karaoqueue-backend/package.json +++ b/backend/karaoqueue-backend/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "start": "node dist/index.js", - "debug": "node --nolazy --inspect-brk=9229 dist/index.js", + "debug": "node --nolazy dist/index.js", "build": "tsc", "test": "echo \"Error: no test specified\" && exit 1" }, diff --git a/backend/karaoqueue-backend/src/controllers/auth.controller.ts b/backend/karaoqueue-backend/src/controllers/auth.controller.ts index af22fd5..b8e4c0b 100644 --- a/backend/karaoqueue-backend/src/controllers/auth.controller.ts +++ b/backend/karaoqueue-backend/src/controllers/auth.controller.ts @@ -1,11 +1,29 @@ +import { Request, Response } from "express"; import { Post, BodyParam, Body, Res, Req, JsonController, UseBefore, Get, CookieParam } from "routing-controllers"; -import { UserCredential } from "../models/usercredential.model"; +import User from "../interfaces/user.interface"; +import { JwtMiddleware } from "../middleware/jwt.middleware"; @JsonController("/auth") export class AuthenticationController { + @Post("/login") - doLogin(@Body() usercredential: UserCredential, @Res() res: any) { - return "//TODO login"; + doLogin(@Body() user: User, @Res() res: Response) { + if (user.username === process.env.KQUEUE_USERNAME) { + if (user.password === process.env.KQUEUE_PASSWORD) { + const jwtMiddleware = new JwtMiddleware(); + const tokenData = jwtMiddleware.createToken(user); + res.cookie("jwt",tokenData,); + res.status(200); + res.send("Welcome.") + return res; + } else { + // TODO wrong password. + return "Wrong password." + } + } else { + // TODO wrong user. + return "Wrong user." + } } @Get("/logout") diff --git a/backend/karaoqueue-backend/src/exceptions/HttpException.ts b/backend/karaoqueue-backend/src/exceptions/HttpException.ts new file mode 100644 index 0000000..3b41eba --- /dev/null +++ b/backend/karaoqueue-backend/src/exceptions/HttpException.ts @@ -0,0 +1,11 @@ +class HttpException extends Error { + public status: number; + public message: string; + constructor(status: number, message: string) { + super(message); + this.status = status; + this.message = message; + } +} + +export default HttpException; diff --git a/backend/karaoqueue-backend/src/index.ts b/backend/karaoqueue-backend/src/index.ts index 6b86269..d668196 100644 --- a/backend/karaoqueue-backend/src/index.ts +++ b/backend/karaoqueue-backend/src/index.ts @@ -1,10 +1,12 @@ import "reflect-metadata"; -import { createExpressServer } from "routing-controllers"; +import { Request, Response } from "express"; +import { Action, createExpressServer } from "routing-controllers"; import { QueueController } from "./controllers/queue.controller"; import { SongController } from "./controllers/songs.controller"; import { StatisticsController } from "./controllers/statistics.controller"; import { AuthenticationController } from "./controllers/auth.controller"; import { RpcController } from "./controllers/rpc.controller"; +import jwt from "jsonwebtoken"; import * as dotenv from "dotenv"; @@ -13,7 +15,34 @@ dotenv.config(); const app = createExpressServer({ routePrefix: "/api", cors: true, + authorizationChecker: async (action: Action) => { + const req: Request = action.request; + const secret = process.env.KQUEUE_JWTSECRET; + const token = parseCookies(req.headers.cookie)["jwt"]; + if (token) { + try { + const verificationResponse = jwt.verify(token, secret); + if (verificationResponse) { + return true; + } else { + return false; + } + } catch (error) { + return false; + } + } else { + return false; + } + + }, controllers: [QueueController, SongController, StatisticsController, AuthenticationController, RpcController] }); +app.listen(process.env.KQUEUE_PORT); -app.listen(process.env.KQUEUE_PORT); \ No newline at end of file +function parseCookies(str) { + let rx = /([^;=\s]*)=([^;]*)/g; + let obj = {}; + for (let m; m = rx.exec(str);) + obj[m[1]] = decodeURIComponent(m[2]); + return obj; +} \ No newline at end of file diff --git a/backend/karaoqueue-backend/src/interfaces/dataStoredInToken.interface.ts b/backend/karaoqueue-backend/src/interfaces/dataStoredInToken.interface.ts new file mode 100644 index 0000000..b2ac992 --- /dev/null +++ b/backend/karaoqueue-backend/src/interfaces/dataStoredInToken.interface.ts @@ -0,0 +1,5 @@ +interface DataStoredInToken { + _id: string; +} + +export default DataStoredInToken; \ No newline at end of file diff --git a/backend/karaoqueue-backend/src/interfaces/user.interface.ts b/backend/karaoqueue-backend/src/interfaces/user.interface.ts new file mode 100644 index 0000000..3cdfa62 --- /dev/null +++ b/backend/karaoqueue-backend/src/interfaces/user.interface.ts @@ -0,0 +1,6 @@ +interface User { + username: string; + password: string; +} + +export default User; \ No newline at end of file diff --git a/backend/karaoqueue-backend/src/middleware/jwt.middleware.ts b/backend/karaoqueue-backend/src/middleware/jwt.middleware.ts new file mode 100644 index 0000000..bd67a91 --- /dev/null +++ b/backend/karaoqueue-backend/src/middleware/jwt.middleware.ts @@ -0,0 +1,14 @@ +import DataStoredInToken from "../interfaces/dataStoredInToken.interface"; +import User from "../interfaces/user.interface"; +import * as jwt from 'jsonwebtoken'; + +export class JwtMiddleware { + public createToken(user: User): string { + const expiresIn = 60 * 60; // an hour + const secret = process.env.KQUEUE_JWTSECRET; + const dataStoredInToken: DataStoredInToken = { + _id: user.username, + }; + return jwt.sign(dataStoredInToken, secret, { expiresIn }); + } +} \ No newline at end of file diff --git a/backend/karaoqueue-backend/src/models/usercredential.model.ts b/backend/karaoqueue-backend/src/models/usercredential.model.ts deleted file mode 100644 index 5f67b6d..0000000 --- a/backend/karaoqueue-backend/src/models/usercredential.model.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface UserCredential { - username: string, - password: string -} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 6944bad..ceaf6be 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -244,12 +244,12 @@ paths: '200': description: > Successfully authenticated. - The session ID is returned in a cookie named `connect.sid`. You need to include this cookie in subsequent requests. + The session ID is returned in a cookie named `jwt`. You need to include this cookie in subsequent requests. headers: Set-Cookie: schema: type: string - example: connect.sid=abcde12345; Path=/; HttpOnly + example: jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiJhZG1pbiIsImlhdCI6MTYwMTY1MDYwNSwiZXhwIjoxNjAxNjU0MjA1fQ.uGvOlBAZdbPT8U9s7jEt5PUWyxLrpgaf02EoPVC_Zlsd; Path=/; HttpOnly /auth/logout: get: summary: Logs the user out and invalidates the session on the server @@ -345,7 +345,7 @@ components: cookieAuth: # arbitrary name for the security scheme; will be used in the "security" key later type: apiKey in: cookie - name: connect.sid # cookie name + name: jwt # cookie name schemas: QueueEntry: type: object