Skip to content

Commit

Permalink
feat(webauthn-angular): Add Login form with register form
Browse files Browse the repository at this point in the history
  • Loading branch information
SkyZeroZx committed Oct 13, 2024
1 parent 5d2ddb0 commit 7a70335
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
<mat-card-header>
<div class="authenticator__header">
<p class="authenticator__header__title">{{ item.device }}</p>
<button mat-icon-button>
<mat-icon fontIcon="delete_forever" class="icon-lg" />
</button>
</div>
</mat-card-header>
<mat-divider />
Expand All @@ -24,8 +21,8 @@
<dd>{{ item.credentialID }}</dd>

<dt>Public Key</dt>
<dd >
{{ item.credentialPublicKey | bufferToBase64}}
<dd>
{{ item.credentialPublicKey | bufferToBase64 }}
</dd>
</div>
</mat-card-content>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
<mat-dialog-content>
<div class="register">
<h4 class="register__title" mat-dialog-title>Login</h4>
<form [formGroup]="loginForm" class="register__form">
<mat-form-field>
<mat-label>UserName</mat-label>
<input matInput formControlName="username" type="text" placeholder="Username" />
</mat-form-field>
<div class="login">
<h4 class="login__title" mat-dialog-title>Log In</h4>
<form [formGroup]="loginForm" class="login__form">
<mat-form-field>
<mat-label>UserName</mat-label>
<input matInput formControlName="username" type="text" placeholder="Username" />
</mat-form-field>

<mat-form-field>
<mat-label>Password</mat-label>
<input matInput formControlName="password" type="password" placeholder="Password" />
</mat-form-field>
<button mat-flat-button (click)="login()" [disabled]="loginForm.invalid">
Login
</button>
</form>
</div>
</mat-dialog-content>
<mat-form-field>
<mat-label>Password</mat-label>
<input matInput formControlName="password" type="password" placeholder="Password" />
</mat-form-field>
</form>
</div>

<div mat-dialog-actions class="login__actions">
<button mat-flat-button (click)="login()" [disabled]="loginForm.invalid || isLoading()">
Log In
</button>

