This commit is contained in:
Constantin Simonis 2025-03-06 11:01:59 +01:00
parent 32aa753452
commit 33683f565f
No known key found for this signature in database
GPG key ID: 3878FF77C24AF4D2
11 changed files with 330 additions and 294 deletions

File diff suppressed because it is too large Load diff

View file

@ -29,8 +29,7 @@
"@stripe/stripe-js": "^5.6.0", "@stripe/stripe-js": "^5.6.0",
"@tailwindcss/postcss": "^4.0.3", "@tailwindcss/postcss": "^4.0.3",
"gsap": "^3.12.7", "gsap": "^3.12.7",
"keycloak-angular": "^16.0.1", "angular-oauth2-oidc": "^19.0.0",
"keycloak-js": "^25.0.5",
"postcss": "^8.5.1", "postcss": "^8.5.1",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tailwindcss": "^4.0.3", "tailwindcss": "^4.0.3",

View file

@ -1,13 +1,12 @@
import { Component, ChangeDetectionStrategy } from '@angular/core'; import { ChangeDetectionStrategy, Component } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router'; import { RouterOutlet } from '@angular/router';
import { KeycloakAngularModule } from 'keycloak-angular';
import { FooterComponent } from './shared/components/footer/footer.component'; import { FooterComponent } from './shared/components/footer/footer.component';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
standalone: true, standalone: true,
imports: [CommonModule, RouterOutlet, KeycloakAngularModule, FooterComponent], imports: [CommonModule, RouterOutlet, FooterComponent],
providers: [], providers: [],
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrl: './app.component.css', styleUrl: './app.component.css',

View file

@ -1,59 +1,19 @@
import { import { ApplicationConfig, provideExperimentalZonelessChangeDetection } from '@angular/core';
APP_INITIALIZER,
ApplicationConfig,
provideExperimentalZonelessChangeDetection,
} from '@angular/core';
import { provideRouter } from '@angular/router'; import { provideRouter } from '@angular/router';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { routes } from './app.routes'; import { routes } from './app.routes';
import { import { provideHttpClient } from '@angular/common/http';
KeycloakAngularModule,
KeycloakBearerInterceptor,
KeycloakService,
} from 'keycloak-angular';
import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { provideOAuthClient } from 'angular-oauth2-oidc';
export const initializeKeycloak = (keycloak: KeycloakService) => async () =>
keycloak.init({
config: {
url: 'http://localhost:9090',
realm: 'LF12',
clientId: 'lf12',
},
loadUserProfileAtStartUp: true,
initOptions: {
onLoad: 'check-sso',
silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html',
checkLoginIframe: false,
redirectUri: window.location.origin + '/',
},
});
function initializeApp(keycloak: KeycloakService): () => Promise<boolean> {
return () => initializeKeycloak(keycloak)();
}
export const appConfig: ApplicationConfig = { export const appConfig: ApplicationConfig = {
providers: [ providers: [
provideRouter(routes), provideRouter(routes),
KeycloakAngularModule,
FontAwesomeModule, FontAwesomeModule,
{ provideHttpClient(),
provide: APP_INITIALIZER,
useFactory: initializeApp,
multi: true,
deps: [KeycloakService],
},
KeycloakService,
provideHttpClient(withInterceptorsFromDi()),
provideExperimentalZonelessChangeDetection(), provideExperimentalZonelessChangeDetection(),
{
provide: HTTP_INTERCEPTORS,
useClass: KeycloakBearerInterceptor,
multi: true,
},
provideAnimationsAsync(), provideAnimationsAsync(),
provideOAuthClient(),
], ],
}; };

View file

