Skip to content

Commit b330246

Browse files
committed
Angular Security course
1 parent 78e52ad commit b330246

12 files changed

+88
-19
lines changed

package-lock.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"command-line-args": "^4.0.6",
3838
"cookie-parser": "^1.4.3",
3939
"core-js": "^2.4.1",
40+
"ngx-cookie": "^1.0.0",
4041
"nodemon": "^1.11.0",
4142
"password-validator": "^4.0.0",
4243
"rxjs": "^5.1.0",

server/constants.ts

Lines changed: 0 additions & 3 deletions
This file was deleted.

server/create-user.route.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {db} from "./database";
33
import {USERS} from "./database-data";
44
import * as argon2 from 'argon2';
55
import {validatePassword} from "./password-validation";
6-
import {AUTH_COOKIE_NAME} from "./constants";
76

87
const util = require('util');
98
const crypto = require('crypto');
@@ -39,11 +38,11 @@ async function createUserAndSession(res: Response, credentials) {
3938

4039
console.log("passwordDigest", passwordDigest);
4140

42-
const user = db.createUser(credentials.email, passwordDigest);
41+
const user = db.createUser(credentials.email, passwordDigest, sessionId);
4342

4443
console.log(USERS);
4544

46-
res.cookie(AUTH_COOKIE_NAME, sessionId, {httpOnly: true, secure: true});
45+
res.cookie("SESSIONID", sessionId, {httpOnly: true, secure: true});
4746

4847
res.status(200).json({id: user.id, email: user.email});
4948

server/database-data.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import {DbUser} from "./db-user";
55
export const USERS: {[key:number]:DbUser} = {};
66

77

8+
export const SESSIONS: {[key:number]: number} = {};
9+
10+
811
export const LESSONS = {
912

1013
1: {

server/database.ts

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

22
import * as _ from 'lodash';
3-
import {LESSONS, USERS} from "./database-data";
3+
import {LESSONS, SESSIONS, USERS} from "./database-data";
44
import {DbUser} from "./db-user";
5+
import {User} from "../src/app/model/user";
56

67

78
class InMemoryDatabase {
@@ -13,7 +14,7 @@ class InMemoryDatabase {
1314
}
1415

1516

16-
createUser(email:string,passwordDigest:string) {
17+
createUser(email:string,passwordDigest:string, sessionId:string) {
1718

1819
const usersPerEmail = _.keyBy( _.values(USERS), "email" );
1920

@@ -35,6 +36,22 @@ class InMemoryDatabase {
3536

3637
USERS[id] = user;
3738

39+
SESSIONS[sessionId] = id;
40+
41+
return user;
42+
}
43+
44+
45+
findUserbySession(sessionId:string) : User {
46+
47+
let user;
48+
49+
const userId = SESSIONS[sessionId];
50+
51+
if (userId) {
52+
user = {id: userId, email: USERS[userId].email};
53+
}
54+
3855
return user;
3956
}
4057

server/get-user.route.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
import {Request, Response} from "express";
3+
import {db} from "./database";
4+
5+
6+
7+
export function getUser(req:Request, res:Response) {
8+
9+
const sessionId = req.cookies['SESSIONID'];
10+
11+
const user = db.findUserbySession(sessionId);
12+
13+
if (user) {
14+
res.status(200).send(user);
15+
}
16+
else {
17+
res.sendStatus(204);
18+
}
19+
20+
21+
22+
23+
24+
}

server/server.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as fs from 'fs';
66
import * as https from 'https';
77
import {readAllLessons} from "./read-all-lessons.route";
88
import {createUser} from "./create-user.route";
9+
import {getUser} from "./get-user.route";
910
const bodyParser = require('body-parser');
1011
const cookieParser = require('cookie-parser');
1112

@@ -31,6 +32,9 @@ app.route('/api/lessons')
3132
app.route('/api/signup')
3233
.post(createUser);
3334

35+
app.route('/api/user')
36+
.get(getUser);
37+
3438

3539
if (options.secure) {
3640

src/app/app.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<li *ngIf="isLoggedOut$ | async">
1818
<a routerLink="/login">Login</a>
1919
</li>
20-
<li *ngIf="isLoggedIn$ | async">
20+
<li *ngIf="isLoggedIn$ | async" (click)="logout()">
2121
<a>Logout</a>
2222
</li>
2323

src/app/app.component.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,10 @@ export class AppComponent implements OnInit{
2828

2929

3030

31+
logout() {
32+
33+
this.authService.logout();
34+
35+
}
36+
3137
}

src/app/app.module.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ import {routesConfig} from "./routes.config";
1111
import {LessonsService} from "./services/lessons.service";
1212
import {ReactiveFormsModule} from "@angular/forms";
1313

14+
import {AuthService} from "./services/auth.service";
15+
import {CookieModule} from "ngx-cookie";
16+
1417
import 'rxjs/add/operator/switchMap';
1518
import 'rxjs/add/operator/map';
1619
import 'rxjs/add/operator/shareReplay';
1720
import 'rxjs/add/operator/do';
18-
19-
import {AuthService} from "./services/auth.service";
20-
21+
import 'rxjs/add/operator/filter';
2122

2223

2324
@NgModule({
@@ -31,7 +32,8 @@ import {AuthService} from "./services/auth.service";
3132
BrowserModule,
3233
HttpClientModule,
3334
RouterModule.forRoot(routesConfig),
34-
ReactiveFormsModule
35+
ReactiveFormsModule,
36+
CookieModule.forRoot()
3537
],
3638
providers: [LessonsService, AuthService],
3739
bootstrap: [AppComponent]

src/app/services/auth.service.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {HttpClient} from "@angular/common/http";
33
import {Observable} from "rxjs/Observable";
44
import {User} from "../model/user";
55
import {BehaviorSubject} from "rxjs/BehaviorSubject";
6+
import {CookieService} from "ngx-cookie";
67

78
export const ANONYMOUS_USER : User = {
89
id: undefined,
@@ -13,26 +14,36 @@ export const ANONYMOUS_USER : User = {
1314
@Injectable()
1415
export class AuthService {
1516

16-
private subject = new BehaviorSubject<User>(ANONYMOUS_USER);
17+
private subject = new BehaviorSubject<User>(undefined);
1718

18-
user$: Observable<User> = this.subject.asObservable();
19+
user$: Observable<User> = this.subject.asObservable().filter(user => !!user);
1920

20-
isLoggedIn$: Observable<boolean> = this.user$.map(user => !!user.id);
21+
isLoggedIn$: Observable<boolean> = this.user$.map(user => !!user.id);
2122

22-
isLoggedOut$: Observable<boolean> = this.isLoggedIn$.map(isLoggedIn => !isLoggedIn);
23+
isLoggedOut$: Observable<boolean> = this.isLoggedIn$.map(isLoggedIn => !isLoggedIn);
2324

2425

25-
constructor(private http: HttpClient) {
26+
constructor(private http: HttpClient, private cookieService: CookieService) {
2627

28+
http.get<User>('/api/user')
29+
.subscribe(user => this.subject.next(user ? user: ANONYMOUS_USER));
2730

2831
}
2932

3033

3134
signUp(email:string, password:string ) {
32-
3335
return this.http.post<User>('/api/signup', {email, password})
3436
.shareReplay()
3537
.do(user => this.subject.next(user));
38+
}
39+
40+
41+
42+
logout() {
43+
44+
this.cookieService.remove('SESSIONID');
45+
46+
this.subject.next(ANONYMOUS_USER);
3647

3748
}
3849

0 commit comments

Comments
 (0)