Skip to content

Commit 4f7aec0

Browse files
committed
Angular Security course
1 parent 5d91f15 commit 4f7aec0

13 files changed

+136
-45
lines changed

server/create-user.route.ts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,8 @@ import {USERS} from "./database-data";
44
import * as argon2 from 'argon2';
55
import {validatePassword} from "./password-validation";
66
import {sessionStore} from "./session-storage";
7+
import {initializeUserSession, randomBytes} from "./security.utils";
78

8-
const util = require('util');
9-
const crypto = require('crypto');
10-
11-
const randomBytes = util.promisify(crypto.randomBytes);
129

1310

1411
export function createUser(req: Request, res: Response) {
@@ -31,23 +28,13 @@ export function createUser(req: Request, res: Response) {
3128

3229
async function createUserAndSession(res: Response, credentials) {
3330

34-
const sessionId = await randomBytes(32).then(bytes => bytes.toString('hex'));
35-
36-
console.log("sessionId", sessionId);
37-
3831
const passwordDigest = await argon2.hash(credentials.password);
3932

4033
console.log("passwordDigest", passwordDigest);
4134

4235
const user = db.createUser(credentials.email, passwordDigest);
4336

44-
sessionStore.createSession(sessionId, user);
45-
46-
console.log(USERS);
47-
48-
res.cookie("SESSIONID", sessionId, {httpOnly: true, secure: true});
49-
50-
res.status(200).json({id: user.id, email: user.email});
37+
return initializeUserSession(user, res);
5138

5239
}
5340

server/database.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import * as _ from 'lodash';
33
import {LESSONS, USERS} from "./database-data";
44
import {DbUser} from "./db-user";
5-
import {User} from "../src/app/model/user";
65

76

87
class InMemoryDatabase {
@@ -13,7 +12,6 @@ class InMemoryDatabase {
1312
return _.values(LESSONS);
1413
}
1514

16-
1715
createUser(email:string,passwordDigest:string) {
1816

1917
const usersPerEmail = _.keyBy( _.values(USERS), "email" );
@@ -36,11 +34,24 @@ class InMemoryDatabase {
3634

3735
USERS[id] = user;
3836

37+
console.log(USERS);
38+
3939
return user;
4040
}
4141

4242

43+
findUserByEmail(email:string) :DbUser {
44+
45+
const users = _.values(USERS);
46+
47+
return _.find(users, user => user.email === email);
48+
}
4349

4450
}
4551

46-
export const db = new InMemoryDatabase();
52+
53+
54+
55+
export const db = new InMemoryDatabase();
56+
57+

server/get-user.route.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,4 @@ export function getUser(req:Request, res:Response) {
1717
res.sendStatus(204);
1818
}
1919

20-
21-
22-
23-
2420
}

server/login.route.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
import {Request, Response} from "express";
3+
import {db} from "./database";
4+
import * as argon2 from 'argon2';
5+
import {DbUser} from "./db-user";
6+
import {initializeUserSession, randomBytes} from "./security.utils";
7+
import {sessionStore} from "./session-storage";
8+
import {User} from "../src/app/model/user";
9+
10+
11+
export function login(req: Request, res: Response) {
12+
13+
const credentials = req.body;
14+
15+
const user = db.findUserByEmail(credentials.email);
16+
17+
if (!user) {
18+
res.sendStatus(403);
19+
}
20+
else {
21+
22+
validatePassword(user, credentials.password)
23+
.then(isPasswordValid => {
24+
25+
if (isPasswordValid) {
26+
initializeUserSession(user, res);
27+
}
28+
else {
29+
res.sendStatus(403);
30+
}
31+
});
32+
33+
}
34+
35+
}
36+
37+
38+
async function validatePassword(user: DbUser, password:string) {
39+
return argon2.verify(user.passwordDigest, password);
40+
}
41+
42+
43+

server/security.utils.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
2+
3+
import {User} from "../src/app/model/user";
4+
import {sessionStore} from "./session-storage";
5+
const util = require('util');
6+
const crypto = require('crypto');
7+
import {Response} from "express";
8+
9+
10+
11+
export const randomBytes = util.promisify(crypto.randomBytes);
12+
13+
14+
15+
16+
export async function initializeUserSession(user:User, res:Response) {
17+
18+
const sessionId = await randomBytes(32).then(bytes => bytes.toString('hex'));
19+
console.log("sessionId", sessionId);
20+
21+
sessionStore.createSession(sessionId, user);
22+
23+
res.cookie("SESSIONID", sessionId, {httpOnly: true, secure: true});
24+
25+
res.status(200).json({id: user.id, email: user.email});
26+
}
27+

server/server.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {readAllLessons} from "./read-all-lessons.route";
88
import {createUser} from "./create-user.route";
99
import {getUser} from "./get-user.route";
1010
import {logout} from "./logout.route";
11+
import {login} from "./login.route";
1112
const bodyParser = require('body-parser');
1213
const cookieParser = require('cookie-parser');
1314

@@ -39,7 +40,8 @@ app.route('/api/user')
3940
app.route('/api/logout')
4041
.post(logout);
4142

42-
43+
app.route('/api/login')
44+
.post(login);
4345

4446
if (options.secure) {
4547

server/session-storage.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,6 @@ class SessionStore {
6161

6262
}
6363

64-
65-
66-
6764
export const sessionStore = new SessionStore();
6865

6966

src/app/lessons/lessons.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

2-
<h2>All Lessons</h2>
2+
<div class="lessons-list-container v-h-center-block-parent" *ngIf="isLoggedIn$ | async">
33

4-
<div class="lessons-list-container v-h-center-block-parent">
4+
<h2>All Lessons</h2>
55

66
<table class="table lessons-list card card-strong">
77
<tbody>

src/app/lessons/lessons.component.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
1-
import { Component, OnInit } from '@angular/core';
1+
import {Component, OnInit} from '@angular/core';
22
import {LessonsService} from "../services/lessons.service";
33
import {Observable} from "rxjs/Observable";
44
import {Lesson} from "../model/lesson";
5+
import {AuthService} from "../services/auth.service";
56

67
@Component({
7-
selector: 'lessons',
8-
templateUrl: './lessons.component.html',
9-
styleUrls: ['./lessons.component.css']
8+
selector: 'lessons',
9+
templateUrl: './lessons.component.html',
10+
styleUrls: ['./lessons.component.css']
1011
})
1112
export class LessonsComponent implements OnInit {
1213

13-
lessons$: Observable<Lesson[]>;
14+
lessons$: Observable<Lesson[]>;
15+
isLoggedIn$: Observable<boolean>;
1416

15-
constructor(private lessonsService:LessonsService) {
17+
constructor(private lessonsService: LessonsService, private authService: AuthService) {
1618

17-
}
19+
}
1820

19-
ngOnInit() {
20-
this.lessons$ = this.lessonsService.loadAllLessons().catch(err => Observable.of([]));
21-
}
21+
ngOnInit() {
22+
this.lessons$ = this.lessonsService.loadAllLessons().catch(err => Observable.of([]));
23+
this.isLoggedIn$ = this.authService.isLoggedIn$;
24+
}
2225

2326
}

src/app/login/login.component.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
2+
<div class="messages messages-error" *ngIf="errors?.length">
3+
<div class="messages-container">
4+
<div *ngFor="let error of errors">{{messagePerErrorCode[error]}}</div>
5+
</div>
6+
</div>
7+
18
<form autocomplete="off" novalidate [formGroup]="form">
29

310
<fieldset>

src/app/login/login.component.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Component, OnInit } from '@angular/core';
22
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
33
import {AuthService} from "../services/auth.service";
4+
import {Router} from "@angular/router";
45

56
@Component({
67
selector: 'login',
@@ -11,7 +12,13 @@ export class LoginComponent implements OnInit {
1112

1213
form:FormGroup;
1314

14-
constructor(private fb:FormBuilder, private authService: AuthService) {
15+
messagePerErrorCode = {
16+
loginfailed: "Invalid credentials"
17+
};
18+
19+
errors = [];
20+
21+
constructor(private fb:FormBuilder, private authService: AuthService, private router: Router) {
1522

1623
this.form = this.fb.group({
1724
email: ['',Validators.required],
@@ -22,17 +29,24 @@ export class LoginComponent implements OnInit {
2229

2330
ngOnInit() {
2431

25-
26-
2732
}
2833

29-
3034
login() {
3135

32-
const formValue = this.form.value;
36+
const val = this.form.value;
3337

38+
if (val.email && val.password) {
3439

40+
this.authService.login(val.email, val.password)
41+
.subscribe(
42+
() => {
43+
console.log("User logged in successfully");
44+
this.router.navigateByUrl('/');
45+
},
46+
response => this.errors = response.error.errors
47+
);
3548

49+
}
3650

3751

3852
}

src/app/services/auth.service.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ export class AuthService {
3232
.do(user => this.subject.next(user));
3333
}
3434

35+
login(email: string, password: string) {
36+
return this.http.post<User>('/api/login', {email, password})
37+
.shareReplay()
38+
.do(user => this.subject.next(user));
39+
}
40+
3541
logout(): Observable<any> {
3642
return this.http.post('/api/logout', null)
3743
.shareReplay()

src/app/signup/signup.component.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,11 @@ export class SignupComponent implements OnInit {
2323

2424

2525
constructor(private fb: FormBuilder, private authService: AuthService, private router:Router) {
26-
2726
this.form = this.fb.group({
2827
email: ['',Validators.required],
2928
password: ['',Validators.required],
3029
confirm: ['',Validators.required]
3130
});
32-
3331
}
3432

3533

0 commit comments

Comments
 (0)