Browse Source

Improved auth service and login

old-project-state
Quildra 2 years ago
parent
commit
a3c7152b36
  1. 1109
      package-lock.json
  2. 6
      package.json
  3. 196
      server.js
  4. 2
      src/app/app-routing.module.ts
  5. 4
      src/app/app.module.ts
  6. 30
      src/app/components/login/login.component.html
  7. 30
      src/app/components/login/login.component.ts
  8. 2
      src/app/components/seasons/seasons.component.html
  9. 4
      src/app/components/seasons/seasons.component.ts
  10. 6
      src/app/components/top-bar/top-bar.component.html
  11. 13
      src/app/components/top-bar/top-bar.component.ts
  12. 52
      src/app/services/auth-service.ts

1109
package-lock.json

File diff suppressed because it is too large

6
package.json

@ -21,13 +21,15 @@
"@angular/platform-browser": "^16.2.0", "@angular/platform-browser": "^16.2.0",
"@angular/platform-browser-dynamic": "^16.2.0", "@angular/platform-browser-dynamic": "^16.2.0",
"@angular/router": "^16.2.0", "@angular/router": "^16.2.0",
"@types/jwt-decode": "^3.1.0",
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"express": "^4.18.2", "express": "^4.18.2",
"express-jwt": "^8.4.1", "jsonwebtoken": "^9.0.2",
"jwt-decode": "^4.0.0",
"moment": "^2.29.4", "moment": "^2.29.4",
"mongoose": "^8.0.0", "mongoose": "^8.0.0",
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"node-jsonwebtoken": "^0.0.1",
"nodemon": "^3.0.1", "nodemon": "^3.0.1",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",

196
server.js

