Compare commits
No commits in common. "d96572f9750602778460920684075115e2a831ff" and "d8f65f124199f246dc51fb3b4ba6e49a1326f57f" have entirely different histories.
d96572f975
...
d8f65f1241
|
@ -14,7 +14,6 @@ export class CorsMiddleware implements NestMiddleware {
|
||||||
const requestOrigin = req.headers.origin;
|
const requestOrigin = req.headers.origin;
|
||||||
|
|
||||||
if (!requestOrigin || allowedOrigins.includes(requestOrigin)) {
|
if (!requestOrigin || allowedOrigins.includes(requestOrigin)) {
|
||||||
res.header('Access-Control-Allow-Credentials', 'true');
|
|
||||||
res.header('Access-Control-Allow-Origin', requestOrigin || '*');
|
res.header('Access-Control-Allow-Origin', requestOrigin || '*');
|
||||||
res.header(
|
res.header(
|
||||||
'Access-Control-Allow-Methods',
|
'Access-Control-Allow-Methods',
|
||||||
|
|
|
@ -12,7 +12,6 @@ export class CspMiddleware implements NestMiddleware {
|
||||||
if (cspDirectives) {
|
if (cspDirectives) {
|
||||||
res.setHeader('Content-Security-Policy', cspDirectives);
|
res.setHeader('Content-Security-Policy', cspDirectives);
|
||||||
}
|
}
|
||||||
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,9 +56,9 @@ export class AuthController {
|
||||||
})
|
})
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
@UseGuards(SessionGuard)
|
@UseGuards(SessionGuard)
|
||||||
@Post('signout')
|
@Post('logout')
|
||||||
public async signout(@Req() request: Request): Promise<SuccessDto> {
|
public async logout(@Req() request: Request): Promise<SuccessDto> {
|
||||||
return this.authService.signout(request.sessionID);
|
return this.authService.logout(request.sessionID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiCreatedResponse({
|
@ApiCreatedResponse({
|
||||||
|
|
|
@ -110,18 +110,6 @@ export class AuthService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async signout(sessionId: string): Promise<{ success: boolean }> {
|
|
||||||
try {
|
|
||||||
this.sessionService.deleteSessionBySessionId(sessionId);
|
|
||||||
return { success: true };
|
|
||||||
} catch (error) {
|
|
||||||
throw new HttpException(
|
|
||||||
'Fehler beim Logout',
|
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async checkAuthStatus(
|
public async checkAuthStatus(
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
userAgend: string
|
userAgend: string
|
||||||
|
@ -157,4 +145,16 @@ export class AuthService {
|
||||||
|
|
||||||
return responseData;
|
return responseData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async logout(sessionId: string): Promise<{ success: boolean }> {
|
||||||
|
try {
|
||||||
|
this.sessionService.deleteSessionBySessionId(sessionId);
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
throw new HttpException(
|
||||||
|
'Fehler beim Logout',
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,14 +27,10 @@ export class SessionInitService {
|
||||||
maxAge: 86400000,
|
maxAge: 86400000,
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure:
|
secure:
|
||||||
this.configService.get<string>('NODE_ENV') === 'development'
|
this.configService.get<string>('NODE_ENV') === 'production'
|
||||||
? false
|
? true
|
||||||
: true,
|
: false,
|
||||||
|
sameSite: 'strict',
|
||||||
sameSite:
|
|
||||||
this.configService.get<string>('NODE_ENV') === 'development'
|
|
||||||
? 'strict'
|
|
||||||
: 'none',
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Component } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { RouterOutlet } from '@angular/router';
|
import { RouterOutlet, Router } from '@angular/router';
|
||||||
|
|
||||||
|
import { AuthService } from './shared/service';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
|
@ -9,6 +10,24 @@ import { RouterOutlet } from '@angular/router';
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrl: './app.component.scss',
|
styleUrl: './app.component.scss',
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent implements OnInit {
|
||||||
public constructor() {}
|
public constructor(
|
||||||
|
private readonly authService: AuthService,
|
||||||
|
private readonly router: Router
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.checkAuthentication();
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkAuthentication(): void {
|
||||||
|
this.authService.isAuthenticated$.subscribe((isAuthenticated: boolean) => {
|
||||||
|
if (isAuthenticated) {
|
||||||
|
console.log('User is authenticated');
|
||||||
|
this.router.navigateByUrl('dashboard');
|
||||||
|
} else {
|
||||||
|
this.router.navigateByUrl('signup');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,18 +3,12 @@ import { ApplicationConfig } from '@angular/core';
|
||||||
import { provideAnimations } from '@angular/platform-browser/animations';
|
import { provideAnimations } from '@angular/platform-browser/animations';
|
||||||
import { provideRouter, withComponentInputBinding } from '@angular/router';
|
import { provideRouter, withComponentInputBinding } from '@angular/router';
|
||||||
|
|
||||||
import { Configuration } from './api';
|
|
||||||
import { routes } from './app.routes';
|
import { routes } from './app.routes';
|
||||||
import { ApiConfiguration } from './config/api-configuration';
|
import { AuthInterceptor } from './shared/interceptors/auth.interceptor';
|
||||||
|
|
||||||
const apiConfiguration = new ApiConfiguration({
|
|
||||||
withCredentials: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const appConfig: ApplicationConfig = {
|
export const appConfig: ApplicationConfig = {
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: Configuration, useValue: apiConfiguration },
|
provideHttpClient(withInterceptors([AuthInterceptor])),
|
||||||
provideHttpClient(withInterceptors([])),
|
|
||||||
provideRouter(routes, withComponentInputBinding()),
|
provideRouter(routes, withComponentInputBinding()),
|
||||||
provideAnimations(),
|
provideAnimations(),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
|
|
||||||
|
import { AuthGuard } from './shared/guard/auth.guard';
|
||||||
|
|
||||||
const publicRoutes: Routes = [
|
const publicRoutes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
loadComponent: () =>
|
loadComponent: () => import('./app.component').then((m) => m.AppComponent),
|
||||||
import('./pages/home-root/home-root.component').then(
|
|
||||||
(m) => m.HomeComponent
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'signup',
|
path: 'signup',
|
||||||
|
@ -31,7 +30,7 @@ const protectedRoutes: Routes = [
|
||||||
import('./pages/dashboard-root/dashboard-root.component').then(
|
import('./pages/dashboard-root/dashboard-root.component').then(
|
||||||
(m) => m.DashboardRootComponent
|
(m) => m.DashboardRootComponent
|
||||||
),
|
),
|
||||||
canActivate: [],
|
canActivate: [AuthGuard],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { Configuration, ConfigurationParameters } from '../api';
|
|
||||||
|
|
||||||
export class ApiConfiguration extends Configuration {
|
|
||||||
public constructor(params?: Partial<ConfigurationParameters>) {
|
|
||||||
super({
|
|
||||||
...params,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
import { HttpErrorResponse } from '@angular/common/http';
|
|
||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
|
|
||||||
import { SuccessDtoApiModel } from '../../api';
|
|
||||||
import { AuthService } from '../../shared/service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-foo',
|
|
||||||
standalone: true,
|
|
||||||
providers: [],
|
|
||||||
imports: [],
|
|
||||||
templateUrl: './home-root.component.html',
|
|
||||||
styleUrl: './home-root.component.scss',
|
|
||||||
})
|
|
||||||
export class HomeComponent implements OnInit {
|
|
||||||
public constructor(
|
|
||||||
private readonly authService: AuthService,
|
|
||||||
private readonly router: Router
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public ngOnInit(): void {
|
|
||||||
this.authService.status().subscribe(
|
|
||||||
(response: SuccessDtoApiModel) => {
|
|
||||||
if (response.success) {
|
|
||||||
this.router.navigate(['/dashboard']);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(error: HttpErrorResponse) => {
|
|
||||||
if (error.status === 401) {
|
|
||||||
this.router.navigate(['signup'], {
|
|
||||||
queryParams: { login: true },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,110 +1,94 @@
|
||||||
<div id="background">
|
<div id="background">
|
||||||
<div class="img-zone">
|
<div class="img-zone">
|
||||||
<div class="img-wrapper">
|
<div class="img-wrapper">
|
||||||
@if (userSignupSuccess()) {
|
<h1>Hi, Welcome to Ticket App.</h1>
|
||||||
<div class="success">
|
|
||||||
<h1>Danke für deine Registrierung!</h1>
|
|
||||||
<h2>
|
|
||||||
Wir haben dir eine Mail geschickt an
|
|
||||||
{{ form?.get('email')?.value }}. Bitte bestätige deine
|
|
||||||
E-Mail-Adresse um fortzufahren.
|
|
||||||
</h2>
|
|
||||||
<p>Du kannst diesen Tab nun schließen</p>
|
|
||||||
</div>
|
|
||||||
} @else {
|
|
||||||
<div class="headline">
|
|
||||||
<h1>Hi, Welcome to Ticket App.</h1>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="content-zone">
|
||||||
|
<h1>
|
||||||
|
@if (isSignupSignal()) {
|
||||||
|
Anmelden
|
||||||
|
} @else if (isRegisterSignal()) {
|
||||||
|
Registrieren
|
||||||
|
} @else {
|
||||||
|
Erste Schritte
|
||||||
|
}
|
||||||
|
</h1>
|
||||||
|
|
||||||
@if (!userSignupSuccess()) {
|
@if (isDisplayButtons()) {
|
||||||
<div class="content-zone">
|
<div class="action">
|
||||||
<h1>
|
<button
|
||||||
@if (isSignupSignal()) {
|
pButton
|
||||||
Anmelden
|
type="button"
|
||||||
} @else if (isRegisterSignal()) {
|
label="Anmelden"
|
||||||
Registrieren
|
(click)="toggleAction('signup')"></button>
|
||||||
} @else {
|
<button
|
||||||
Erste Schritte
|
pButton
|
||||||
|
type="button"
|
||||||
|
label="Registrieren"
|
||||||
|
(click)="toggleAction('register')"></button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (isSignupSignal() || isRegisterSignal()) {
|
||||||
|
<div class="register-wrapper">
|
||||||
|
@if (form) {
|
||||||
|
<form [formGroup]="form" (ngSubmit)="onSubmit()">
|
||||||
|
<div class="e-mail">
|
||||||
|
<div class="label">
|
||||||
|
<label for="email">E-Mail</label>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
pInputText
|
||||||
|
id="email"
|
||||||
|
formControlName="email"
|
||||||
|
aria-describedby="e-mail" />
|
||||||
|
</div>
|
||||||
|
<div class="password">
|
||||||
|
<div class="label">
|
||||||
|
<label for="password">Password</label>
|
||||||
|
</div>
|
||||||
|
<p-password
|
||||||
|
class="custom-p-password"
|
||||||
|
id="password"
|
||||||
|
formControlName="password"
|
||||||
|
aria-describedby="password"
|
||||||
|
[toggleMask]="true"></p-password>
|
||||||
|
</div>
|
||||||
|
@if (isRegisterSignal()) {
|
||||||
|
<div class="terms">
|
||||||
|
<p-checkbox
|
||||||
|
formControlName="terms"
|
||||||
|
label="Ich habe die AGB gelesen und stimme zu."
|
||||||
|
name="terms"
|
||||||
|
[binary]="true"></p-checkbox>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="signup">
|
||||||
|
<button
|
||||||
|
pButton
|
||||||
|
type="submit"
|
||||||
|
[label]="
|
||||||
|
isSignupSignal()
|
||||||
|
? 'Anmelden'
|
||||||
|
: '✨ Jetzt KOSTENFREI loslegen ✨'
|
||||||
|
"></button>
|
||||||
|
</div>
|
||||||
|
<div class="change-mask">
|
||||||
|
<a
|
||||||
|
(click)="switchMask()"
|
||||||
|
(keyup.enter)="switchMask()"
|
||||||
|
tabindex="0">
|
||||||
|
@if (isSignupSignal()) {
|
||||||
|
Kein Account? Erstellen Sie jetzt KOSTENFREI einen!
|
||||||
|
} @else {
|
||||||
|
Schon einen Account? Hier einloggen
|
||||||
|
}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
}
|
}
|
||||||
</h1>
|
</div>
|
||||||
|
}
|
||||||
@if (isDisplayButtons()) {
|
</div>
|
||||||
<div class="action">
|
|
||||||
<button
|
|
||||||
pButton
|
|
||||||
type="button"
|
|
||||||
label="Anmelden"
|
|
||||||
(click)="toggleAction('signup')"></button>
|
|
||||||
<button
|
|
||||||
pButton
|
|
||||||
type="button"
|
|
||||||
label="Registrieren"
|
|
||||||
(click)="toggleAction('register')"></button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@if (isSignupSignal() || isRegisterSignal()) {
|
|
||||||
<div class="register-wrapper">
|
|
||||||
@if (form) {
|
|
||||||
<form [formGroup]="form" (ngSubmit)="onSubmit()">
|
|
||||||
<div class="e-mail">
|
|
||||||
<div class="label">
|
|
||||||
<label for="email">E-Mail</label>
|
|
||||||
</div>
|
|
||||||
<input
|
|
||||||
pInputText
|
|
||||||
id="email"
|
|
||||||
formControlName="email"
|
|
||||||
aria-describedby="e-mail" />
|
|
||||||
</div>
|
|
||||||
<div class="password">
|
|
||||||
<div class="label">
|
|
||||||
<label for="password">Password</label>
|
|
||||||
</div>
|
|
||||||
<p-password
|
|
||||||
class="custom-p-password"
|
|
||||||
id="password"
|
|
||||||
formControlName="password"
|
|
||||||
aria-describedby="password"
|
|
||||||
[toggleMask]="true"></p-password>
|
|
||||||
</div>
|
|
||||||
@if (isRegisterSignal()) {
|
|
||||||
<div class="terms">
|
|
||||||
<p-checkbox
|
|
||||||
formControlName="terms"
|
|
||||||
label="Ich habe die AGB gelesen und stimme zu."
|
|
||||||
name="terms"
|
|
||||||
[binary]="true"></p-checkbox>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<div class="signup">
|
|
||||||
<button
|
|
||||||
pButton
|
|
||||||
type="submit"
|
|
||||||
[label]="
|
|
||||||
isSignupSignal()
|
|
||||||
? 'Anmelden'
|
|
||||||
: '✨ Jetzt KOSTENFREI loslegen ✨'
|
|
||||||
"></button>
|
|
||||||
</div>
|
|
||||||
<div class="change-mask">
|
|
||||||
<a
|
|
||||||
(click)="switchMask()"
|
|
||||||
(keyup.enter)="switchMask()"
|
|
||||||
tabindex="0">
|
|
||||||
@if (isSignupSignal()) {
|
|
||||||
Kein Account? Erstellen Sie jetzt KOSTENFREI einen!
|
|
||||||
} @else {
|
|
||||||
Schon einen Account? Hier einloggen
|
|
||||||
}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -12,20 +12,10 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.success {
|
h1 {
|
||||||
margin-left: 4em;
|
font-size: 4em;
|
||||||
h1 {
|
margin-left: 1em;
|
||||||
font-size: 4em;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.headline {
|
|
||||||
h1 {
|
|
||||||
font-size: 4em;
|
|
||||||
margin-left: 1em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,14 +25,8 @@ import { CheckboxModule } from 'primeng/checkbox';
|
||||||
import { InputTextModule } from 'primeng/inputtext';
|
import { InputTextModule } from 'primeng/inputtext';
|
||||||
import { PasswordModule } from 'primeng/password';
|
import { PasswordModule } from 'primeng/password';
|
||||||
|
|
||||||
import {
|
import { AuthService } from '../../shared/service';
|
||||||
Configuration,
|
import { LoginCredentials } from '../../shared/types';
|
||||||
SigninResponseDtoApiModel,
|
|
||||||
SuccessDtoApiModel,
|
|
||||||
UserCredentialsDtoApiModel,
|
|
||||||
} from '../../api';
|
|
||||||
import { ApiConfiguration } from '../../config/api-configuration';
|
|
||||||
import { AuthService, SessionStorageService } from '../../shared/service';
|
|
||||||
import {
|
import {
|
||||||
customEmailValidator,
|
customEmailValidator,
|
||||||
customPasswordValidator,
|
customPasswordValidator,
|
||||||
|
@ -53,20 +47,13 @@ type AuthAction = 'register' | 'signup';
|
||||||
PasswordModule,
|
PasswordModule,
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [],
|
||||||
{
|
|
||||||
provide: Configuration,
|
|
||||||
useFactory: (): unknown =>
|
|
||||||
new ApiConfiguration({ withCredentials: true }),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
templateUrl: './register-root.component.html',
|
templateUrl: './register-root.component.html',
|
||||||
styleUrl: './register-root.component.scss',
|
styleUrl: './register-root.component.scss',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class RegisterRootComponent implements OnInit {
|
export class RegisterRootComponent implements OnInit {
|
||||||
public verified: InputSignal<boolean> = input<boolean>(false);
|
public verified: InputSignal<boolean> = input<boolean>(false);
|
||||||
public login: InputSignal<boolean> = input<boolean>(false);
|
|
||||||
public email: InputSignal<string> = input<string>('');
|
public email: InputSignal<string> = input<string>('');
|
||||||
public form: FormGroup | undefined;
|
public form: FormGroup | undefined;
|
||||||
public isRegisterSignal: WritableSignal<boolean> = signal(false);
|
public isRegisterSignal: WritableSignal<boolean> = signal(false);
|
||||||
|
@ -75,14 +62,12 @@ export class RegisterRootComponent implements OnInit {
|
||||||
public emailInvalid: WritableSignal<string | null> = signal(null);
|
public emailInvalid: WritableSignal<string | null> = signal(null);
|
||||||
public passwordInvalid: WritableSignal<string | null> = signal(null);
|
public passwordInvalid: WritableSignal<string | null> = signal(null);
|
||||||
public termsInvalid: WritableSignal<string | null> = signal(null);
|
public termsInvalid: WritableSignal<string | null> = signal(null);
|
||||||
public userSignupSuccess: WritableSignal<boolean> = signal(false);
|
|
||||||
private removeQueryParams: WritableSignal<boolean> = signal(false);
|
private removeQueryParams: WritableSignal<boolean> = signal(false);
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly formBuilder: FormBuilder,
|
private readonly formBuilder: FormBuilder,
|
||||||
private readonly authService: AuthService,
|
private readonly authService: AuthService,
|
||||||
private readonly router: Router,
|
private readonly router: Router
|
||||||
private readonly sessionStorageService: SessionStorageService
|
|
||||||
) {
|
) {
|
||||||
effect(() => {
|
effect(() => {
|
||||||
if (this.form) {
|
if (this.form) {
|
||||||
|
@ -105,22 +90,13 @@ export class RegisterRootComponent implements OnInit {
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.initializeForm();
|
this.initializeForm();
|
||||||
this.setupValueChanges();
|
this.setupValueChanges();
|
||||||
this.preselectForm();
|
|
||||||
|
|
||||||
if ((this.email() && this.verified()) || this.login()) {
|
if (this.email() || this.verified()) {
|
||||||
this.handleRedirect();
|
this.handleRedirect();
|
||||||
this.removeQueryParams.set(true);
|
this.removeQueryParams.set(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public preselectForm(): void {
|
|
||||||
if (!this.email() || !this.verified()) {
|
|
||||||
const email = this.sessionStorageService.getItem('email');
|
|
||||||
|
|
||||||
this.form?.get('email')?.setValue(email);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public toggleAction(action: AuthAction): void {
|
public toggleAction(action: AuthAction): void {
|
||||||
if (action === 'register') {
|
if (action === 'register') {
|
||||||
this.isRegisterSignal.set(true);
|
this.isRegisterSignal.set(true);
|
||||||
|
@ -137,7 +113,7 @@ export class RegisterRootComponent implements OnInit {
|
||||||
|
|
||||||
if (this.form?.valid) {
|
if (this.form?.valid) {
|
||||||
if (this.isRegisterSignal()) {
|
if (this.isRegisterSignal()) {
|
||||||
this.signup(this.form.value);
|
this.register(this.form.value);
|
||||||
} else {
|
} else {
|
||||||
this.signin(this.form.value);
|
this.signin(this.form.value);
|
||||||
}
|
}
|
||||||
|
@ -174,6 +150,7 @@ export class RegisterRootComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleRedirect(): void {
|
private handleRedirect(): void {
|
||||||
|
console.log('handleRedirect');
|
||||||
if (this.verified()) {
|
if (this.verified()) {
|
||||||
this.isDisplayButtons.set(false);
|
this.isDisplayButtons.set(false);
|
||||||
this.isRegisterSignal.set(false);
|
this.isRegisterSignal.set(false);
|
||||||
|
@ -182,12 +159,6 @@ export class RegisterRootComponent implements OnInit {
|
||||||
if (this.email()) {
|
if (this.email()) {
|
||||||
this.form?.get('email')?.setValue(decodeURIComponent(atob(this.email())));
|
this.form?.get('email')?.setValue(decodeURIComponent(atob(this.email())));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.login()) {
|
|
||||||
this.isSignupSignal.set(true);
|
|
||||||
this.isDisplayButtons.set(false);
|
|
||||||
this.isRegisterSignal.set(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private clearRouteParams(): void {
|
private clearRouteParams(): void {
|
||||||
|
@ -266,23 +237,11 @@ export class RegisterRootComponent implements OnInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private signin(logiCredentials: UserCredentialsDtoApiModel): void {
|
private signin(logiCredentials: LoginCredentials): void {
|
||||||
this.authService
|
this.authService.signin(logiCredentials);
|
||||||
.signin(logiCredentials)
|
|
||||||
.subscribe((response: SigninResponseDtoApiModel) => {
|
|
||||||
if (response) {
|
|
||||||
this.router.navigate(['/dashboard']);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private signup(logiCredentials: UserCredentialsDtoApiModel): void {
|
private register(logiCredentials: LoginCredentials): void {
|
||||||
this.authService
|
this.authService.signup(logiCredentials);
|
||||||
.signup(logiCredentials)
|
|
||||||
.subscribe((response: SuccessDtoApiModel) => {
|
|
||||||
if (response.success) {
|
|
||||||
this.userSignupSuccess.set(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { inject } from '@angular/core';
|
||||||
|
import {
|
||||||
|
ActivatedRouteSnapshot,
|
||||||
|
CanActivateFn,
|
||||||
|
Router,
|
||||||
|
RouterStateSnapshot,
|
||||||
|
UrlTree,
|
||||||
|
} from '@angular/router';
|
||||||
|
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import { AuthService } from '../service';
|
||||||
|
|
||||||
|
export const AuthGuard: CanActivateFn = (
|
||||||
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot
|
||||||
|
):
|
||||||
|
| Observable<boolean | UrlTree>
|
||||||
|
| Promise<boolean | UrlTree>
|
||||||
|
| boolean
|
||||||
|
| UrlTree => {
|
||||||
|
const authService: AuthService = inject(AuthService);
|
||||||
|
const router: Router = inject(Router);
|
||||||
|
|
||||||
|
authService.isAuthenticated$.subscribe((isAuthenticated: boolean) => {
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
router.navigateByUrl('signup');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
|
@ -0,0 +1,79 @@
|
||||||
|
import {
|
||||||
|
HttpInterceptorFn,
|
||||||
|
HttpRequest,
|
||||||
|
HttpHandlerFn,
|
||||||
|
HttpEvent,
|
||||||
|
HttpErrorResponse,
|
||||||
|
} from '@angular/common/http';
|
||||||
|
import { inject } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
import { Observable, throwError } from 'rxjs';
|
||||||
|
import { catchError, switchMap } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { AuthService } from '../service';
|
||||||
|
|
||||||
|
export const AuthInterceptor: HttpInterceptorFn = (
|
||||||
|
request: HttpRequest<unknown>,
|
||||||
|
next: HttpHandlerFn
|
||||||
|
): Observable<HttpEvent<unknown>> => {
|
||||||
|
const router = inject(Router);
|
||||||
|
const authService = inject(AuthService);
|
||||||
|
|
||||||
|
const handleRequest = (
|
||||||
|
req: HttpRequest<unknown>
|
||||||
|
): Observable<HttpEvent<unknown>> => {
|
||||||
|
const accessToken = authService.access_token;
|
||||||
|
|
||||||
|
if (accessToken) {
|
||||||
|
req = addAuthHeader(req, accessToken);
|
||||||
|
}
|
||||||
|
return next(req);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addAuthHeader = (
|
||||||
|
req: HttpRequest<unknown>,
|
||||||
|
token: string
|
||||||
|
): HttpRequest<unknown> => {
|
||||||
|
return req.clone({
|
||||||
|
setHeaders: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handle401Error = (
|
||||||
|
req: HttpRequest<unknown>
|
||||||
|
): Observable<HttpEvent<unknown>> => {
|
||||||
|
console.log(authService.refresh_token);
|
||||||
|
if (!authService.refresh_token) {
|
||||||
|
router.navigateByUrl('signup');
|
||||||
|
return throwError(() => new Error('Authentication required'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return authService.refreshToken().pipe(
|
||||||
|
switchMap((tokens) => {
|
||||||
|
req = addAuthHeader(req, tokens.access_token);
|
||||||
|
return next(req);
|
||||||
|
}),
|
||||||
|
catchError((refreshError) => {
|
||||||
|
router.navigateByUrl('signup');
|
||||||
|
return throwError(() => new Error(refreshError));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleError = (
|
||||||
|
error: HttpErrorResponse,
|
||||||
|
req: HttpRequest<unknown>
|
||||||
|
): Observable<HttpEvent<unknown>> => {
|
||||||
|
if (error.status === 401) {
|
||||||
|
return handle401Error(req);
|
||||||
|
}
|
||||||
|
return throwError(() => new Error('Unhandled error'));
|
||||||
|
};
|
||||||
|
|
||||||
|
return handleRequest(request).pipe(
|
||||||
|
catchError((error) => handleError(error, request))
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,16 +1,12 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable, tap } from 'rxjs';
|
||||||
|
|
||||||
import {
|
|
||||||
SigninResponseDtoApiModel,
|
|
||||||
UserCredentialsDtoApiModel,
|
|
||||||
} from '../../api';
|
|
||||||
import { AuthenticationApiService } from '../../api/api/authentication.api.service';
|
import { AuthenticationApiService } from '../../api/api/authentication.api.service';
|
||||||
|
import { LoginCredentials, Tokens } from '../types';
|
||||||
|
|
||||||
type SuccessResponse = {
|
import { LocalStorageService } from './local-storage.service';
|
||||||
success: boolean;
|
import { SessionStorageService } from './session-storage.service';
|
||||||
};
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
|
@ -18,28 +14,73 @@ type SuccessResponse = {
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
public isAuthenticated$: BehaviorSubject<boolean> =
|
public isAuthenticated$: BehaviorSubject<boolean> =
|
||||||
new BehaviorSubject<boolean>(false);
|
new BehaviorSubject<boolean>(false);
|
||||||
|
private _access_token: string | null = null;
|
||||||
|
private _refresh_token: string | null = null;
|
||||||
|
|
||||||
|
public get access_token(): string | null {
|
||||||
|
return this._access_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get refresh_token(): string | null {
|
||||||
|
return this._refresh_token;
|
||||||
|
}
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
private readonly localStorageService: LocalStorageService,
|
||||||
|
private readonly sessionStorageService: SessionStorageService,
|
||||||
private readonly authenticationApiService: AuthenticationApiService
|
private readonly authenticationApiService: AuthenticationApiService
|
||||||
) {}
|
) {
|
||||||
|
this._access_token =
|
||||||
public signup(
|
this.localStorageService.getItem<string>('access_token');
|
||||||
credentials: UserCredentialsDtoApiModel
|
this._refresh_token =
|
||||||
): Observable<SuccessResponse> {
|
this.sessionStorageService.getItem<string>('refresh_token');
|
||||||
return this.authenticationApiService.authControllerSignup(credentials);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public signin(
|
public signin(credentials: LoginCredentials): void {
|
||||||
credentials: UserCredentialsDtoApiModel
|
this.authenticationApiService
|
||||||
): Observable<SigninResponseDtoApiModel> {
|
.authControllerSignin(credentials)
|
||||||
return this.authenticationApiService.authControllerSignin(credentials);
|
.subscribe((response: Tokens) => {
|
||||||
|
this.handleSuccess(response);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public signout(): Observable<SuccessResponse> {
|
public signup(credentials: LoginCredentials): void {
|
||||||
return this.authenticationApiService.authControllerSignout();
|
this.authenticationApiService
|
||||||
|
.authControllerSignup(credentials)
|
||||||
|
.subscribe((response: Tokens) => {
|
||||||
|
this.handleSuccess(response);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public status(): Observable<SuccessResponse> {
|
public refreshToken(): Observable<Tokens> {
|
||||||
return this.authenticationApiService.authControllerStatus();
|
if (this._refresh_token) {
|
||||||
|
return this.authenticationApiService
|
||||||
|
.authControllerRefresh(this._refresh_token)
|
||||||
|
.pipe(tap((response: Tokens) => this.handleSuccess(response)));
|
||||||
|
} else {
|
||||||
|
throw new Error('Refresh token is missing');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public signout(): void {
|
||||||
|
this.authenticationApiService
|
||||||
|
.authControllerLogout()
|
||||||
|
.subscribe((response: boolean) => {
|
||||||
|
if (response) {
|
||||||
|
this._access_token = null;
|
||||||
|
this._refresh_token = null;
|
||||||
|
this.localStorageService.removeItem('access_token');
|
||||||
|
this.sessionStorageService.removeItem('refresh_token');
|
||||||
|
this.isAuthenticated$.next(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleSuccess(tokens: Tokens): void {
|
||||||
|
this._access_token = tokens.access_token;
|
||||||
|
this._refresh_token = tokens.refresh_token;
|
||||||
|
this.localStorageService.setItem('access_token', tokens.access_token);
|
||||||
|
this.sessionStorageService.setItem('refresh_token', tokens.refresh_token);
|
||||||
|
this.isAuthenticated$.next(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from './login-credentials';
|
||||||
|
export * from './tokens';
|
|
@ -0,0 +1,4 @@
|
||||||
|
export type LoginCredentials = {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
};
|
|
@ -0,0 +1,4 @@
|
||||||
|
export type Tokens = {
|
||||||
|
access_token: string;
|
||||||
|
refresh_token: string;
|
||||||
|
};
|
Loading…
Reference in New Issue