Skip to content

Commit

Permalink
Feat/choose backend (#81)
Browse files Browse the repository at this point in the history
* fix: optimize login flow

Signed-off-by: Mirko Mollik <[email protected]>

* add login flow to extension

Signed-off-by: Mirko Mollik <[email protected]>

* chore: fix lint issues

Signed-off-by: Mirko Mollik <[email protected]>

---------

Signed-off-by: Mirko Mollik <[email protected]>
  • Loading branch information
cre8 authored Jul 9, 2024
1 parent ed97b66 commit 89a5bf4
Show file tree
Hide file tree
Showing 45 changed files with 397 additions and 108 deletions.
4 changes: 3 additions & 1 deletion apps/holder-app/src/app/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ import {
ApiModule,
Configuration,
AuthServiceInterface,
ConfigService,
} from '@credhub/holder-shared';
import { AuthService } from './auth/auth.service';
import { provideServiceWorker } from '@angular/service-worker';
import { ConfigService } from './config.service';
import * as Sentry from '@sentry/angular';
import { environment } from '../environments/environment';

Sentry.init({
dsn: environment.sentryDsn,
enabled: !isDevMode(),
integrations: [
Sentry.browserTracingIntegration(),
Sentry.replayIntegration(),
Expand Down Expand Up @@ -73,6 +74,7 @@ export const appConfig: ApplicationConfig = {
resourceServer: {
sendAccessToken: true,
customUrlValidation: (url: string) => {
if (configService.isConfigUrl(url)) return true;
// we need to ignore calls to assets to fetch the config file
if (url.startsWith('/assets')) return true;
return url.startsWith(configService.getConfig('backendUrl'));
Expand Down
3 changes: 1 addition & 2 deletions apps/holder-app/src/app/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import { Router } from '@angular/router';
import { AuthConfig, OAuthService } from 'angular-oauth2-oidc';
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
import { filter, map } from 'rxjs/operators';
import { AuthServiceInterface } from '@credhub/holder-shared';
import { ConfigService } from '../config.service';
import { AuthServiceInterface, ConfigService } from '@credhub/holder-shared';

@Injectable({ providedIn: 'root' })
export class AuthService implements AuthServiceInterface {
Expand Down
37 changes: 0 additions & 37 deletions apps/holder-app/src/app/config.service.ts

This file was deleted.

7 changes: 6 additions & 1 deletion apps/holder-app/src/app/login/login.component.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
<div fxLayout="column" fxLayoutAlign="space-around center" id="content">
<button mat-flat-button color="primary" (click)="login()">Login</button>
<div fxLayout="column" fxLayoutGap="16px">
<img src="assets/icons/icon.svg" alt="Wallet" />
<h3>Login into {{ name }}</h3>
<button mat-flat-button color="primary" (click)="login()">Login</button>
<button mat-button (click)="changeBackend()">Change backend</button>
</div>
</div>
33 changes: 29 additions & 4 deletions apps/holder-app/src/app/login/login.component.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,47 @@
import { Component } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../auth/auth.service';
import { MatButtonModule } from '@angular/material/button';
import { ActivatedRoute } from '@angular/router';
import { FlexLayoutModule } from 'ng-flex-layout';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { firstValueFrom } from 'rxjs';
import { BackendDialogComponent, ConfigService } from '@credhub/holder-shared';

@Component({
selector: 'app-login',
standalone: true,
imports: [MatButtonModule, FlexLayoutModule],
imports: [MatButtonModule, FlexLayoutModule, MatDialogModule],
templateUrl: './login.component.html',
styleUrl: './login.component.scss',
})
export class LoginComponent {
constructor(public authService: AuthService, private route: ActivatedRoute) {}
export class LoginComponent implements OnInit {
name!: string;

constructor(
public authService: AuthService,
private route: ActivatedRoute,
private matDialog: MatDialog,
private configService: ConfigService
) {}

ngOnInit(): void {
this.name = this.configService.getConfig('name');
}

login() {
// get the targeturl when passed in the query params
const targetUrl = this.route.snapshot.queryParams['targetUrl'];
this.authService.login(targetUrl !== 'login' ? targetUrl : null);
}

/**
* Open the dialog to change the backend. Reload the page after the dialog is closed so the new backend is used when the application starts.
*/
changeBackend() {
firstValueFrom(
this.matDialog
.open(BackendDialogComponent, { disableClose: true })
.afterClosed()
).then(() => window.location.reload());
}
}
5 changes: 1 addition & 4 deletions apps/holder-app/src/assets/config/config.example.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
{
"backendUrl": "http://localhost:3000",
"oidcUrl": "http://host.docker.internal:8080/realms/wallet",
"oidcClient": "wallet",
"oidcAllowHttp": true
"backendUrl": "http://localhost:3000"
}
5 changes: 1 addition & 4 deletions apps/holder-app/src/assets/config/config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
{
"backendUrl": "http://localhost:3000",
"oidcUrl": "http://host.docker.internal:8080/realms/wallet",
"oidcClient": "wallet",
"oidcAllowHttp": true
"backendUrl": "http://localhost:3000"
}
Binary file added apps/holder-app/src/assets/icons/144x144.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/holder-app/src/assets/icons/192x192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/holder-app/src/assets/icons/512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file removed apps/holder-app/src/assets/icons/icon-192.png
Binary file not shown.
Binary file not shown.
Binary file removed apps/holder-app/src/assets/icons/icon-512.png
Binary file not shown.
6 changes: 6 additions & 0 deletions apps/holder-app/src/assets/icons/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed apps/holder-app/src/favicon.ico
Binary file not shown.
1 change: 1 addition & 0 deletions apps/holder-app/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
/>
<link rel="manifest" href="manifest.webmanifest" />
<meta name="theme-color" content="#D7E3FF" />
<link rel="icon" type="image/png" href="assets/icons/192x192.png" />
</head>
<body class="mat-app-background">
<app-root></app-root>
Expand Down
13 changes: 4 additions & 9 deletions apps/holder-app/src/manifest.webmanifest
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,18 @@
],
"icons": [
{
"src": "assets/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "assets/icons/icon-512.png",
"sizes": "512x512",
"src": "assets/icons/144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "assets/icons/icon-192-maskable.png",
"src": "assets/icons/192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "assets/icons/icon-512-maskable.png",
"src": "assets/icons/512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
Expand Down
5 changes: 4 additions & 1 deletion apps/holder-app/tsconfig.editor.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"extends": "./tsconfig.json",
"include": ["src/**/*.ts"],
"include": [
"src/**/*.ts",
"../../libs/holder-shared/src/lib/config.service.ts"
],
"compilerOptions": {},
"exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"]
}
16 changes: 12 additions & 4 deletions apps/holder-backend/src/app/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import { Controller, Delete, UseGuards } from '@nestjs/common';
import { Controller, Delete, Get, UseGuards } from '@nestjs/common';
import { ApiOAuth2, ApiOperation, ApiTags } from '@nestjs/swagger';
import { AuthGuard, AuthenticatedUser } from 'nest-keycloak-connect';
import { AuthGuard, AuthenticatedUser, Public } from 'nest-keycloak-connect';
import { KeycloakUser } from './user';
import { AuthService } from './auth.service';
import { EndpointResponse } from './dto/endpoint-response.dto';

@UseGuards(AuthGuard)
@ApiOAuth2([])
@ApiTags('auth')
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}

@ApiOperation({ summary: 'give information about the oidc provider' })
@Public()
@Get()
endpoints(): EndpointResponse {
return this.authService.endpoints();
}

@UseGuards(AuthGuard)
@ApiOAuth2([])
@ApiOperation({ summary: 'delete account' })
@Delete()
deleteAccount(@AuthenticatedUser() user: KeycloakUser) {
Expand Down
4 changes: 4 additions & 0 deletions apps/holder-backend/src/app/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export class AuthService {
@Inject(oidcclientName) private oidcClient: OIDCClient
) {}

endpoints() {
return this.oidcClient.endpoints();
}

async deleteAccount(userId: string) {
const event = new UserDeletedEvent();
event.id = userId;
Expand Down
6 changes: 6 additions & 0 deletions apps/holder-backend/src/app/auth/dto/endpoint-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class EndpointResponse {
oidcUrl: string;
oidcClient: string;
oidcAllowHttp: boolean;
name: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { UserDeletedEvent } from '../auth.service';
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
import { EndpointResponse } from '../dto/endpoint-response.dto';

/**
* Keycloak OIDC client implementation
Expand All @@ -25,6 +26,15 @@ export class KeycloakOIDCClient implements OIDCClient {
this.clientSecret = this.configService.get('OIDC_ADMIN_CLIENT_SECRET');
}

endpoints(): EndpointResponse {
return {
oidcUrl: `${this.keycloakUrl}/realms/${this.realm}`,
oidcClient: this.configService.get('OIDC_PUBLIC_CLIENT_ID'),
oidcAllowHttp: true,
name: this.configService.get('WEBAUTHN_RP_NAME'),
};
}

/**
* Get access token from Keycloak
* @returns
Expand Down
2 changes: 2 additions & 0 deletions apps/holder-backend/src/app/auth/oidc-client/oidc-client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { UserDeletedEvent } from '../auth.service';
import { EndpointResponse } from '../dto/endpoint-response.dto';

export abstract class OIDCClient {
abstract endpoints(): EndpointResponse;
abstract userDeleteEvent(payload: UserDeletedEvent): void;
}
25 changes: 23 additions & 2 deletions apps/holder-browser-extension/src/app/app.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { type ApplicationConfig, importProvidersFrom } from '@angular/core';
import {
APP_INITIALIZER,
type ApplicationConfig,
importProvidersFrom,
} from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideHttpClient } from '@angular/common/http';
import { HttpClient, provideHttpClient } from '@angular/common/http';
import { provideAnimations } from '@angular/platform-browser/animations';
import { MAT_DIALOG_DEFAULT_OPTIONS } from '@angular/material/dialog';
import { provideOAuthClient } from 'angular-oauth2-oidc';
Expand All @@ -10,6 +14,7 @@ import {
ApiModule,
Configuration,
AuthServiceInterface,
ConfigService,
} from '@credhub/holder-shared';
import { AuthService } from './auth/auth.service';
import { HashLocationStrategy, LocationStrategy } from '@angular/common';
Expand All @@ -27,6 +32,22 @@ export const appConfig: ApplicationConfig = {
provide: AuthServiceInterface,
useClass: AuthService,
},
{
provide: APP_INITIALIZER,
useFactory:
(
authService: AuthService,
configService: ConfigService,
httpClient: HttpClient
) =>
async () => {
await configService.appConfigLoader(httpClient);
//authService.init();
//await authService.runInitialLoginSequence();
},
deps: [AuthService, ConfigService, HttpClient],
multi: true,
},
{
provide: Configuration,
useFactory: (authService: AuthService) =>
Expand Down
29 changes: 7 additions & 22 deletions apps/holder-browser-extension/src/app/login/login.component.html
Original file line number Diff line number Diff line change
@@ -1,23 +1,8 @@
<div fxLayout="column" fxLayoutAlign="start center" fxLayoutGap="16px">
<img src="assets/images/icon.png" alt="Logo" class="logo" />
<form
[formGroup]="form"
(submit)="login()"
fxLayout="column"
fxLayoutAlign="start space-between"
fxLayoutGap="32px"
>
<button
mat-flat-button
color="primary"
type="submit"
[disabled]="form.invalid"
>
Login
</button>
<mat-form-field style="display: none">
<mat-label>Endpoint</mat-label>
<input matInput formControlName="endpoint" />
</mat-form-field>
</form>
<div fxLayout="column" fxLayoutAlign="space-around center" id="content">
<div fxLayout="column" fxLayoutGap="16px">
<img src="assets/images/icon.svg" alt="Wallet" />
<h3>Login into {{ name }}</h3>
<button mat-flat-button color="primary" (click)="login()">Login</button>
<button mat-button (click)="changeBackend()">Change backend</button>
</div>
</div>
Loading

0 comments on commit 89a5bf4

Please sign in to comment.