change to firebase auth

This commit is contained in:
Andreas Knuth 2025-02-18 18:05:51 -06:00
parent b9a9b983e9
commit a2e6243e93
34 changed files with 465 additions and 63 deletions

View File

@ -58,6 +58,7 @@ report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
pictures pictures
pictures_base pictures_base
pictures_
src/*.js src/*.js
bun.lockb bun.lockb

View File

@ -20,10 +20,10 @@
{ {
"type": "node", "type": "node",
"request": "launch", "request": "launch",
"name": "Launch TypeScript file with tsx", "name": "Launch import from exported with tsx",
"runtimeExecutable": "npx", "runtimeExecutable": "npx",
"runtimeArgs": ["tsx", "--inspect"], "runtimeArgs": ["tsx", "--inspect"],
"args": ["${workspaceFolder}/src/drizzle/import.ts"], "args": ["${workspaceFolder}/src/drizzle/importFromExported.ts"],
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
"outFiles": ["${workspaceFolder}/dist/**/*.js", "!**/node_modules/**"], "outFiles": ["${workspaceFolder}/dist/**/*.js", "!**/node_modules/**"],
"sourceMaps": true, "sourceMaps": true,

View File

@ -23,7 +23,6 @@ import { APP_INTERCEPTOR } from '@nestjs/core';
import { ClsMiddleware, ClsModule } from 'nestjs-cls'; import { ClsMiddleware, ClsModule } from 'nestjs-cls';
import { LoggingInterceptor } from './interceptors/logging.interceptor'; import { LoggingInterceptor } from './interceptors/logging.interceptor';
import { UserInterceptor } from './interceptors/user.interceptor'; import { UserInterceptor } from './interceptors/user.interceptor';
import { PaymentModule } from './payment/payment.module';
import { RequestDurationMiddleware } from './request-duration/request-duration.middleware'; import { RequestDurationMiddleware } from './request-duration/request-duration.middleware';
import { SelectOptionsModule } from './select-options/select-options.module'; import { SelectOptionsModule } from './select-options/select-options.module';
import { UserModule } from './user/user.module'; import { UserModule } from './user/user.module';
@ -70,7 +69,7 @@ console.log(JSON.stringify(process.env, null, 2));
PassportModule, PassportModule,
AiModule, AiModule,
LogModule, LogModule,
PaymentModule, // PaymentModule,
EventModule, EventModule,
], ],
controllers: [AppController, LogController], controllers: [AppController, LogController],

View File

@ -18,6 +18,7 @@
"@angular/common": "^18.1.3", "@angular/common": "^18.1.3",
"@angular/compiler": "^18.1.3", "@angular/compiler": "^18.1.3",
"@angular/core": "^18.1.3", "@angular/core": "^18.1.3",
"@angular/fire": "^18.0.1",
"@angular/forms": "^18.1.3", "@angular/forms": "^18.1.3",
"@angular/platform-browser": "^18.1.3", "@angular/platform-browser": "^18.1.3",
"@angular/platform-browser-dynamic": "^18.1.3", "@angular/platform-browser-dynamic": "^18.1.3",

View File

@ -1,6 +1,6 @@
<!-- <div class="container"> --> <!-- <div class="container"> -->
<div class="flex flex-col" [ngClass]="{ 'bg-slate-100 print:bg-white': actualRoute !== 'home' }"> <div class="flex flex-col" [ngClass]="{ 'bg-slate-100 print:bg-white': actualRoute !== 'home' }">
@if (actualRoute !=='home'){ @if (actualRoute !=='home' && actualRoute !=='login'){
<header></header> <header></header>
} }
<main class="flex-grow"> <main class="flex-grow">

View File

@ -2,6 +2,8 @@ import { APP_INITIALIZER, ApplicationConfig, ErrorHandler } from '@angular/core'
import { provideRouter, withEnabledBlockingInitialNavigation, withInMemoryScrolling } from '@angular/router'; import { provideRouter, withEnabledBlockingInitialNavigation, withInMemoryScrolling } from '@angular/router';
import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { initializeApp, provideFirebaseApp } from '@angular/fire/app';
import { getAuth, provideAuth } from '@angular/fire/auth';
import { provideAnimations } from '@angular/platform-browser/animations'; import { provideAnimations } from '@angular/platform-browser/animations';
import { KeycloakBearerInterceptor, KeycloakService } from 'keycloak-angular'; import { KeycloakBearerInterceptor, KeycloakService } from 'keycloak-angular';
import { GALLERY_CONFIG, GalleryConfig } from 'ng-gallery'; import { GALLERY_CONFIG, GalleryConfig } from 'ng-gallery';
@ -9,7 +11,9 @@ import { provideQuillConfig } from 'ngx-quill';
import { provideShareButtonsOptions, SharerMethods, withConfig } from 'ngx-sharebuttons'; import { provideShareButtonsOptions, SharerMethods, withConfig } from 'ngx-sharebuttons';
import { shareIcons } from 'ngx-sharebuttons/icons'; import { shareIcons } from 'ngx-sharebuttons/icons';
import { provideNgxStripe } from 'ngx-stripe'; import { provideNgxStripe } from 'ngx-stripe';
import { environment } from '../environments/environment';
import { routes } from './app.routes'; import { routes } from './app.routes';
import { AuthInterceptor } from './interceptors/auth.interceptor';
import { LoadingInterceptor } from './interceptors/loading.interceptor'; import { LoadingInterceptor } from './interceptors/loading.interceptor';
import { TimeoutInterceptor } from './interceptors/timeout.interceptor'; import { TimeoutInterceptor } from './interceptors/timeout.interceptor';
import { GlobalErrorHandler } from './services/globalErrorHandler'; import { GlobalErrorHandler } from './services/globalErrorHandler';
@ -52,6 +56,7 @@ export const appConfig: ApplicationConfig = {
useClass: TimeoutInterceptor, useClass: TimeoutInterceptor,
multi: true, multi: true,
}, },
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
{ {
provide: 'TIMEOUT_DURATION', provide: 'TIMEOUT_DURATION',
useValue: 5000, // Standard-Timeout von 5 Sekunden useValue: 5000, // Standard-Timeout von 5 Sekunden
@ -93,6 +98,9 @@ export const appConfig: ApplicationConfig = {
], ],
}, },
}), }),
provideFirebaseApp(() => initializeApp(environment.firebaseConfig)),
provideAuth(() => getAuth()),
// provideFirestore(() => getFirestore()),
], ],
}; };
function initServices(selectOptions: SelectOptionsService) { function initServices(selectOptions: SelectOptionsService) {

View File

@ -2,6 +2,7 @@ import { Routes } from '@angular/router';
import { LogoutComponent } from './components/logout/logout.component'; import { LogoutComponent } from './components/logout/logout.component';
import { NotFoundComponent } from './components/not-found/not-found.component'; import { NotFoundComponent } from './components/not-found/not-found.component';
import { LoginRegisterComponent } from './components/login-register/login-register.component';
import { AuthGuard } from './guards/auth.guard'; import { AuthGuard } from './guards/auth.guard';
import { ListingCategoryGuard } from './guards/listing-category.guard'; import { ListingCategoryGuard } from './guards/listing-category.guard';
import { UserListComponent } from './pages/admin/user-list/user-list.component'; import { UserListComponent } from './pages/admin/user-list/user-list.component';
@ -12,7 +13,6 @@ import { HomeComponent } from './pages/home/home.component';
import { BrokerListingsComponent } from './pages/listings/broker-listings/broker-listings.component'; import { BrokerListingsComponent } from './pages/listings/broker-listings/broker-listings.component';
import { BusinessListingsComponent } from './pages/listings/business-listings/business-listings.component'; import { BusinessListingsComponent } from './pages/listings/business-listings/business-listings.component';
import { CommercialPropertyListingsComponent } from './pages/listings/commercial-property-listings/commercial-property-listings.component'; import { CommercialPropertyListingsComponent } from './pages/listings/commercial-property-listings/commercial-property-listings.component';
import { LoginComponent } from './pages/login/login.component';
import { PricingComponent } from './pages/pricing/pricing.component'; import { PricingComponent } from './pages/pricing/pricing.component';
import { AccountComponent } from './pages/subscription/account/account.component'; import { AccountComponent } from './pages/subscription/account/account.component';
import { EditBusinessListingComponent } from './pages/subscription/edit-business-listing/edit-business-listing.component'; import { EditBusinessListingComponent } from './pages/subscription/edit-business-listing/edit-business-listing.component';
@ -57,9 +57,17 @@ export const routes: Routes = [
canActivate: [ListingCategoryGuard], canActivate: [ListingCategoryGuard],
component: NotFoundComponent, // Dummy-Komponente, wird nie angezeigt, da der Guard weiterleitet component: NotFoundComponent, // Dummy-Komponente, wird nie angezeigt, da der Guard weiterleitet
}, },
// {
// path: 'login/:page',
// component: LoginComponent, // Dummy-Komponente, wird nie angezeigt, da der Guard weiterleitet
// },
{ {
path: 'login/:page', path: 'login/:page',
component: LoginComponent, // Dummy-Komponente, wird nie angezeigt, da der Guard weiterleitet component: LoginRegisterComponent, // Dummy-Komponente, wird nie angezeigt, da der Guard weiterleitet
},
{
path: 'login',
component: LoginRegisterComponent, // Dummy-Komponente, wird nie angezeigt, da der Guard weiterleitet
}, },
{ {
path: 'notfound', path: 'notfound',

View File

@ -0,0 +1,7 @@
<div class="flex flex-col items-center justify-center min-h-screen bg-gray-100">
<div class="bg-white p-8 rounded shadow-md w-full max-w-md text-center">
<h2 class="text-2xl font-bold mb-4">Email Verification</h2>
<p class="mb-4">A verification email has been sent to your email address. Please check your inbox and click the link to verify your account.</p>
<p>Once verified, please return to the application.</p>
</div>
</div>

View File

@ -0,0 +1,9 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-email-verification',
standalone: true,
imports: [],
templateUrl: './email-verification.component.html',
})
export class EmailVerificationComponent {}

View File

@ -11,7 +11,7 @@
<div class="flex flex-col lg:flex-row items-center order-3 lg:order-2"> <div class="flex flex-col lg:flex-row items-center order-3 lg:order-2">
<a class="text-sm text-blue-600 hover:underline hover:cursor-pointer mx-2" data-drawer-target="terms-of-use" data-drawer-show="terms-of-use" aria-controls="terms-of-use">Terms of use</a> <a class="text-sm text-blue-600 hover:underline hover:cursor-pointer mx-2" data-drawer-target="terms-of-use" data-drawer-show="terms-of-use" aria-controls="terms-of-use">Terms of use</a>
<a class="text-sm text-blue-600 hover:underline hover:cursor-pointer mx-2" data-drawer-target="privacy" data-drawer-show="privacy" aria-controls="privacy">Privacy statement</a> <a class="text-sm text-blue-600 hover:underline hover:cursor-pointer mx-2" data-drawer-target="privacy" data-drawer-show="privacy" aria-controls="privacy">Privacy statement</a>
<a class="text-sm text-blue-600 hover:underline hover:cursor-pointer mx-2" routerLink="/pricingOverview">Pricing</a> <!-- <a class="text-sm text-blue-600 hover:underline hover:cursor-pointer mx-2" routerLink="/pricingOverview">Pricing</a> -->
</div> </div>
<div class="flex flex-col lg:flex-row items-center order-2 lg:order-3"> <div class="flex flex-col lg:flex-row items-center order-2 lg:order-3">

View File

@ -135,10 +135,10 @@
<div class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded-lg shadow dark:bg-gray-700 dark:divide-gray-600" id="user-unknown"> <div class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded-lg shadow dark:bg-gray-700 dark:divide-gray-600" id="user-unknown">
<ul class="py-2" aria-labelledby="user-menu-button"> <ul class="py-2" aria-labelledby="user-menu-button">
<li> <li>
<a (click)="login()" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">Log In</a> <a routerLink="/login" [queryParams]="{ mode: 'login' }" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">Log In</a>
</li> </li>
<li> <li>
<a routerLink="/pricing" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">Register</a> <a routerLink="/login" [queryParams]="{ mode: 'register' }" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white">Register</a>
</li> </li>
</ul> </ul>
<ul class="py-2 md:hidden"> <ul class="py-2 md:hidden">

View File

@ -11,6 +11,7 @@ import { filter, Observable, Subject, Subscription } from 'rxjs';
import { SortByOptions, User } from '../../../../../bizmatch-server/src/models/db.model'; import { SortByOptions, User } from '../../../../../bizmatch-server/src/models/db.model';
import { BusinessListingCriteria, CommercialPropertyListingCriteria, emailToDirName, KeycloakUser, KeyValueAsSortBy, UserListingCriteria } from '../../../../../bizmatch-server/src/models/main.model'; import { BusinessListingCriteria, CommercialPropertyListingCriteria, emailToDirName, KeycloakUser, KeyValueAsSortBy, UserListingCriteria } from '../../../../../bizmatch-server/src/models/main.model';
import { environment } from '../../../environments/environment'; import { environment } from '../../../environments/environment';
import { AuthService } from '../../services/auth.service';
import { CriteriaChangeService } from '../../services/criteria-change.service'; import { CriteriaChangeService } from '../../services/criteria-change.service';
import { SearchService } from '../../services/search.service'; import { SearchService } from '../../services/search.service';
import { SelectOptionsService } from '../../services/select-options.service'; import { SelectOptionsService } from '../../services/select-options.service';
@ -56,6 +57,7 @@ export class HeaderComponent {
private searchService: SearchService, private searchService: SearchService,
private criteriaChangeService: CriteriaChangeService, private criteriaChangeService: CriteriaChangeService,
public selectOptions: SelectOptionsService, public selectOptions: SelectOptionsService,
private authService: AuthService,
) {} ) {}
@HostListener('document:click', ['$event']) @HostListener('document:click', ['$event'])
handleGlobalClick(event: Event) { handleGlobalClick(event: Event) {
@ -65,7 +67,8 @@ export class HeaderComponent {
} }
} }
async ngOnInit() { async ngOnInit() {
const token = await this.keycloakService.getToken(); //const token = await this.keycloakService.getToken();
const token = await this.authService.getToken();
this.keycloakUser = map2User(token); this.keycloakUser = map2User(token);
if (this.keycloakUser) { if (this.keycloakUser) {
this.user = await this.userService.getByMail(this.keycloakUser?.email); this.user = await this.userService.getByMail(this.keycloakUser?.email);

View File

@ -0,0 +1,51 @@
<div class="flex flex-col items-center justify-center min-h-screen bg-gray-100">
<div class="bg-white p-8 rounded shadow-md w-full max-w-md">
<h2 class="text-2xl font-bold mb-6 text-center">
{{ isLoginMode ? 'Login' : 'Registrierung' }}
</h2>
<!-- Toggle Switch mit Flowbite -->
<div class="flex items-center justify-center mb-6">
<span class="mr-3 text-gray-700 font-medium">Login</span>
<label for="toggle-switch" class="inline-flex relative items-center cursor-pointer">
<input type="checkbox" id="toggle-switch" class="sr-only peer" [checked]="!isLoginMode" (change)="toggleMode()" />
<div
class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:bg-gray-700 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"
></div>
</label>
<span class="ml-3 text-gray-700 font-medium">Sign Up</span>
</div>
<!-- E-Mail Eingabe -->
<div class="mb-4">
<label for="email" class="block text-gray-700 mb-2">EMail</label>
<input id="email" type="email" [(ngModel)]="email" placeholder="Please enter EMail Address" class="w-full px-3 py-2 border rounded focus:outline-none focus:border-blue-500" />
</div>
<!-- Passwort Eingabe -->
<div class="mb-4">
<label for="password" class="block text-gray-700 mb-2">Password</label>
<input id="password" type="password" [(ngModel)]="password" placeholder="Please enter Passwort" class="w-full px-3 py-2 border rounded focus:outline-none focus:border-blue-500" />
</div>
<!-- Passwort-Bestätigung nur im Registrierungsmodus -->
<div *ngIf="!isLoginMode" class="mb-6">
<label for="confirmPassword" class="block text-gray-700 mb-2">Confirm Password</label>
<input id="confirmPassword" type="password" [(ngModel)]="confirmPassword" placeholder="Repeat Password" class="w-full px-3 py-2 border rounded focus:outline-none focus:border-blue-500" />
</div>
<div *ngIf="errorMessage" class="text-red-500 text-center mb-4">
{{ errorMessage }}
</div>
<button (click)="onSubmit()" class="w-full bg-blue-500 hover:bg-blue-600 text-white py-2 rounded-lg mb-4">
{{ isLoginMode ? 'Sign in with Email' : 'Register' }}
</button>
<div class="flex items-center justify-center my-4">
<span class="border-b w-1/5 md:w-1/4"></span>
<span class="text-xs text-center text-gray-500 uppercase mx-2">or</span>
<span class="border-b w-1/5 md:w-1/4"></span>
</div>
<button (click)="loginWithGoogle()" class="w-full bg-red-500 hover:bg-red-600 text-white py-2 rounded-lg">Continue with Google</button>
</div>
</div>

View File

@ -0,0 +1,85 @@
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { AuthService } from '../../services/auth.service';
@Component({
selector: 'app-login-register',
standalone: true,
imports: [CommonModule, FormsModule],
templateUrl: './login-register.component.html',
})
export class LoginRegisterComponent {
email: string = '';
password: string = '';
confirmPassword: string = '';
isLoginMode: boolean = true; // true: Login, false: Registration
errorMessage: string = '';
constructor(private authService: AuthService, private route: ActivatedRoute, private router: Router) {}
ngOnInit(): void {
// Set mode based on query parameter "mode"
this.route.queryParamMap.subscribe(params => {
const mode = params.get('mode');
this.isLoginMode = mode !== 'register';
});
}
toggleMode(): void {
this.isLoginMode = !this.isLoginMode;
this.errorMessage = '';
}
// Login with Email
onSubmit(): void {
this.errorMessage = '';
if (this.isLoginMode) {
this.authService
.loginWithEmail(this.email, this.password)
.then(userCredential => {
console.log('Successfully logged in:', userCredential);
this.router.navigate([`home`]);
})
.catch(error => {
console.error('Error during email login:', error);
this.errorMessage = error.message;
});
} else {
// Registration mode: also check if passwords match
if (this.password !== this.confirmPassword) {
console.error('Passwords do not match');
this.errorMessage = 'Passwords do not match.';
return;
}
this.authService
.registerWithEmail(this.email, this.password)
.then(userCredential => {
console.log('Successfully registered:', userCredential);
})
.catch(error => {
console.error('Error during registration:', error);
if (error.code === 'auth/email-already-in-use') {
this.errorMessage = 'This email address is already in use. Please try logging in.';
} else {
this.errorMessage = error.message;
}
});
}
}
// Login with Google
loginWithGoogle(): void {
this.errorMessage = '';
this.authService
.loginWithGoogle()
.then(userCredential => {
console.log('Successfully logged in with Google:', userCredential);
})
.catch(error => {
console.error('Error during Google login:', error);
this.errorMessage = error.message;
});
}
}

View File

@ -1,7 +1,8 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { RouterModule } from '@angular/router'; import { Router, RouterModule } from '@angular/router';
import { KeycloakService } from 'keycloak-angular'; import { KeycloakService } from 'keycloak-angular';
import { AuthService } from '../../services/auth.service';
@Component({ @Component({
selector: 'logout', selector: 'logout',
@ -10,8 +11,10 @@ import { KeycloakService } from 'keycloak-angular';
template: ``, template: ``,
}) })
export class LogoutComponent { export class LogoutComponent {
constructor(public keycloakService: KeycloakService) { constructor(public keycloakService: KeycloakService, private authService: AuthService, private router: Router) {
sessionStorage.removeItem('USERID'); //sessionStorage.removeItem('USERID');
keycloakService.logout(window.location.origin + '/home'); //keycloakService.logout(window.location.origin + '/home');
this.authService.logout();
this.router.navigate(['/home']);
} }
} }

View File

@ -1,42 +1,54 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; import { CanActivate, Router } from '@angular/router';
import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular'; import { AuthService } from '../services/auth.service';
import { KeycloakInitializerService } from '../services/keycloak-initializer.service';
import { createLogger } from '../utils/utils'; import { createLogger } from '../utils/utils';
const logger = createLogger('AuthGuard'); const logger = createLogger('AuthGuard');
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class AuthGuard extends KeycloakAuthGuard { // export class AuthGuard extends KeycloakAuthGuard {
constructor(protected override readonly router: Router, protected readonly keycloak: KeycloakService, private keycloakInitializer: KeycloakInitializerService) { // constructor(protected override readonly router: Router, protected readonly keycloak: KeycloakService, private keycloakInitializer: KeycloakInitializerService) {
super(router, keycloak); // super(router, keycloak);
} // }
async isAccessAllowed(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean | UrlTree> { // async isAccessAllowed(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean | UrlTree> {
logger.info(`--->AuthGuard`); // logger.info(`--->AuthGuard`);
while (!this.keycloakInitializer.initialized) { // while (!this.keycloakInitializer.initialized) {
logger.info(`Waiting 100 msec`); // logger.info(`Waiting 100 msec`);
await new Promise(resolve => setTimeout(resolve, 100)); // await new Promise(resolve => setTimeout(resolve, 100));
} // }
// Force the user to log in if currently unauthenticated. // // Force the user to log in if currently unauthenticated.
const authenticated = this.keycloak.isLoggedIn(); // const authenticated = this.keycloak.isLoggedIn();
//this.keycloak.isTokenExpired() // //this.keycloak.isTokenExpired()
if (!this.authenticated && !authenticated) { // if (!this.authenticated && !authenticated) {
await this.keycloak.login({ // await this.keycloak.login({
redirectUri: window.location.origin + state.url, // redirectUri: window.location.origin + state.url,
}); // });
// return false; // // return false;
} // }
// Get the roles required from the route. // // Get the roles required from the route.
const requiredRoles = route.data['roles']; // const requiredRoles = route.data['roles'];
// Allow the user to proceed if no additional roles are required to access the route. // // Allow the user to proceed if no additional roles are required to access the route.
if (!Array.isArray(requiredRoles) || requiredRoles.length === 0) { // if (!Array.isArray(requiredRoles) || requiredRoles.length === 0) {
// return true;
// }
// // Allow the user to proceed if all the required roles are present.
// return requiredRoles.every(role => this.roles.includes(role));
// }
// }
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
async canActivate(): Promise<boolean> {
const token = await this.authService.getToken();
if (token) {
return true; return true;
} } else {
this.router.navigate(['/login-register']);
// Allow the user to proceed if all the required roles are present. return false;
return requiredRoles.every(role => this.roles.includes(role)); }
} }
} }

View File

@ -0,0 +1,25 @@
// auth.interceptor.ts
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, from } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { AuthService } from '../services/auth.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return from(this.authService.getToken()).pipe(
switchMap(token => {
if (token) {
const clonedReq = req.clone({
setHeaders: { Authorization: `Bearer ${token}` },
});
return next.handle(clonedReq);
}
return next.handle(req);
}),
);
}
}

View File

@ -25,6 +25,7 @@ import { SharedModule } from '../../../shared/shared/shared.module';
import { createMailInfo, map2User } from '../../../utils/utils'; import { createMailInfo, map2User } from '../../../utils/utils';
// Import für Leaflet // Import für Leaflet
// Benannte Importe für Leaflet // Benannte Importe für Leaflet
import { AuthService } from '../../../services/auth.service';
import { BaseDetailsComponent } from '../base-details.component'; import { BaseDetailsComponent } from '../base-details.component';
@Component({ @Component({
selector: 'app-details-business-listing', selector: 'app-details-business-listing',
@ -80,6 +81,7 @@ export class DetailsBusinessListingComponent extends BaseDetailsComponent {
private auditService: AuditService, private auditService: AuditService,
public emailService: EMailService, public emailService: EMailService,
private geoService: GeoService, private geoService: GeoService,
private authService: AuthService,
) { ) {
super(); super();
this.router.events.subscribe(event => { this.router.events.subscribe(event => {
@ -92,7 +94,8 @@ export class DetailsBusinessListingComponent extends BaseDetailsComponent {
} }
async ngOnInit() { async ngOnInit() {
const token = await this.keycloakService.getToken(); // const token = await this.keycloakService.getToken();
const token = await this.authService.getToken();
this.keycloakUser = map2User(token); this.keycloakUser = map2User(token);
if (this.keycloakUser) { if (this.keycloakUser) {
this.user = await this.userService.getByMail(this.keycloakUser.email); this.user = await this.userService.getByMail(this.keycloakUser.email);

View File

@ -17,6 +17,7 @@ import { ValidatedNgSelectComponent } from '../../../components/validated-ng-sel
import { ValidatedTextareaComponent } from '../../../components/validated-textarea/validated-textarea.component'; import { ValidatedTextareaComponent } from '../../../components/validated-textarea/validated-textarea.component';
import { ValidationMessagesService } from '../../../components/validation-messages.service'; import { ValidationMessagesService } from '../../../components/validation-messages.service';
import { AuditService } from '../../../services/audit.service'; import { AuditService } from '../../../services/audit.service';
import { AuthService } from '../../../services/auth.service';
import { HistoryService } from '../../../services/history.service'; import { HistoryService } from '../../../services/history.service';
import { ImageService } from '../../../services/image.service'; import { ImageService } from '../../../services/image.service';
import { ListingsService } from '../../../services/listings.service'; import { ListingsService } from '../../../services/listings.service';
@ -84,13 +85,15 @@ export class DetailsCommercialPropertyListingComponent extends BaseDetailsCompon
private messageService: MessageService, private messageService: MessageService,
private auditService: AuditService, private auditService: AuditService,
private emailService: EMailService, private emailService: EMailService,
private authService: AuthService,
) { ) {
super(); super();
this.mailinfo = { sender: {}, email: '', url: environment.mailinfoUrl }; this.mailinfo = { sender: {}, email: '', url: environment.mailinfoUrl };
} }
async ngOnInit() { async ngOnInit() {
const token = await this.keycloakService.getToken(); // const token = await this.keycloakService.getToken();
const token = await this.authService.getToken();
this.keycloakUser = map2User(token); this.keycloakUser = map2User(token);
if (this.keycloakUser) { if (this.keycloakUser) {
this.user = await this.userService.getByMail(this.keycloakUser.email); this.user = await this.userService.getByMail(this.keycloakUser.email);

View File

@ -6,6 +6,7 @@ import { Observable } from 'rxjs';
import { BusinessListing, CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model'; import { BusinessListing, CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
import { KeycloakUser, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model'; import { KeycloakUser, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
import { environment } from '../../../../environments/environment'; import { environment } from '../../../../environments/environment';
import { AuthService } from '../../../services/auth.service';
import { HistoryService } from '../../../services/history.service'; import { HistoryService } from '../../../services/history.service';
import { ImageService } from '../../../services/image.service'; import { ImageService } from '../../../services/image.service';
import { ListingsService } from '../../../services/listings.service'; import { ListingsService } from '../../../services/listings.service';
@ -46,6 +47,7 @@ export class DetailsUserComponent {
private imageService: ImageService, private imageService: ImageService,
public historyService: HistoryService, public historyService: HistoryService,
public keycloakService: KeycloakService, public keycloakService: KeycloakService,
private authService: AuthService,
) {} ) {}
async ngOnInit() { async ngOnInit() {
@ -55,7 +57,8 @@ export class DetailsUserComponent {
this.businessListings = results[0]; this.businessListings = results[0];
this.commercialPropListings = results[1] as CommercialPropertyListing[]; this.commercialPropListings = results[1] as CommercialPropertyListing[];
//this.user$ = this.userService.getUserObservable(); //this.user$ = this.userService.getUserObservable();
const token = await this.keycloakService.getToken(); // const token = await this.keycloakService.getToken();
const token = await this.authService.getToken();
this.keycloakUser = map2User(token); this.keycloakUser = map2User(token);
this.companyOverview = this.sanitizer.bypassSecurityTrustHtml(this.user.companyOverview ? this.user.companyOverview : ''); this.companyOverview = this.sanitizer.bypassSecurityTrustHtml(this.user.companyOverview ? this.user.companyOverview : '');
this.offeredServices = this.sanitizer.bypassSecurityTrustHtml(this.user.offeredServices ? this.user.offeredServices : ''); this.offeredServices = this.sanitizer.bypassSecurityTrustHtml(this.user.offeredServices ? this.user.offeredServices : '');

View File

@ -5,8 +5,9 @@
<a routerLink="/account" class="text-blue-600 border border-blue-600 px-3 py-2 rounded">Account</a> <a routerLink="/account" class="text-blue-600 border border-blue-600 px-3 py-2 rounded">Account</a>
} @else { } @else {
<!-- <a routerLink="/pricing" class="text-gray-800">Pricing</a> --> <!-- <a routerLink="/pricing" class="text-gray-800">Pricing</a> -->
<a (click)="login()" class="text-blue-600 border border-blue-600 px-3 py-2 rounded">Log In</a> <a routerLink="/login" [queryParams]="{ mode: 'login' }" class="text-blue-600 border border-blue-600 px-3 py-2 rounded">Log In</a>
<a routerLink="/pricing" class="text-white bg-blue-600 px-4 py-2 rounded">Register</a> <a routerLink="/login" [queryParams]="{ mode: 'register' }" class="text-white bg-blue-600 px-4 py-2 rounded">Register</a>
<!-- <a routerLink="/login" class="text-blue-500 hover:underline">Login/Register</a> -->
} }
</div> </div>
<button (click)="toggleMenu()" class="md:hidden text-gray-600"> <button (click)="toggleMenu()" class="md:hidden text-gray-600">

View File

@ -11,6 +11,7 @@ import { BusinessListingCriteria, CityAndStateResult, CommercialPropertyListingC
import { ModalService } from '../../components/search-modal/modal.service'; import { ModalService } from '../../components/search-modal/modal.service';
import { TooltipComponent } from '../../components/tooltip/tooltip.component'; import { TooltipComponent } from '../../components/tooltip/tooltip.component';
import { AiService } from '../../services/ai.service'; import { AiService } from '../../services/ai.service';
import { AuthService } from '../../services/auth.service';
import { CriteriaChangeService } from '../../services/criteria-change.service'; import { CriteriaChangeService } from '../../services/criteria-change.service';
import { GeoService } from '../../services/geo.service'; import { GeoService } from '../../services/geo.service';
import { ListingsService } from '../../services/listings.service'; import { ListingsService } from '../../services/listings.service';
@ -78,12 +79,14 @@ export class HomeComponent {
private listingService: ListingsService, private listingService: ListingsService,
private userService: UserService, private userService: UserService,
private aiService: AiService, private aiService: AiService,
private authService: AuthService,
) {} ) {}
async ngOnInit() { async ngOnInit() {
setTimeout(() => { setTimeout(() => {
initFlowbite(); initFlowbite();
}, 0); }, 0);
const token = await this.keycloakService.getToken(); //const token = await this.keycloakService.getToken();
const token = await this.authService.getToken();
sessionStorage.removeItem('businessListings'); sessionStorage.removeItem('businessListings');
sessionStorage.removeItem('commercialPropertyListings'); sessionStorage.removeItem('commercialPropertyListings');
sessionStorage.removeItem('brokerListings'); sessionStorage.removeItem('brokerListings');

View File

@ -3,6 +3,7 @@ import { Component } from '@angular/core';
import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import { ActivatedRoute, Router, RouterModule } from '@angular/router';
import { KeycloakService } from 'keycloak-angular'; import { KeycloakService } from 'keycloak-angular';
import { lastValueFrom } from 'rxjs'; import { lastValueFrom } from 'rxjs';
import { AuthService } from '../../services/auth.service';
import { SubscriptionsService } from '../../services/subscriptions.service'; import { SubscriptionsService } from '../../services/subscriptions.service';
import { UserService } from '../../services/user.service'; import { UserService } from '../../services/user.service';
import { map2User } from '../../utils/utils'; import { map2User } from '../../utils/utils';
@ -15,9 +16,17 @@ import { map2User } from '../../utils/utils';
}) })
export class LoginComponent { export class LoginComponent {
page: string | undefined = this.activatedRoute.snapshot.params['page'] as string | undefined; page: string | undefined = this.activatedRoute.snapshot.params['page'] as string | undefined;
constructor(public userService: UserService, private activatedRoute: ActivatedRoute, private keycloakService: KeycloakService, private router: Router, private subscriptionService: SubscriptionsService) {} constructor(
public userService: UserService,
private activatedRoute: ActivatedRoute,
private keycloakService: KeycloakService,
private router: Router,
private subscriptionService: SubscriptionsService,
private authService: AuthService,
) {}
async ngOnInit() { async ngOnInit() {
const token = await this.keycloakService.getToken(); // const token = await this.keycloakService.getToken();
const token = await this.authService.getToken();
const keycloakUser = map2User(token); const keycloakUser = map2User(token);
const email = keycloakUser.email; const email = keycloakUser.email;
const user = await this.userService.getByMail(email); const user = await this.userService.getByMail(email);

View File

@ -8,6 +8,7 @@ import { User } from '../../../../../bizmatch-server/src/models/db.model';
import { Checkout, KeycloakUser } from '../../../../../bizmatch-server/src/models/main.model'; import { Checkout, KeycloakUser } from '../../../../../bizmatch-server/src/models/main.model';
import { environment } from '../../../environments/environment'; import { environment } from '../../../environments/environment';
import { AuditService } from '../../services/audit.service'; import { AuditService } from '../../services/audit.service';
import { AuthService } from '../../services/auth.service';
import { UserService } from '../../services/user.service'; import { UserService } from '../../services/user.service';
import { SharedModule } from '../../shared/shared/shared.module'; import { SharedModule } from '../../shared/shared/shared.module';
import { map2User } from '../../utils/utils'; import { map2User } from '../../utils/utils';
@ -33,10 +34,12 @@ export class PricingComponent {
private userService: UserService, private userService: UserService,
private router: Router, private router: Router,
private auditService: AuditService, private auditService: AuditService,
private authService: AuthService,
) {} ) {}
async ngOnInit() { async ngOnInit() {
const token = await this.keycloakService.getToken(); // const token = await this.keycloakService.getToken();
const token = await this.authService.getToken();
this.keycloakUser = map2User(token); this.keycloakUser = map2User(token);
if (this.keycloakUser) { if (this.keycloakUser) {
this.user = await this.userService.getByMail(this.keycloakUser.email); this.user = await this.userService.getByMail(this.keycloakUser.email);

View File

@ -25,6 +25,7 @@ import { ValidatedLocationComponent } from '../../../components/validated-locati
import { ValidatedQuillComponent } from '../../../components/validated-quill/validated-quill.component'; import { ValidatedQuillComponent } from '../../../components/validated-quill/validated-quill.component';
import { ValidatedSelectComponent } from '../../../components/validated-select/validated-select.component'; import { ValidatedSelectComponent } from '../../../components/validated-select/validated-select.component';
import { ValidationMessagesService } from '../../../components/validation-messages.service'; import { ValidationMessagesService } from '../../../components/validation-messages.service';
import { AuthService } from '../../../services/auth.service';
import { GeoService } from '../../../services/geo.service'; import { GeoService } from '../../../services/geo.service';
import { ImageService } from '../../../services/image.service'; import { ImageService } from '../../../services/image.service';
import { LoadingService } from '../../../services/loading.service'; import { LoadingService } from '../../../services/loading.service';
@ -97,6 +98,7 @@ export class AccountComponent {
private subscriptionService: SubscriptionsService, private subscriptionService: SubscriptionsService,
private datePipe: DatePipe, private datePipe: DatePipe,
private router: Router, private router: Router,
private authService: AuthService,
) {} ) {}
async ngOnInit() { async ngOnInit() {
setTimeout(() => { setTimeout(() => {
@ -105,7 +107,8 @@ export class AccountComponent {
if (this.id) { if (this.id) {
this.user = await this.userService.getById(this.id); this.user = await this.userService.getById(this.id);
} else { } else {
const token = await this.keycloakService.getToken(); // const token = await this.keycloakService.getToken();
const token = await this.authService.getToken();
const keycloakUser = map2User(token); const keycloakUser = map2User(token);
const email = keycloakUser.email; const email = keycloakUser.email;
this.user = await this.userService.getByMail(email); this.user = await this.userService.getByMail(email);

View File

@ -26,6 +26,7 @@ import { ValidatedQuillComponent } from '../../../components/validated-quill/val
import { ValidatedTextareaComponent } from '../../../components/validated-textarea/validated-textarea.component'; import { ValidatedTextareaComponent } from '../../../components/validated-textarea/validated-textarea.component';
import { ValidationMessagesService } from '../../../components/validation-messages.service'; import { ValidationMessagesService } from '../../../components/validation-messages.service';
import { ArrayToStringPipe } from '../../../pipes/array-to-string.pipe'; import { ArrayToStringPipe } from '../../../pipes/array-to-string.pipe';
import { AuthService } from '../../../services/auth.service';
import { GeoService } from '../../../services/geo.service'; import { GeoService } from '../../../services/geo.service';
import { ImageService } from '../../../services/image.service'; import { ImageService } from '../../../services/image.service';
import { LoadingService } from '../../../services/loading.service'; import { LoadingService } from '../../../services/loading.service';
@ -87,6 +88,7 @@ export class EditBusinessListingComponent {
private route: ActivatedRoute, private route: ActivatedRoute,
private keycloakService: KeycloakService, private keycloakService: KeycloakService,
private validationMessagesService: ValidationMessagesService, private validationMessagesService: ValidationMessagesService,
private authService: AuthService,
) { ) {
this.router.events.subscribe(event => { this.router.events.subscribe(event => {
if (event instanceof NavigationEnd) { if (event instanceof NavigationEnd) {
@ -103,7 +105,8 @@ export class EditBusinessListingComponent {
}); });
} }
async ngOnInit() { async ngOnInit() {
const token = await this.keycloakService.getToken(); // const token = await this.keycloakService.getToken();
const token = await this.authService.getToken();
const keycloakUser = map2User(token); const keycloakUser = map2User(token);
this.listingUser = await this.userService.getByMail(keycloakUser.email); this.listingUser = await this.userService.getByMail(keycloakUser.email);
if (this.mode === 'edit') { if (this.mode === 'edit') {

View File

@ -30,6 +30,7 @@ import { ValidatedPriceComponent } from '../../../components/validated-price/val
import { ValidatedQuillComponent } from '../../../components/validated-quill/validated-quill.component'; import { ValidatedQuillComponent } from '../../../components/validated-quill/validated-quill.component';
import { ValidationMessagesService } from '../../../components/validation-messages.service'; import { ValidationMessagesService } from '../../../components/validation-messages.service';
import { ArrayToStringPipe } from '../../../pipes/array-to-string.pipe'; import { ArrayToStringPipe } from '../../../pipes/array-to-string.pipe';
import { AuthService } from '../../../services/auth.service';
import { GeoService } from '../../../services/geo.service'; import { GeoService } from '../../../services/geo.service';
import { ImageService } from '../../../services/image.service'; import { ImageService } from '../../../services/image.service';
import { LoadingService } from '../../../services/loading.service'; import { LoadingService } from '../../../services/loading.service';
@ -128,6 +129,7 @@ export class EditCommercialPropertyListingComponent {
private messageService: MessageService, private messageService: MessageService,
private viewportRuler: ViewportRuler, private viewportRuler: ViewportRuler,
private validationMessagesService: ValidationMessagesService, private validationMessagesService: ValidationMessagesService,
private authService: AuthService,
) { ) {
// Abonniere Router-Events, um den aktiven Link zu ermitteln // Abonniere Router-Events, um den aktiven Link zu ermitteln
this.router.events.subscribe(event => { this.router.events.subscribe(event => {
@ -145,7 +147,8 @@ export class EditCommercialPropertyListingComponent {
}); });
} }
async ngOnInit() { async ngOnInit() {
const token = await this.keycloakService.getToken(); // const token = await this.keycloakService.getToken();
const token = await this.authService.getToken();
const keycloakUser = map2User(token); const keycloakUser = map2User(token);
const email = keycloakUser.email; const email = keycloakUser.email;
this.user = await this.userService.getByMail(email); this.user = await this.userService.getByMail(email);

View File

@ -9,6 +9,7 @@ import { ValidatedNgSelectComponent } from '../../../components/validated-ng-sel
import { ValidatedTextareaComponent } from '../../../components/validated-textarea/validated-textarea.component'; import { ValidatedTextareaComponent } from '../../../components/validated-textarea/validated-textarea.component';
import { ValidationMessagesService } from '../../../components/validation-messages.service'; import { ValidationMessagesService } from '../../../components/validation-messages.service';
import { AuditService } from '../../../services/audit.service'; import { AuditService } from '../../../services/audit.service';
import { AuthService } from '../../../services/auth.service';
import { MailService } from '../../../services/mail.service'; import { MailService } from '../../../services/mail.service';
import { SelectOptionsService } from '../../../services/select-options.service'; import { SelectOptionsService } from '../../../services/select-options.service';
import { UserService } from '../../../services/user.service'; import { UserService } from '../../../services/user.service';
@ -35,11 +36,13 @@ export class EmailUsComponent {
private messageService: MessageService, private messageService: MessageService,
public selectOptions: SelectOptionsService, public selectOptions: SelectOptionsService,
private auditService: AuditService, private auditService: AuditService,
private authService: AuthService,
) { ) {
this.mailinfo = createMailInfo(); this.mailinfo = createMailInfo();
} }
async ngOnInit() { async ngOnInit() {
const token = await this.keycloakService.getToken(); // const token = await this.keycloakService.getToken();
const token = await this.authService.getToken();
this.keycloakUser = map2User(token); this.keycloakUser = map2User(token);
if (this.keycloakUser) { if (this.keycloakUser) {
this.user = await this.userService.getByMail(this.keycloakUser.email); this.user = await this.userService.getByMail(this.keycloakUser.email);

View File

@ -4,6 +4,7 @@ import { BusinessListing, CommercialPropertyListing } from '../../../../../../bi
import { KeycloakUser } from '../../../../../../bizmatch-server/src/models/main.model'; import { KeycloakUser } from '../../../../../../bizmatch-server/src/models/main.model';
import { ConfirmationComponent } from '../../../components/confirmation/confirmation.component'; import { ConfirmationComponent } from '../../../components/confirmation/confirmation.component';
import { ConfirmationService } from '../../../components/confirmation/confirmation.service'; import { ConfirmationService } from '../../../components/confirmation/confirmation.service';
import { AuthService } from '../../../services/auth.service';
import { ListingsService } from '../../../services/listings.service'; import { ListingsService } from '../../../services/listings.service';
import { SelectOptionsService } from '../../../services/select-options.service'; import { SelectOptionsService } from '../../../services/select-options.service';
import { SharedModule } from '../../../shared/shared/shared.module'; import { SharedModule } from '../../../shared/shared/shared.module';
@ -20,9 +21,16 @@ export class FavoritesComponent {
user: KeycloakUser; user: KeycloakUser;
// listings: Array<ListingType> = []; //= dataListings as unknown as Array<BusinessListing>; // listings: Array<ListingType> = []; //= dataListings as unknown as Array<BusinessListing>;
favorites: Array<BusinessListing | CommercialPropertyListing>; favorites: Array<BusinessListing | CommercialPropertyListing>;
constructor(public keycloakService: KeycloakService, private listingsService: ListingsService, public selectOptions: SelectOptionsService, private confirmationService: ConfirmationService) {} constructor(
public keycloakService: KeycloakService,
private listingsService: ListingsService,
public selectOptions: SelectOptionsService,
private confirmationService: ConfirmationService,
private authService: AuthService,
) {}
async ngOnInit() { async ngOnInit() {
const token = await this.keycloakService.getToken(); // const token = await this.keycloakService.getToken();
const token = await this.authService.getToken();
this.user = map2User(token); this.user = map2User(token);
const result = await Promise.all([await this.listingsService.getFavoriteListings('business'), await this.listingsService.getFavoriteListings('commercialProperty')]); const result = await Promise.all([await this.listingsService.getFavoriteListings('business'), await this.listingsService.getFavoriteListings('commercialProperty')]);
this.favorites = [...result[0], ...result[1]]; this.favorites = [...result[0], ...result[1]];

View File

@ -6,6 +6,7 @@ import { ConfirmationComponent } from '../../../components/confirmation/confirma
import { ConfirmationService } from '../../../components/confirmation/confirmation.service'; import { ConfirmationService } from '../../../components/confirmation/confirmation.service';
import { MessageComponent } from '../../../components/message/message.component'; import { MessageComponent } from '../../../components/message/message.component';
import { MessageService } from '../../../components/message/message.service'; import { MessageService } from '../../../components/message/message.service';
import { AuthService } from '../../../services/auth.service';
import { ListingsService } from '../../../services/listings.service'; import { ListingsService } from '../../../services/listings.service';
import { SelectOptionsService } from '../../../services/select-options.service'; import { SelectOptionsService } from '../../../services/select-options.service';
import { UserService } from '../../../services/user.service'; import { UserService } from '../../../services/user.service';
@ -32,10 +33,12 @@ export class MyListingComponent {
public selectOptions: SelectOptionsService, public selectOptions: SelectOptionsService,
private messageService: MessageService, private messageService: MessageService,
private confirmationService: ConfirmationService, private confirmationService: ConfirmationService,
private authService: AuthService,
) {} ) {}
async ngOnInit() { async ngOnInit() {
// const keycloakUser = this.userService.getKeycloakUser(); // const keycloakUser = this.userService.getKeycloakUser();
const token = await this.keycloakService.getToken(); // const token = await this.keycloakService.getToken();
const token = await this.authService.getToken();
const keycloakUser = map2User(token); const keycloakUser = map2User(token);
const email = keycloakUser.email; const email = keycloakUser.email;
this.user = await this.userService.getByMail(email); this.user = await this.userService.getByMail(email);

View File

@ -4,6 +4,7 @@ import { Router, RouterModule } from '@angular/router';
import { KeycloakService } from 'keycloak-angular'; import { KeycloakService } from 'keycloak-angular';
import { User } from '../../../../../bizmatch-server/src/models/db.model'; import { User } from '../../../../../bizmatch-server/src/models/db.model';
import { AuditService } from '../../services/audit.service'; import { AuditService } from '../../services/audit.service';
import { AuthService } from '../../services/auth.service';
import { UserService } from '../../services/user.service'; import { UserService } from '../../services/user.service';
import { map2User } from '../../utils/utils'; import { map2User } from '../../utils/utils';
@ -18,12 +19,13 @@ export class SuccessComponent {
user: User; user: User;
maxAttemptsReached: boolean = false; // Neue Variable hinzufügen maxAttemptsReached: boolean = false; // Neue Variable hinzufügen
constructor(private keycloakService: KeycloakService, private userService: UserService, private auditService: AuditService, private router: Router) {} constructor(private keycloakService: KeycloakService, private userService: UserService, private auditService: AuditService, private router: Router, private authService: AuthService) {}
async ngOnInit() { async ngOnInit() {
let email = null; let email = null;
try { try {
const token = await this.keycloakService.getToken(); // const token = await this.keycloakService.getToken();
const token = await this.authService.getToken();
const keycloakUser = map2User(token); const keycloakUser = map2User(token);
email = keycloakUser.email; email = keycloakUser.email;
this.user = await this.userService.getByMail(email); this.user = await this.userService.getByMail(email);

View File

@ -0,0 +1,131 @@
// auth.service.ts
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Injectable, inject } from '@angular/core';
import { FirebaseApp } from '@angular/fire/app';
import { GoogleAuthProvider, UserCredential, createUserWithEmailAndPassword, getAuth, sendEmailVerification, signInWithEmailAndPassword, signInWithPopup } from 'firebase/auth';
import { firstValueFrom } from 'rxjs';
import { environment } from '../../environments/environment';
@Injectable({
providedIn: 'root',
})
export class AuthService {
private app = inject(FirebaseApp);
private auth = getAuth(this.app);
private http = inject(HttpClient);
// Registrierung mit Email und Passwort
async registerWithEmail(email: string, password: string): Promise<UserCredential> {
const userCredential = await createUserWithEmailAndPassword(this.auth, email, password);
// E-Mail-Verifizierung senden
if (userCredential.user) {
await sendEmailVerification(userCredential.user);
}
// Token, RefreshToken und ggf. photoURL speichern
const token = await userCredential.user.getIdToken();
localStorage.setItem('authToken', token);
localStorage.setItem('refreshToken', userCredential.user.refreshToken);
if (userCredential.user.photoURL) {
localStorage.setItem('photoURL', userCredential.user.photoURL);
}
return userCredential;
}
// Login mit Email und Passwort
loginWithEmail(email: string, password: string): Promise<UserCredential> {
return signInWithEmailAndPassword(this.auth, email, password).then(async userCredential => {
if (userCredential.user) {
const token = await userCredential.user.getIdToken();
localStorage.setItem('authToken', token);
localStorage.setItem('refreshToken', userCredential.user.refreshToken);
if (userCredential.user.photoURL) {
localStorage.setItem('photoURL', userCredential.user.photoURL);
}
}
return userCredential;
});
}
// Login mit Google
loginWithGoogle(): Promise<UserCredential> {
const provider = new GoogleAuthProvider();
return signInWithPopup(this.auth, provider).then(async userCredential => {
if (userCredential.user) {
const token = await userCredential.user.getIdToken();
localStorage.setItem('authToken', token);
localStorage.setItem('refreshToken', userCredential.user.refreshToken);
if (userCredential.user.photoURL) {
localStorage.setItem('photoURL', userCredential.user.photoURL);
}
}
return userCredential;
});
}
// Logout: Token, RefreshToken und photoURL entfernen
logout(): Promise<void> {
localStorage.removeItem('authToken');
localStorage.removeItem('refreshToken');
localStorage.removeItem('photoURL');
return this.auth.signOut();
}
// Prüft, ob ein Token noch gültig ist (über die "exp"-Eigenschaft)
private isTokenValid(token: string): boolean {
try {
const payloadBase64 = token.split('.')[1];
const payloadJson = atob(payloadBase64.replace(/-/g, '+').replace(/_/g, '/'));
const payload = JSON.parse(payloadJson);
const exp = payload.exp;
const now = Math.floor(Date.now() / 1000);
return exp > now;
} catch (e) {
return false;
}
}
// Versucht, mit dem RefreshToken einen neuen Access Token zu erhalten
async refreshToken(): Promise<string | null> {
const storedRefreshToken = localStorage.getItem('refreshToken');
if (!storedRefreshToken) {
return null;
}
const apiKey = environment.firebaseConfig.apiKey; // Stelle sicher, dass dieser Wert in Deiner environment.ts gesetzt ist
const url = `https://securetoken.googleapis.com/v1/token?key=${apiKey}`;
const body = new HttpParams().set('grant_type', 'refresh_token').set('refresh_token', storedRefreshToken);
const headers = new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded' });
try {
const response: any = await firstValueFrom(this.http.post(url, body.toString(), { headers }));
// response enthält z.B. id_token, refresh_token, expires_in etc.
const newToken = response.id_token;
const newRefreshToken = response.refresh_token;
localStorage.setItem('authToken', newToken);
localStorage.setItem('refreshToken', newRefreshToken);
return newToken;
} catch (error) {
console.error('Error refreshing token:', error);
return null;
}
}
/**
* Gibt einen gültigen Token zurück.
* Falls der gespeicherte Token noch gültig ist, wird er zurückgegeben.
* Ansonsten wird versucht, einen neuen Token mit dem RefreshToken zu holen.
* Ist auch das nicht möglich, wird null zurückgegeben.
*/
async getToken(): Promise<string | null> {
const token = localStorage.getItem('authToken');
if (token && this.isTokenValid(token)) {
return token;
} else {
return await this.refreshToken();
}
}
}

View File

@ -181,7 +181,7 @@ export function routeListingWithState(router: Router, value: string, data: any)
} }
} }
export function map2User(jwt: string): KeycloakUser { export function map2User(jwt: string | null): KeycloakUser {
if (jwt) { if (jwt) {
const token = jwtDecode<JwtToken>(jwt); const token = jwtDecode<JwtToken>(jwt);
return { return {

View File

@ -12,4 +12,13 @@ export const environment_base = {
redirectUri: 'https://dev.bizmatch.net', redirectUri: 'https://dev.bizmatch.net',
}, },
ipinfo_token: '7029590fb91214', ipinfo_token: '7029590fb91214',
firebaseConfig: {
apiKey: 'AIzaSyBqVutQqdgUzwD9tKiKJrJq2Q6rD1hNdzw',
authDomain: 'bizmatch-net.firebaseapp.com',
projectId: 'bizmatch-net',
storageBucket: 'bizmatch-net.firebasestorage.app',
messagingSenderId: '1065122571067',
appId: '1:1065122571067:web:1124571ab67bc0f5240d1e',
measurementId: 'G-MHVDK1KSWV',
},
}; };