@ -8,7 +8,7 @@ export const routes: Routes = [
component: LandingComponent, component: LandingComponent,
}, },
{ {
path: 'login/success', path: 'auth/callback',
loadComponent: () => import('./feature/login-success/login-success.component'), loadComponent: () => import('./feature/login-success/login-success.component'),
}, },
{ {

View file

@ -1,12 +1,12 @@
import { CanActivateFn, Router } from '@angular/router'; import { CanActivateFn, Router } from '@angular/router';
import { inject } from '@angular/core'; import { inject } from '@angular/core';
import { KeycloakService } from 'keycloak-angular'; import { AuthService } from './service/auth.service';
export const authGuard: CanActivateFn = async () => { export const authGuard: CanActivateFn = async () => {
const keycloakService = inject(KeycloakService); const authService = inject(AuthService);
const router = inject(Router); const router = inject(Router);
if (keycloakService.isLoggedIn()) { if (authService.isLoggedIn()) {
return true; return true;
} }

View file

@ -1,7 +1,6 @@
import { ChangeDetectionStrategy, Component, inject, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, inject, OnInit } from '@angular/core';
import { UserService } from '../../service/user.service';
import { KeycloakService } from 'keycloak-angular';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { AuthService } from '../../service/auth.service';
@Component({ @Component({
selector: 'app-login-success', selector: 'app-login-success',
@ -12,15 +11,10 @@ import { Router } from '@angular/router';
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export default class LoginSuccessComponent implements OnInit { export default class LoginSuccessComponent implements OnInit {
private userService: UserService = inject(UserService);
private keycloakService: KeycloakService = inject(KeycloakService);
private router: Router = inject(Router); private router: Router = inject(Router);
private authService: AuthService = inject(AuthService);
async ngOnInit() { async ngOnInit() {
const userProfile = await this.keycloakService.loadUserProfile(); console.log(this.authService.getAccessToken());
const user = await this.userService.getOrCreateUser(userProfile); (this.authService.getUserInfo().then(console.log));
sessionStorage.setItem('user', JSON.stringify(user));
this.router.navigate(['home']);
} }
} }

View file

@ -1,5 +1,5 @@
export interface User { export interface User {
keycloakId: string; authentikId: string;
username: string; username: string;
balance: number; balance: number;
} }

View file

@ -0,0 +1,52 @@
import { inject, Injectable } from '@angular/core';
import { Subject } from 'rxjs';
import { AuthConfig, OAuthService } from 'angular-oauth2-oidc';
@Injectable({
providedIn: 'root',
})
export class AuthService {
private readonly authConfig: AuthConfig = {
issuer: 'https://oauth.simonis.lol/application/o/casino-dev/',
clientId: 'MDqjm1kcWKuZfqHJXjxwAV20i44aT7m4VhhTL3Nm',
redirectUri: window.location.origin + '/auth/callback',
responseType: 'code',
scope: 'openid profile email',
showDebugInformation: true,
oidc: true,
requestAccessToken: true,
};
private isAuthenticated = new Subject<boolean>();
private oauthService: OAuthService = inject(OAuthService);
constructor() {
this.oauthService.configure(this.authConfig);
this.oauthService.loadDiscoveryDocumentAndTryLogin().then(() => {
this.isAuthenticated.next(this.oauthService.hasValidAccessToken());
});
}
login() {
this.oauthService.initLoginFlow();
}
logout() {
this.oauthService.logOut();
this.isAuthenticated.next(false);
}
getAccessToken() {
return this.oauthService.getAccessToken();
}
getUserInfo() {
return this.oauthService.loadUserProfile();
}
isLoggedIn() {
return this.oauthService.hasValidAccessToken();
}
}

View file

@ -1,6 +1,5 @@
import { inject, Injectable } from '@angular/core'; import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { KeycloakProfile } from 'keycloak-js';
import { catchError, EMPTY, Observable } from 'rxjs'; import { catchError, EMPTY, Observable } from 'rxjs';
import { User } from '../model/User'; import { User } from '../model/User';
@ -20,23 +19,8 @@ export class UserService {
public createUser(id: string, username: string): Observable<User> { public createUser(id: string, username: string): Observable<User> {
return this.http.post<User>('/backend/user', { return this.http.post<User>('/backend/user', {
keycloakId: id, authentikId: id,
username: username, username: username,
}); });
} }
public async getOrCreateUser(userProfile: KeycloakProfile) {
if (userProfile.id == null) {
return;
}
return await this.getUser(userProfile.id)
.toPromise()
.then(async (user) => {
if (user) {
return user;
}
return await this.createUser(userProfile.id ?? '', userProfile.username ?? '').toPromise();
});
}
} }

View file

@ -1,9 +1,9 @@
import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@angular/core'; import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@angular/core';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { KeycloakService } from 'keycloak-angular';
import { UserService } from '../../../service/user.service'; import { UserService } from '../../../service/user.service';
import { CurrencyPipe } from '@angular/common'; import { CurrencyPipe } from '@angular/common';
import { AuthService } from '../../../service/auth.service';
@Component({ @Component({
selector: 'app-navbar', selector: 'app-navbar',
templateUrl: './navbar.component.html', templateUrl: './navbar.component.html',
@ -13,8 +13,8 @@ import { CurrencyPipe } from '@angular/common';
}) })
export class NavbarComponent implements OnInit { export class NavbarComponent implements OnInit {
isMenuOpen = false; isMenuOpen = false;
private keycloakService: KeycloakService = inject(KeycloakService); private authService: AuthService = inject(AuthService);
isLoggedIn = this.keycloakService.isLoggedIn(); isLoggedIn = this.authService.isLoggedIn();
private userService = inject(UserService); private userService = inject(UserService);
private user = this.userService.getCurrentUser(); private user = this.userService.getCurrentUser();
@ -29,15 +29,14 @@ export class NavbarComponent implements OnInit {
login() { login() {
try { try {
const baseUrl = window.location.origin; this.authService.login();
this.keycloakService.login({ redirectUri: `${baseUrl}/login/success` });
} catch (error) { } catch (error) {
console.error('Login failed:', error); console.error('Login failed:', error);
} }
} }
logout() { logout() {
this.keycloakService.logout(); this.authService.logout();
} }
toggleMenu() { toggleMenu() {