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-dynamic": "^16.2.0",
"@angular/router": "^16.2.0",
"@types/jwt-decode": "^3.1.0",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"express": "^4.18.2",
"express-jwt": "^8.4.1",
"jsonwebtoken": "^9.0.2",
"jwt-decode": "^4.0.0",
"moment": "^2.29.4",
"mongoose": "^8.0.0",
"multer": "^1.4.5-lts.1",
"node-jsonwebtoken": "^0.0.1",
"nodemon": "^3.0.1",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",

196
server.js

@ -1,136 +1,124 @@
//import {Request, Response} from "express";
const express = require('express');
const fs = require('fs');
const app = express();
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
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 gbxHeader = require('./backend/trackmania-replays/gbx-header.js');
// handling CORS
app.use((req, res, next) => {
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(cors());
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 checkIfAuthenticated = expressJwt({
// secret: RSA_PUBLIC_KEY
//});
/*export */function loginRoute(req, res) {
const users = [
{ id: 1, username: 'user1', password: 'password1' },
{ id: 2, username: 'user2', password: 'password2' }
];
const email = req.body.email
const password = req.body.password;
const seasons = [
{ id: 1, seasonName: "Season 1", seasonTag: "Post Winter Blues", seasonCardImage: "", seasonHeaderImage: "", seasonStartDate: "", seasonEndDate: "", seasonDesc: "",},
{ id: 2, seasonName: "Season 2", seasonTag: "Post Winter Blues", seasonCardImage: "", seasonHeaderImage: "", seasonStartDate: "", seasonEndDate: "", seasonDesc: "",}
]
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 upload = require('./backend/routes/upload-replay');
app.get('/api/seasons', (req, res) => {
seasons = [{
const season_details = {
details: {
id: 1,
seasonName: "Season 1",
seasonTag: "Post Winter Blues",
seasonCardImage: "",
seasonHeaderImage: "",
seasonStartDate: "",
seasonEndDate: "",
seasonSendDate: "",
seasonId: "",
seasonDesc: "",
},
standings: [{
position: 1,
points: 4,
user: {
realName: "Dan H",
gamerHandle: "Quildra",
}
},
{
id: 2,
seasonName: "Season 2",
seasonTag: "Post Winter Blues",
seasonCardImage: "",
seasonHeaderImage: "",
seasonStartDate: "",
seasonEndDate: "",
seasonDesc: "",
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",
}
}
]
}]
}
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});
});
app.get('/api/seasons/:id', (req, res) => {
data = {
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});
res.json({data: season_details});
});
// 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 { SeasonsComponent } from './components/seasons/seasons.component';
import { SeasonDetailsComponent } from './components/season-details/season-details.component';
import { LoginComponent } from './components/login/login.component';
const routes: Routes = [
{ path: '', redirectTo: '/seasons', pathMatch: 'full' },
{ path: 'seasons', component: SeasonsComponent },
{ path: 'seasons/:id', component: SeasonDetailsComponent },
{ path: 'login', component: LoginComponent },
];
@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 { NewSeasonCardComponent } from './components/new-season-card/new-season-card.component';
import { NewSeasonDialogComponent } from './components/new-season-dialog/new-season-dialog.component';
import { MatMenuModule } from '@angular/material/menu';
import { FormsModule } from '@angular/forms';
@NgModule({
declarations: [
@ -60,6 +62,8 @@ import { NewSeasonDialogComponent } from './components/new-season-dialog/new-sea
MatTabsModule,
ReactiveFormsModule,
ObserversModule,
FormsModule,
MatMenuModule,
HttpClientModule
],
providers: [],

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

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

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

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

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

@ -2,5 +2,5 @@
<ng-container *ngFor="let season of seasons">
<app-season-card [season]="season" class="grid-item"></app-season-card>
</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>

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

@ -12,7 +12,7 @@ import { AuthService } from 'src/app/services/auth-service';
export class SeasonsComponent
{
seasons: Season[];
isLoggedIn: boolean = false;
isAuthenticated: boolean = false;
constructor(private apiService: ApiService,
private authService: AuthService)
@ -24,6 +24,6 @@ export class SeasonsComponent
this.apiService.getSeasons().subscribe(data => {
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>
<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>
</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 class="example-spacer"></span>
<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 { AuthService } from 'src/app/services/auth-service';
@Component({
@ -7,5 +8,17 @@ import { Component } from '@angular/core';
styleUrls: ['./top-bar.component.less']
})
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 { HttpClient } from '@angular/common/http';
import { shareReplay, tap } from 'rxjs/operators'
import { Observable } from 'rxjs';
import * as moment from "moment";
import { jwtDecode } from "jwt-decode";
@Injectable({
providedIn: 'root'
@ -10,15 +12,45 @@ import * as moment from "moment";
export class AuthService
{
constructor(private http: HttpClient) { }
private apiUrl = 'http://localhost:3000/api';
login(email: string, password: string)
{
return this.http.post('/api/login', {email, password}).pipe(
tap( res => this.setSession ),
shareReplay()
);
login(username: string, password: string): Observable<{ token: string }> {
return this.http.post<{ token: string }>(`${this.apiUrl}/login`, { username, password });
}
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) {
const expiresAt = moment().add((authResult as any).expiresIn,'second');
@ -26,10 +58,10 @@ export class AuthService
localStorage.setItem("expires_at", JSON.stringify(expiresAt.valueOf()) );
}
logout() {
localStorage.removeItem("id_token");
localStorage.removeItem("expires_at");
}
//logout() {
// localStorage.removeItem("id_token");
// localStorage.removeItem("expires_at");
//}
public isLoggedIn() {
return true;

Loading…
Cancel
Save