<button mat-button (click)="goBack()" [disabled]="isLoading()">Go Back</button>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.login {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1rem;

&__title {
text-align: center;
}

&__form {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1rem;
}

&__actions {
display: flex;
flex-direction: column;
gap: 1rem;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,21 @@ export class LoginComponent {
private readonly authService = inject(AuthService);
private readonly snackBar = inject(MatSnackBar);
private readonly formBuilder = inject(FormBuilder);
readonly isLoading = this.authService.isLoading;

onLogin = output<void>();
onGoBack = output<void>();

loginForm: FormGroup<TypedFormControls<LoginUser>> = this.formBuilder.group({
username: ['', [Validators.required, Validators.minLength(5), Validators.maxLength(255)]],
password: ['', [Validators.required, Validators.minLength(5), Validators.maxLength(128)]]
});

goBack() {
this.onGoBack.emit();
this.loginForm.reset();
}

login() {
const { username, password } = this.loginForm.getRawValue();
this.authService.login(username, password).subscribe({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,40 @@
<div class="register">
<mat-dialog-content style="overflow: hidden">
@if (!hasAccount()) {
<ng-container [ngTemplateOutlet]="signIn" />
} @else {
<app-login (onGoBack)="hideLogin()" (onLogin)="onClose.emit()" />
}
</mat-dialog-content>
</div>

<ng-template #signIn>
<h4 class="register__title" mat-dialog-title>Sign In</h4>
<mat-dialog-content>
<form [formGroup]="registerForm" class="register__form">
<mat-form-field>
<mat-label>UserName</mat-label>
<input matInput formControlName="username" type="text" placeholder="Username" />
</mat-form-field>
<mat-form-field>
<mat-label>Name</mat-label>
<input matInput formControlName="name" type="text" placeholder="Name" />
</mat-form-field>
<mat-form-field>
<mat-label>Last Name</mat-label>
<input matInput formControlName="lastName" type="text" placeholder="Last Name" />
</mat-form-field>
<mat-form-field>
<mat-label>Password</mat-label>
<input matInput formControlName="password" type="password" placeholder="Password" />
</mat-form-field>
<button mat-flat-button (click)="register()" [disabled]="registerForm.invalid">
<form [formGroup]="registerForm" class="register__form">
<mat-form-field>
<mat-label>UserName</mat-label>
<input matInput formControlName="username" type="text" placeholder="Username" />
</mat-form-field>
<mat-form-field>
<mat-label>Name</mat-label>
<input matInput formControlName="name" type="text" placeholder="Name" />
</mat-form-field>
<mat-form-field>
<mat-label>Last Name</mat-label>
<input matInput formControlName="lastName" type="text" placeholder="Last Name" />
</mat-form-field>
<mat-form-field>
<mat-label>Password</mat-label>
<input matInput formControlName="password" type="password" placeholder="Password" />
</mat-form-field>

<div mat-dialog-actions style="display: flex; flex-direction: column; gap: 1rem">
<button mat-flat-button (click)="register()" [disabled]="registerForm.invalid || isLoading()">
Register
</button>
</form>
</mat-dialog-content>
</div>
<button mat-button (click)="showLogin()" [disabled]="isLoading()">
Have an account? Click here
</button>
</div>
</form>
</ng-template>
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { MAT_FORM_FIELD_CUSTOM } from '@/core/config';
import { TypedFormControls } from '@/core/interface/forms/forms.interface';
import { AuthService } from '@/services/auth';
import { ChangeDetectionStrategy, Component, inject, output } from '@angular/core';
import { NgTemplateOutlet } from '@angular/common';
import { ChangeDetectionStrategy, Component, inject, output, signal } from '@angular/core';
import {
FormBuilder,
FormGroup,
Expand All @@ -15,6 +16,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatSnackBar } from '@angular/material/snack-bar';
import { RegisterUser } from '@skyzerozx/shared-interfaces';
import { LoginComponent } from '../login/login.component';

@Component({
selector: 'app-register',
Expand All @@ -25,7 +27,9 @@ import { RegisterUser } from '@skyzerozx/shared-interfaces';
MatFormFieldModule,
MatButtonModule,
MatInputModule,
MatDialogModule
MatDialogModule,
NgTemplateOutlet,
LoginComponent
],
templateUrl: './register.component.html',
styleUrl: './register.component.scss',
Expand All @@ -36,8 +40,10 @@ export class RegisterComponent {
private readonly authService = inject(AuthService);
private readonly snackBar = inject(MatSnackBar);
private readonly formBuilder = inject(FormBuilder);
readonly isLoading = this.authService.isLoading;

onRegister = output<void>();
onClose = output<void>();
hasAccount = signal(false);

registerForm: FormGroup<TypedFormControls<RegisterUser>> = this.formBuilder.group({
username: ['', [Validators.required, Validators.minLength(5), Validators.maxLength(255)]],
Expand All @@ -46,11 +52,19 @@ export class RegisterComponent {
lastName: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(255)]]
});

hideLogin() {
this.hasAccount.set(false);
}

showLogin() {
this.hasAccount.set(true);
}

register() {
this.authService.register(this.registerForm.getRawValue()).subscribe({
next: () => {
this.snackBar.open('User Registration Success', 'OK');
this.onRegister.emit();
this.onClose.emit();
}
});
}
Expand Down
4 changes: 2 additions & 2 deletions apps/webauthn-angular/src/app/pages/home/home.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class HomeComponent implements OnInit {
switchMap(() => authenticators$),
shareReplay(1)
);
console.log('Authenticators');

return toSignal(refreshAuthenticators$, {
initialValue: []
});
Expand All @@ -56,6 +56,6 @@ export class HomeComponent implements OnInit {
disableClose: true
});

dialogRef.componentRef.instance.onRegister.subscribe(() => dialogRef.close());
dialogRef.componentRef.instance.onClose.subscribe(() => dialogRef.close());
}
}
22 changes: 19 additions & 3 deletions apps/webauthn-angular/src/app/services/auth/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { concatMap, tap } from 'rxjs';
import { concatMap, finalize, tap } from 'rxjs';
import { jwtDecode } from 'jwt-decode';
import { HttpClient } from '@angular/common/http';
import { inject, Injectable, signal } from '@angular/core';
Expand All @@ -19,19 +19,35 @@ import { STORAGE_KEYS } from '../../core/constants';
export class AuthService {
private readonly http = inject(HttpClient);
private readonly userStorage = signal<UserProfile>(this.decodeToken());
private readonly _isLoading = signal(false);

get isLoading() {
return this._isLoading.asReadonly();
}

register(registerUser: RegisterUser) {
this._isLoading.set(false);
return this.http
.post<ResponseFormat<UserProfile>>(`${environment.API_URL}/users/register`, registerUser)
.pipe(concatMap(() => this.login(registerUser.username, registerUser.password)));
.pipe(
tap(() => this._isLoading.set(true)),
concatMap(() => this.login(registerUser.username, registerUser.password)),
finalize(() => this._isLoading.set(false))
);
}

login(username: string, password: string) {
this._isLoading.set(false);

return this.http
.post<
ResponseFormat<UserAuthenticated>
>(`${environment.API_URL}/auth/login`, { username, password })
.pipe(tap(({ data }) => this.saveUserStorage(data)));
.pipe(
tap(() => this._isLoading.set(true)),
tap(({ data }) => this.saveUserStorage(data)),
finalize(() => this._isLoading.set(false))
);
}

saveUserStorage({ token, user }: UserAuthenticated) {
Expand Down
8 changes: 3 additions & 5 deletions apps/webauthn-angular/src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,16 @@ body {

.register-dialog-container {
width: 600px;
height: 600px;

@media (width <= 600px) {
width: 400px;
height: 550px;
}
}

.json-viewer-dialog-container {
width: 600px;
height: 560px;
width: 900px;

@media (width <= 600px) {
width: 400px;
height: 530px;
}
}

0 comments on commit 7a70335

Please sign in to comment.