@ -1,136 +1,124 @@
//import {Request, Response} from "express";
const express = require('express'); const express = require('express');
const fs = require('fs');
const app = express();
const bodyParser = require('body-parser'); const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const jwt = require('jsonwebtoken'); const jwt = require('jsonwebtoken');
const expressJwt = require('express-jwt'); const cors = require('cors');
const fs = require('fs');
const app = express();
const upload = require('./backend/routes/upload-replay');
const AdvancableBuffer = require('./backend/utilities/AdvancableBuffer.js'); const AdvancableBuffer = require('./backend/utilities/AdvancableBuffer.js');
const gbxHeader = require('./backend/trackmania-replays/gbx-header.js'); const gbxHeader = require('./backend/trackmania-replays/gbx-header.js');
// handling CORS // handling CORS
app.use((req, res, next) => { app.use(cors());
res.header("Access-Control-Allow-Origin",
"http://localhost:4200");
res.header("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.use(bodyParser.json()); app.use(bodyParser.json());
app.route('/api/login').post(loginRoute);
const RSA_PRIVATE_KEY = fs.readFileSync('./private.key'); //const RSA_PRIVATE_KEY = fs.readFileSync('./private.key');
const RSA_PRIVATE_KEY = "Secret_KeY";
const RSA_PUBLIC_KEY = fs.readFileSync('./public.key'); const RSA_PUBLIC_KEY = fs.readFileSync('./public.key');
//const checkIfAuthenticated = expressJwt({ const users = [
// secret: RSA_PUBLIC_KEY { id: 1, username: 'user1', password: 'password1' },
//}); { id: 2, username: 'user2', password: 'password2' }
];
/*export */function loginRoute(req, res) { const seasons = [
{ id: 1, seasonName: "Season 1", seasonTag: "Post Winter Blues", seasonCardImage: "", seasonHeaderImage: "", seasonStartDate: "", seasonEndDate: "", seasonDesc: "",},
const email = req.body.email { id: 2, seasonName: "Season 2", seasonTag: "Post Winter Blues", seasonCardImage: "", seasonHeaderImage: "", seasonStartDate: "", seasonEndDate: "", seasonDesc: "",}
const password = req.body.password; ]
if (validateEmailAndPassword(email, password))
{
const userId = findUserIdForEmail(email);
const jwtBearerToken = jwt.sign({}, RSA_PRIVATE_KEY, {
algorithm: 'RS256',
expiresIn: 120,
subject: userId
});
res.status(200).json({
idToken: jwtBearerToken,
expiresIn: 120
});
}
else {
// send status 401 Unauthorized
res.sendStatus(401);
}
}
//app.route('/api/session/create').get(checkIfAuthenticated, ) const season_details = {
details: {
const upload = require('./backend/routes/upload-replay');
app.get('/api/seasons', (req, res) => {
seasons = [{
id: 1, id: 1,
seasonName: "Season 1", seasonName: "Season 1",
seasonTag: "Post Winter Blues", seasonTag: "Post Winter Blues",
seasonCardImage: "", seasonCardImage: "",
seasonHeaderImage: "", seasonHeaderImage: "",
seasonStartDate: "", seasonStartDate: "",
seasonEndDate: "", seasonSendDate: "",
seasonId: "",
seasonDesc: "", seasonDesc: "",
}, },
standings: [{
position: 1,
points: 4,
user: {
realName: "Dan H",
gamerHandle: "Quildra",
}
},
{ {
id: 2, position: 2,
seasonName: "Season 2", points: 2,
seasonTag: "Post Winter Blues", user: {
seasonCardImage: "", realName: "Dan Mc",
seasonHeaderImage: "", gamerHandle: "Mini-Quildra",
seasonStartDate: "", }
seasonEndDate: "", },
seasonDesc: "", ],
weeks:[{
id: "1",
map: "bob",
mapImg: "bob.jpg",
entries: [
{
position: 1,
runTime: 4.0,
user: {
realName: "Dan H",
gamerHandle: "Quildra",
}
}
]
}] }]
}
function verifyToken(req, res, next) {
const token = req.headers['authorization'];
if (!token) {
return res.status(403).json({ message: 'Token not provided' });
}
jwt.verify(token, RSA_PRIVATE_KEY, (err, decoded) => {
if (err) {
return res.status(401).json({ message: 'Failed to authenticate token' });
}
req.userId = decoded.userId;
next();
});
}
app.post('/api/login', (req, res) => {
const { username, password } = req.body;
// Check if the user exists and the password is correct
const user = users.find(u => u.username === username && u.password === password);
if (!user) {
return res.status(401).json({ message: 'Invalid username or password' });
}
// Generate and return a JWT token
const token = jwt.sign({ userId: user.id, username: user.username }, RSA_PRIVATE_KEY, { expiresIn: '1h' });
res.json({ token });
});
// Protected route example
app.get('/api/profile', verifyToken, (req, res) => {
const user = users.find(u => u.id === req.userId);
res.json({ username: user.username, userId: user.id });
});
app.get('/api/seasons', (req, res) => {
res.json({seasons: seasons}); res.json({seasons: seasons});
}); });
app.get('/api/seasons/:id', (req, res) => { app.get('/api/seasons/:id', (req, res) => {
data = { res.json({data: season_details});
details: {
id: 1,
seasonName: "Season 1",
seasonTag: "Post Winter Blues",
seasonCardImage: "",
seasonHeaderImage: "",
seasonStartDate: "",
seasonSendDate: "",
seasonId: "",
seasonDesc: "",
},
standings: [{
position: 1,
points: 4,
user: {
realName: "Dan H",
gamerHandle: "Quildra",
}
},
{
position: 2,
points: 2,
user: {
realName: "Dan Mc",
gamerHandle: "Mini-Quildra",
}
},
],
weeks:[{
id: "1",
map: "bob",
mapImg: "bob.jpg",
entries: [
{
position: 1,
runTime: 4.0,
user: {
realName: "Dan H",
gamerHandle: "Quildra",
}
}
]
}]
}
res.json({data: data});
}); });
// route for handling requests from the Angular client // route for handling requests from the Angular client

2
src/app/app-routing.module.ts

@ -2,11 +2,13 @@ import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { SeasonsComponent } from './components/seasons/seasons.component'; import { SeasonsComponent } from './components/seasons/seasons.component';
import { SeasonDetailsComponent } from './components/season-details/season-details.component'; import { SeasonDetailsComponent } from './components/season-details/season-details.component';
import { LoginComponent } from './components/login/login.component';
const routes: Routes = [ const routes: Routes = [
{ path: '', redirectTo: '/seasons', pathMatch: 'full' }, { path: '', redirectTo: '/seasons', pathMatch: 'full' },
{ path: 'seasons', component: SeasonsComponent }, { path: 'seasons', component: SeasonsComponent },
{ path: 'seasons/:id', component: SeasonDetailsComponent }, { path: 'seasons/:id', component: SeasonDetailsComponent },
{ path: 'login', component: LoginComponent },
]; ];
@NgModule({ @NgModule({

4
src/app/app.module.ts

@ -28,6 +28,8 @@ import { ObserversModule } from '@angular/cdk/observers';
import { AdminComponent } from './components/admin/admin.component'; import { AdminComponent } from './components/admin/admin.component';
import { NewSeasonCardComponent } from './components/new-season-card/new-season-card.component'; import { NewSeasonCardComponent } from './components/new-season-card/new-season-card.component';
import { NewSeasonDialogComponent } from './components/new-season-dialog/new-season-dialog.component'; import { NewSeasonDialogComponent } from './components/new-season-dialog/new-season-dialog.component';
import { MatMenuModule } from '@angular/material/menu';
import { FormsModule } from '@angular/forms';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -60,6 +62,8 @@ import { NewSeasonDialogComponent } from './components/new-season-dialog/new-sea
MatTabsModule, MatTabsModule,
ReactiveFormsModule, ReactiveFormsModule,
ObserversModule, ObserversModule,
FormsModule,
MatMenuModule,
HttpClientModule HttpClientModule
], ],
providers: [], providers: [],

30
src/app/components/login/login.component.html

@ -1,18 +1,12 @@
<form [formGroup]="form"> <div>
<fieldset> <h2>Login</h2>
<legend>Login</legend> <form (submit)="login()">
<div class="form-field"> <label for="username">Username: </label>
<label>Email:</label> <input type="text" id="username" [(ngModel)]="username" name="username" required>
<input name="email" formControlName="email">
</div> <label for="password">Password: </label>
<div class="form-field"> <input type="password" id="password" [(ngModel)]="password" name="password" required>
<label>Password:</label>
<input name="password" formControlName="password" <button type="submit">Login</button>
type="password"> </form>
</div> </div>
</fieldset>
<div class="form-buttons">
<button class="button button-primary"
(click)="login()">Login</button>
</div>
</form>

30
src/app/components/login/login.component.ts

@ -9,30 +9,24 @@ import { AuthService } from '../../services/auth-service';
styleUrls: ['./login.component.less'] styleUrls: ['./login.component.less']
}) })
export class LoginComponent { export class LoginComponent {
form:FormGroup;
username: string = '';
password: string = '';
constructor(private fb:FormBuilder, constructor(private fb:FormBuilder,
private authService: AuthService, private authService: AuthService,
private router: Router) private router: Router)
{ {}
this.form = this.fb.group({
email: ['',Validators.required],
password: ['',Validators.required]
});
}
login() { login(): void {
const val = this.form.value; this.authService.login(this.username, this.password).subscribe(
(response) => {
if (val.email && val.password) { if (response.token) {
this.authService.login(val.email, val.password) localStorage.setItem('token', response.token);
.subscribe( this.router.navigate(['/profile']);
() => {
console.log("User is logged in");
this.router.navigateByUrl('/');
} }
},
(error) => console.error(error)
); );
} }
}
} }

2
src/app/components/seasons/seasons.component.html

@ -2,5 +2,5 @@
<ng-container *ngFor="let season of seasons"> <ng-container *ngFor="let season of seasons">
<app-season-card [season]="season" class="grid-item"></app-season-card> <app-season-card [season]="season" class="grid-item"></app-season-card>
</ng-container> </ng-container>
<app-new-season-card *ngIf="isLoggedIn" class="grid-item"></app-new-season-card> <app-new-season-card *ngIf="isAuthenticated" class="grid-item"></app-new-season-card>
</div> </div>

4
src/app/components/seasons/seasons.component.ts

@ -12,7 +12,7 @@ import { AuthService } from 'src/app/services/auth-service';
export class SeasonsComponent export class SeasonsComponent
{ {
seasons: Season[]; seasons: Season[];
isLoggedIn: boolean = false; isAuthenticated: boolean = false;
constructor(private apiService: ApiService, constructor(private apiService: ApiService,
private authService: AuthService) private authService: AuthService)
@ -24,6 +24,6 @@ export class SeasonsComponent
this.apiService.getSeasons().subscribe(data => { this.apiService.getSeasons().subscribe(data => {
this.seasons = (data as any).seasons; this.seasons = (data as any).seasons;
}); });
this.isLoggedIn = this.authService.isLoggedIn(); this.isAuthenticated = this.authService.isAuthenticated();
} }
} }

6
src/app/components/top-bar/top-bar.component.html

@ -1,7 +1,11 @@
<mat-toolbar> <mat-toolbar>
<button mat-icon-button class="example-icon" aria-label="Example icon-button with menu icon"> <button mat-icon-button class="example-icon" aria-label="Example icon-button with menu icon" [matMenuTriggerFor]="menu">
<mat-icon>menu</mat-icon> <mat-icon>menu</mat-icon>
</button> </button>
<mat-menu #menu="matMenu">
<button *ngIf="!isAuthenticated" mat-menu-item routerLink="/login">Log In</button>
<button *ngIf="isAuthenticated" mat-menu-item (click)="logOut()">Log Out</button>
</mat-menu>
<span>Stainless Trackmanaia Tournament Tracker</span> <span>Stainless Trackmanaia Tournament Tracker</span>
<span class="example-spacer"></span> <span class="example-spacer"></span>
<button mat-icon-button class="example-icon favorite-icon" aria-label="Example icon-button with heart icon"> <button mat-icon-button class="example-icon favorite-icon" aria-label="Example icon-button with heart icon">

13
src/app/components/top-bar/top-bar.component.ts

@ -1,4 +1,5 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { AuthService } from 'src/app/services/auth-service';
@Component({ @Component({
@ -7,5 +8,17 @@ import { Component } from '@angular/core';
styleUrls: ['./top-bar.component.less'] styleUrls: ['./top-bar.component.less']
}) })
export class TopBarComponent { export class TopBarComponent {
constructor(private authService: AuthService){}
isAuthenticated: boolean = false;
ngOnInit() {
this.isAuthenticated = this.authService.isAuthenticated();
}
logOut()
{
this.authService.logout();
window.location.reload();
}
} }

52
src/app/services/auth-service.ts

@ -1,7 +1,9 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { shareReplay, tap } from 'rxjs/operators' import { shareReplay, tap } from 'rxjs/operators'
import { Observable } from 'rxjs';
import * as moment from "moment"; import * as moment from "moment";
import { jwtDecode } from "jwt-decode";
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -10,15 +12,45 @@ import * as moment from "moment";
export class AuthService export class AuthService
{ {
constructor(private http: HttpClient) { } constructor(private http: HttpClient) { }
private apiUrl = 'http://localhost:3000/api';
login(email: string, password: string) login(username: string, password: string): Observable<{ token: string }> {
{ return this.http.post<{ token: string }>(`${this.apiUrl}/login`, { username, password });
return this.http.post('/api/login', {email, password}).pipe(
tap( res => this.setSession ),
shareReplay()
);
} }
isAuthenticated(): boolean {
const token = localStorage.getItem('token');
if (!token) {
return false;
}
try {
const decoded: any = jwtDecode(token);
// Check if the token is expired
const isTokenExpired = decoded.exp < Date.now() / 1000;
return !isTokenExpired;
} catch (error) {
console.error('Error decoding JWT:', error);
return false;
}
}
logout(): void {
// Clear the token from local storage
localStorage.removeItem('token');
}
//login(email: string, password: string)
//{
// return this.http.post('/api/login', {email, password}).pipe(
// tap( res => this.setSession ),
// shareReplay()
// );
//}
private setSession(authResult: any) { private setSession(authResult: any) {
const expiresAt = moment().add((authResult as any).expiresIn,'second'); const expiresAt = moment().add((authResult as any).expiresIn,'second');
@ -26,10 +58,10 @@ export class AuthService
localStorage.setItem("expires_at", JSON.stringify(expiresAt.valueOf()) ); localStorage.setItem("expires_at", JSON.stringify(expiresAt.valueOf()) );
} }
logout() { //logout() {
localStorage.removeItem("id_token"); // localStorage.removeItem("id_token");
localStorage.removeItem("expires_at"); // localStorage.removeItem("expires_at");
} //}
public isLoggedIn() { public isLoggedIn() {
return true; return true;

Loading…
Cancel
Save