Skip to content
Snippets Groups Projects
Commit 0e16c5f4 authored by Jonny Ngo Luong's avatar Jonny Ngo Luong
Browse files

feat: added jwt token as session and authentication token (#9)

parent b46209f9
No related branches found
No related tags found
No related merge requests found
Showing with 368 additions and 11 deletions
......@@ -18,3 +18,8 @@ yarn-error.log*
.idea
.vscode
# keys and .env
jwtRS256.key
jwtRS256.key.pub
server/.env
\ No newline at end of file
......@@ -6,6 +6,7 @@ import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { PostModule } from './posts/post.module';
import { UserModule } from './users/user.module';
import { AuthModule } from './authentication/auth.module';
import { SharedModule } from './shared/shared.module';
......@@ -16,7 +17,7 @@ import { SharedModule } from './shared/shared.module';
imports: [
BrowserModule,
UserModule,
AuthModule,
AppRoutingModule,
PostModule,
SharedModule,
......
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [],
imports: [
CommonModule
]
})
export class AuthModule { }
import { TestBed } from '@angular/core/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(AuthService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { HttpClient, HttpEvent, HttpInterceptor, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { User } from '../models/user.model';
import { tap, shareReplay } from 'rxjs/operators';
interface IUserLogin {
username: string;
password: string;
}
@Injectable({
providedIn: 'root'
})
export class AuthService {
loginUrl = "api/user/login"
constructor(private http: HttpClient) { }
login(body: IUserLogin): Promise<string> {
return new Promise<string>(
(resolve, reject) => {
this.login_user(body).subscribe((data: any) => {
try {
resolve(data.token);
} catch (err: any) {
reject(err);
}
},
(err: any) => {
console.log(err.message);
reject(err);
});
}
);
}
private login_user(body: IUserLogin) {
return this.http.post(this.loginUrl, body).pipe(
tap(res =>this.setSession(res)),
shareReplay());;
}
private setSession(authResult) {
console.log(authResult);
localStorage.setItem('token', authResult.token);
}
logout() {
localStorage.removeItem("token");
}
}
......@@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { User } from 'src/app/models/user.model';
import { UserService } from '../user.service';
import { AuthService } from '../../authentication/auth.service';
@Component({
selector: 'app-user-login-form',
......@@ -14,7 +15,7 @@ export class UserLoginFormComponent implements OnInit {
statusMessage: string = "";
constructor(private userService: UserService, private router: Router) { }
constructor(private userService: UserService, private authService: AuthService, private router: Router) { }
ngOnInit(): void {
}
......@@ -46,13 +47,21 @@ export class UserLoginFormComponent implements OnInit {
password: this.password,
};
// Adds user to database and changes page afterwards
// Login the user
this.authService.login(request).then(status => {
console.log("User login1: " + JSON.stringify(status));
this.router.navigateByUrl("/");
}).catch(error => {
console.log("Error user login: " + error);
});
/* Old
this.userService.login(request).then(status => {
console.log("User login: " + JSON.stringify(status));
console.log("User login2: " + JSON.stringify(status));
this.router.navigateByUrl("/");
}).catch(error => {
console.log("Error adding user: " + error);
});
*/
}
}
......
<p>user-profile works!</p>
<p>Userid: {{user.getUserId}}</p>
<p>username: {{user.getUsername}}</p>
<p>email: {{user.getEmail}}</p>
\ No newline at end of file
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from 'src/app/authentication/auth.service';
import { User } from 'src/app/models/user.model';
import { UserService } from '../user.service';
@Component({
selector: 'app-user-profile',
......@@ -7,9 +12,21 @@ import { Component, OnInit } from '@angular/core';
})
export class UserProfileComponent implements OnInit {
constructor() { }
user: User = new User();
constructor(private userService: UserService, private authService: AuthService, private router: Router) { }
ngOnInit(): void {
const token = localStorage.getItem('token');
if (!token) this.router.navigateByUrl("/");
// Get user data from JWT token
const user_data = JSON.parse(atob(token.split(".")[1])).data[0];
console.log(user_data)
// Gets all categories and displays them in dropdown
this.userService.getUser(user_data.userId).then(user => {
this.user = user;
}).catch (error => {
console.log("Error getting user: " + error);
});
}
}
......@@ -15,14 +15,17 @@
"@types/supertest": "^2.0.10",
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"jest": "^26.6.3",
"jsonwebtoken": "^8.5.1",
"mysql": "^2.18.1",
"mysql2": "^2.2.5",
"supertest": "^6.1.3",
"ts-jest": "^26.5.1"
},
"devDependencies": {
"@types/jsonwebtoken": "^8.5.0",
"nodemon": "^2.0.7",
"ts-node": "^9.1.1",
"typescript": "^4.1.3"
......@@ -1062,6 +1065,15 @@
"pretty-format": "^26.0.0"
}
},
"node_modules/@types/jsonwebtoken": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz",
"integrity": "sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/mime": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
......@@ -1697,6 +1709,11 @@
"node-int64": "^0.4.0"
}
},
"node_modules/buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
},
"node_modules/buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
......@@ -2387,6 +2404,14 @@
"node": ">=8"
}
},
"node_modules/dotenv": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==",
"engines": {
"node": ">=8"
}
},
"node_modules/duplexer3": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
......@@ -2402,6 +2427,14 @@
"safer-buffer": "^2.1.0"
}
},
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
"dependencies": {
"safe-buffer": "^5.0.1"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
......@@ -4904,6 +4937,32 @@
"node": ">=6"
}
},
"node_modules/jsonwebtoken": {
"version": "8.5.1",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
"integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
"dependencies": {
"jws": "^3.2.2",
"lodash.includes": "^4.3.0",
"lodash.isboolean": "^3.0.3",
"lodash.isinteger": "^4.0.4",
"lodash.isnumber": "^3.0.3",
"lodash.isplainobject": "^4.0.6",
"lodash.isstring": "^4.0.1",
"lodash.once": "^4.0.0",
"ms": "^2.1.1",
"semver": "^5.6.0"
},
"engines": {
"node": ">=4",
"npm": ">=1.4.28"
}
},
"node_modules/jsonwebtoken/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
......@@ -4918,6 +4977,25 @@
"verror": "1.10.0"
}
},
"node_modules/jwa": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
"dependencies": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/jws": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
"dependencies": {
"jwa": "^1.4.1",
"safe-buffer": "^5.0.1"
}
},
"node_modules/keyv": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
......@@ -4996,6 +5074,41 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
},
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
"integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
},
"node_modules/lodash.isboolean": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
"integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
},
"node_modules/lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
"integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
},
"node_modules/lodash.isnumber": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
"integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
},
"node_modules/lodash.isstring": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
},
"node_modules/lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
"integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
},
"node_modules/lodash.sortby": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
......@@ -8997,6 +9110,15 @@
"pretty-format": "^26.0.0"
}
},
"@types/jsonwebtoken": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz",
"integrity": "sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/mime": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
......@@ -9501,6 +9623,11 @@
"node-int64": "^0.4.0"
}
},
"buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
},
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
......@@ -10039,6 +10166,11 @@
"is-obj": "^2.0.0"
}
},
"dotenv": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
},
"duplexer3": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
......@@ -10054,6 +10186,14 @@
"safer-buffer": "^2.1.0"
}
},
"ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
"requires": {
"safe-buffer": "^5.0.1"
}
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
......@@ -11928,6 +12068,30 @@
"minimist": "^1.2.5"
}
},
"jsonwebtoken": {
"version": "8.5.1",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
"integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
"requires": {
"jws": "^3.2.2",
"lodash.includes": "^4.3.0",
"lodash.isboolean": "^3.0.3",
"lodash.isinteger": "^4.0.4",
"lodash.isnumber": "^3.0.3",
"lodash.isplainobject": "^4.0.6",
"lodash.isstring": "^4.0.1",
"lodash.once": "^4.0.0",
"ms": "^2.1.1",
"semver": "^5.6.0"
},
"dependencies": {
"ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
}
}
},
"jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
......@@ -11939,6 +12103,25 @@
"verror": "1.10.0"
}
},
"jwa": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
"requires": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"jws": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
"requires": {
"jwa": "^1.4.1",
"safe-buffer": "^5.0.1"
}
},
"keyv": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
......@@ -11999,6 +12182,41 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
},
"lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
"integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
},
"lodash.isboolean": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
"integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
},
"lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
"integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
},
"lodash.isnumber": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
"integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
},
"lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
},
"lodash.isstring": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
},
"lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
"integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
},
"lodash.sortby": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
......
......@@ -17,14 +17,17 @@
"@types/supertest": "^2.0.10",
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"jest": "^26.6.3",
"jsonwebtoken": "^8.5.1",
"mysql": "^2.18.1",
"mysql2": "^2.2.5",
"supertest": "^6.1.3",
"ts-jest": "^26.5.1"
},
"devDependencies": {
"@types/jsonwebtoken": "^8.5.0",
"nodemon": "^2.0.7",
"ts-node": "^9.1.1",
"typescript": "^4.1.3"
......
const { config } = require('dotenv');
config({ path: __dirname+'/../.env'});
const env = process.env;
const config = {
export default {
db: {
host: env.DB_HOST || 'mysql.stud.ntnu.no',
user: env.DB_USER || 'jonnynl_tdt4140',
......@@ -12,6 +14,9 @@ const config = {
debug: false
},
listPerPage: 10,
JWT_KEY : env.JWT_KEY || "",
HOST: env.HOST || "localhost",
PORT: env.HTTPPORT || 3000,
ACCESS_TOKEN_SECRET: env.ACCESS_TOKEN_SECRET,
REFRESH_TOKEN_SECRET: env.REFRESH_TOKEN_SECRET,
};
export default config;
......@@ -2,6 +2,8 @@ import { Response, Request } from "express";
import query from '../../services/db_query';
import express from 'express';
import IUser from '../../models/user';
import * as jwt from 'jsonwebtoken';
import config from '../../config';
const router = express.Router();
/* ============================= CREATE ============================= */
......@@ -50,9 +52,23 @@ router.route('/login').post(async (request: Request, response: Response) => {
const {username, password} = request.body;
try {
const input = "SELECT userId, username, email, create_time FROM user WHERE username=? AND password=?;"
response.status(200).json(await query(input,[username, password]));
const user = await query(input,[username, password]);
// Check if an user object is retrieved
const userObj = Object.values(JSON.parse(JSON.stringify(user.data)))[0];
if (userObj) {
const jwt_token = jwt.sign({data: user.data}, config.JWT_KEY.replace(/\\n/gm, '\n'), {
algorithm: 'RS256',
expiresIn: 3600*24, // 24 hours
});
response.status(200).json({
token: jwt_token,
});
} else {
response.status(403).send("Invalid combination of username and password given!");
}
} catch (error) {
response.status(400).send("Bad Request");
console.log(error);
}
});
......
import app from './app';
import config from './config';
// REST API config
const port = 3000;
const port = config.PORT;
app.listen(port, () => {
console.log(`Listening on port ${port}!`)
const host = config.HOST;
console.log('[*] Server listening at http://%s:%s \n', host, port);
});
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment