added session token to database

added tokenvalidation
This commit is contained in:
Marcus Ferl 2024-03-04 23:00:10 +01:00
parent 23588bf663
commit 524de9bd98
12 changed files with 251 additions and 14 deletions

View File

@ -13,6 +13,7 @@ namespace Weifer.Database.EF.Entitys
public string LastName { get; set; }
public string Email { get; set; }
public string PasswordHash { get; set; }
public string SessionToken { get; set; }
public DateTime CreatedOn { get; set; }
public ICollection<ShoppingList> ShoppingLists { get; set; }
}

View File

@ -0,0 +1,138 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Weifer.Database.EF;
#nullable disable
namespace Weifer.Database.EF.Migrations
{
[DbContext(typeof(DatabaseContext))]
[Migration("20240304193908_added_sessiontoken")]
partial class added_sessiontoken
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Weifer.Database.EF.Entitys.Customer", b =>
{
b.Property<Guid>("CustomerId")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTime>("CreatedOn")
.HasColumnType("datetime2");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("SessionToken")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("CustomerId");
b.ToTable("Customers");
});
modelBuilder.Entity("Weifer.Database.EF.Entitys.ShoppingItem", b =>
{
b.Property<Guid>("ShoppingItemId")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("Number")
.HasColumnType("int");
b.Property<bool>("Purchased")
.HasColumnType("bit");
b.Property<Guid>("ShoppingListId")
.HasColumnType("uniqueidentifier");
b.HasKey("ShoppingItemId");
b.HasIndex("ShoppingListId");
b.ToTable("ShoppingItems");
});
modelBuilder.Entity("Weifer.Database.EF.Entitys.ShoppingList", b =>
{
b.Property<Guid>("ShoppingListId")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<Guid>("CustomerId")
.HasColumnType("uniqueidentifier");
b.Property<string>("ShoppingListName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("ShoppingListId");
b.HasIndex("CustomerId");
b.ToTable("ShoppingLists");
});
modelBuilder.Entity("Weifer.Database.EF.Entitys.ShoppingItem", b =>
{
b.HasOne("Weifer.Database.EF.Entitys.ShoppingList", null)
.WithMany("ShoppingItems")
.HasForeignKey("ShoppingListId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Weifer.Database.EF.Entitys.ShoppingList", b =>
{
b.HasOne("Weifer.Database.EF.Entitys.Customer", null)
.WithMany("ShoppingLists")
.HasForeignKey("CustomerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Weifer.Database.EF.Entitys.Customer", b =>
{
b.Navigation("ShoppingLists");
});
modelBuilder.Entity("Weifer.Database.EF.Entitys.ShoppingList", b =>
{
b.Navigation("ShoppingItems");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Weifer.Database.EF.Migrations
{
/// <inheritdoc />
public partial class added_sessiontoken : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "SessionToken",
table: "Customers",
type: "nvarchar(max)",
nullable: false,
defaultValue: "");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "SessionToken",
table: "Customers");
}
}
}

View File

@ -47,6 +47,10 @@ namespace Weifer.Database.EF.Migrations
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("SessionToken")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("CustomerId");
b.ToTable("Customers");

View File

@ -4,6 +4,7 @@
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>

View File

@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Primitives;
using Weifer.Database.EF;
using Weifer.ShoppingApp.API.Controllers;
using Weifer.ShoppingApp.API.Models;
@ -39,19 +40,39 @@ public class AuthenticationApiController : ControllerBase
{
var customer = await dbContext.Customers.Where(cu => cu.Email == credentials.Email).FirstOrDefaultAsync();
var token = authenticationController.GenerateJwtToken();
if (customer != null)
{
customer.SessionToken = token;
await dbContext.SaveChangesAsync();
}
return Ok(new
{
token = token, // Token Information
customer = new CustomerDto
{ // Kundeninformationen
{
CustomerId = customer.CustomerId,
FirstName = customer.FirstName,
LastName = customer.LastName,
Email = credentials.Email
}
});
}
return Unauthorized();
}
[HttpGet("validateToken")]
public async Task<IActionResult> ValidateToken()
{
var token = HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
var user = await dbContext.Customers.FirstOrDefaultAsync(x => x.SessionToken == token);
if (user != null)
{
return Ok();
}
return Unauthorized();
}
}

View File

@ -10,10 +10,7 @@
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Weifer.Database.EF\Weifer.Database.EF.csproj" />
<PackageReference Include="Weifer.Database.EF" Version="1.0.0" />
</ItemGroup>
<ItemGroup>

View File

@ -0,0 +1,5 @@
{
"version": "1.0",
"defaultProvider": "cdnjs",
"libraries": []
}

View File

@ -1,4 +1,4 @@
import { Component, HostBinding } from '@angular/core';
import { Component, HostBinding, OnInit } from '@angular/core';
import { AuthService, ScreenService, AppInfoService } from './shared/services';
@Component({
@ -6,12 +6,15 @@ import { AuthService, ScreenService, AppInfoService } from './shared/services';
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
export class AppComponent implements OnInit {
@HostBinding('class') get getClass() {
return Object.keys(this.screen.sizes).filter(cl => this.screen.sizes[cl]).join(' ');
}
constructor(private authService: AuthService, private screen: ScreenService, public appInfo: AppInfoService) { }
ngOnInit(): void {
this.authService.checkAuthenticationStatus();
}
isAuthenticated() {
return this.authService.loggedIn;

View File

@ -15,7 +15,7 @@
</dxi-item>
<dxi-item dataField="rememberMe" editorType="dxCheckBox"
[editorOptions]="{ text: 'Remember me', elementAttr: { class: 'form-text' } }">
[editorOptions]="{ text: 'Remember me', elementAttr: { class: 'form-text' }, value: remember_me, onValueChanged: rememberMeChanged }">
<dxo-label [visible]="false"></dxo-label>
</dxi-item>

View File

@ -15,6 +15,7 @@ import { AuthService } from '../../services';
export class LoginFormComponent {
loading = false;
loginCredentials: any = {};
remember_me: boolean = false;
constructor(private authService: AuthService, private router: Router) { }
@ -24,7 +25,7 @@ export class LoginFormComponent {
this.loading = true;
try {
const result = await this.authService.logIn(email, password);
const result = await this.authService.logIn(email, password, this.remember_me);
this.loading = false;
var message = "Success"
notify({
@ -49,6 +50,11 @@ export class LoginFormComponent {
}
}
rememberMeChanged = (e: any) => {
this.remember_me = e.value;
console.log(this.remember_me)
}
onCreateAccountClick = () => {
this.router.navigate(['/create-account']);
}

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot } from '@angular/router';
import { Observable, firstValueFrom, tap } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { environment } from '../../../environment/environment';
import { CustomerDto } from '../../types/CustomerDto';
@ -31,7 +31,7 @@ export class AuthService {
constructor(private router: Router, private http: HttpClient) {}
async logIn(email: string, password: string): Promise<any> {
async logIn(email: string, password: string, rememberMe: boolean = false): Promise<any> {
const result = await firstValueFrom(this.http.post<any>(`${API_URL}/authenticationapi/login`, { email, password }));
if (result.token) {
@ -42,12 +42,41 @@ export class AuthService {
lastName: result.customer.lastName,
avatarUrl: "https://js.devexpress.com/Demos/WidgetsGallery/JSDemos/images/employees/07.png"
};
}
localStorage.setItem('access_token', result.token);
// localStorage.setItem('user_data', JSON.stringify(this.user));
}
if (rememberMe) {
localStorage.setItem('remember_me', 'true');
}
return result;
}
checkAuthenticationStatus(): void {
const token = localStorage.getItem('access_token');
const rememberMe = localStorage.getItem('remember_me') === 'true';
//const userData = localStorage.getItem('user_data');
//if (userData) {
// this.user = JSON.parse(userData);
//}
if (token && rememberMe) {
// Erstellen des Headers mit dem Token
const headers = new HttpHeaders({
'Authorization': token,
});
this.http.get<any>(`${API_URL}/authenticationapi/validateToken`, { headers: headers }).subscribe({
next: (response) => {
this.router.navigate(['/home']);
},
error: (err) => {
console.error(err);
this.logOut(); // Bereinigung im Fehlerfall
}
});
} else {
this.logOut(); // Bereinigung, falls Remember Me nicht gesetzt ist
}
}
async getUser() {
try {
@ -120,6 +149,9 @@ export class AuthService {
async logOut() {
this.user = null;
// Bereinigen des lokalen Speichers
localStorage.removeItem('access_token');
localStorage.removeItem('remember_me');
this.router.navigate(['/login-form']);
}
}