Skip to content

Commit

Permalink
feat: create frontend auth.service
Browse files Browse the repository at this point in the history
  • Loading branch information
danieljancar committed Jul 26, 2024
1 parent 1d625a9 commit d64c34d
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 116 deletions.
1 change: 0 additions & 1 deletion apps/backend/src/app/auth/captcha.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export class CaptchaService {
): Promise<string> {
const image = new Jimp(200, 100, 'white')
await Jimp.loadFont(Jimp.FONT_SANS_32_BLACK).then(font => {
// @ts-expect-error - TS doesn't know about the print method
image.print(font, 50, 50, answer)
})

Expand Down
5 changes: 5 additions & 0 deletions apps/frontend/src/app/config/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as process from 'process'

const API_URL = process.env.API_URL || 'http://localhost:3000'

export { API_URL }
50 changes: 20 additions & 30 deletions apps/frontend/src/app/core/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,32 @@
import { Injectable } from '@angular/core'
import { HttpClient, HttpErrorResponse } from '@angular/common/http'
import { Observable, of, throwError } from 'rxjs'
import { catchError, map } from 'rxjs/operators'
import { Keypair } from '@stellar/stellar-sdk'
import { API_URL } from '../config/config'
import { HttpClient } from '@angular/common/http'

@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(private http: HttpClient) {}

generateKeyPair(): Keypair {
return Keypair.random()
}
private apiURL = API_URL

register(publicKey: string): Observable<any> {
return this.http
.post('/api/users/register', { publicKey })
.pipe(catchError(this.handleError))
}

login(publicKey: string): Observable<any> {
return this.http
.post('/api/auth/login', { publicKey }, { withCredentials: true })
.pipe(catchError(this.handleError))
}
constructor(private http: HttpClient) {}

isAuthenticated(): Observable<boolean> {
return this.http.get('/api/auth/check', { withCredentials: true }).pipe(
catchError(() => of({ authenticated: false })),
map((response: any) => response.authenticated),
)
getChallenge(publicKey: string) {
return this.http.get<any>(`${this.apiURL}/auth/challenge}`, {
params: { publicKey },
})
}

private handleError(error: HttpErrorResponse) {
console.error('An error occurred:', error.message)
return throwError(
() => new Error('Something went wrong. Please try again later.'),
)
authenticate(
transaction: string,
publicKey: string,
captchaId: string,
captchaAnswer: string,
) {
return this.http.post<any>(`${this.apiURL}/auth/authenticate`, {
transaction,
publicKey: publicKey,
captchaId,
captchaAnswer,
})
}
}
17 changes: 8 additions & 9 deletions apps/frontend/src/app/features/login/login.component.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<div class="flex min-h-full flex-col justify-center px-6 py-12 lg:px-8">
<div class="sm:mx-auto sm:w-full sm:max-w-sm">
<img
alt="VoteVault"
class="mx-auto h-10 w-auto"
height="100"
ngSrc="vv-logo-s.png"
alt="VoteVault"
width="100"
height="100"
/>
<h2
class="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900"
Expand All @@ -15,30 +15,29 @@
</div>

<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
<form class="space-y-6" (ngSubmit)="onSubmit()">
<form class="space-y-6">
<div>
<label
for="publicKey"
class="block text-sm font-medium leading-6 text-gray-900"
for="publicKey"
>Stellar Public Key</label
>
<div class="mt-2">
<input
class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
id="publicKey"
name="publicKey"
type="text"
[(ngModel)]="publicKey"
placeholder="Enter your Stellar public key"
class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
required
type="text"
/>
</div>
</div>

<div>
<button
type="submit"
class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
type="submit"
>
Log in
</button>
Expand All @@ -48,8 +47,8 @@
<p class="mt-10 text-center text-sm text-gray-500">
Don't have an account?
<a
href="/register"
class="font-semibold leading-6 text-indigo-600 hover:text-indigo-500"
href="/register"
>Register</a
>
</p>
Expand Down
21 changes: 2 additions & 19 deletions apps/frontend/src/app/features/login/login.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Component, inject } from '@angular/core'
import { Component } from '@angular/core'
import { CommonModule, NgOptimizedImage } from '@angular/common'
import { FormsModule } from '@angular/forms'
import { AuthService } from '../../core/auth.service'

@Component({
selector: 'app-login',
Expand All @@ -10,20 +9,4 @@ import { AuthService } from '../../core/auth.service'
templateUrl: './login.component.html',
styleUrls: ['./login.component.css'],
})
export class LoginComponent {
publicKey = ''
private authService = inject(AuthService)

onSubmit() {
if (this.publicKey) {
this.authService.login(this.publicKey).subscribe(
response => {
console.log('Login successful', response)
},
error => {
console.error('Login failed', error)
},
)
}
}
}
export class LoginComponent {}
20 changes: 9 additions & 11 deletions apps/frontend/src/app/features/register/register.component.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<div class="flex min-h-full flex-col justify-center px-6 py-12 lg:px-8">
<div class="sm:mx-auto sm:w-full sm:max-w-sm">
<img
alt="VoteVault"
class="mx-auto h-10 w-auto"
height="100"
ngSrc="vv-logo-s.png"
alt="VoteVault"
width="100"
height="100"
/>
<h2
class="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900"
Expand All @@ -15,38 +15,36 @@
</div>

<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
<form class="space-y-6" (ngSubmit)="onSubmit()">
<form class="space-y-6">
<div>
<button
type="button"
(click)="generateKeyPair()"
class="w-full rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
type="button"
>
Generate Key Pair
</button>
</div>
<div>
<label
for="publicKey"
class="block text-sm font-medium leading-6 text-gray-900"
for="publicKey"
>Stellar Public Key</label
>
<div class="mt-2">
<input
class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
id="publicKey"
name="publicKey"
type="text"
[value]="publicKey"
readonly
class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
type="text"
/>
</div>
</div>

<div>
<button
type="submit"
class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
type="submit"
>
Register
</button>
Expand All @@ -56,8 +54,8 @@
<p class="mt-10 text-center text-sm text-gray-500">
Already have an account?
<a
href="/login"
class="font-semibold leading-6 text-indigo-600 hover:text-indigo-500"
href="/login"
>Log in</a
>
</p>
Expand Down
43 changes: 25 additions & 18 deletions apps/frontend/src/app/features/register/register.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Component, inject } from '@angular/core'
import { Component } from '@angular/core'
import { CommonModule, NgOptimizedImage } from '@angular/common'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { AuthService } from '../../core/auth.service'
import { Keypair } from '@stellar/typescript-wallet-sdk'

@Component({
selector: 'app-register',
Expand All @@ -11,26 +12,32 @@ import { AuthService } from '../../core/auth.service'
styleUrls: ['./register.component.css'],
})
export class RegisterComponent {
publicKey = ''
privateKey = ''
private authService = inject(AuthService)
publicKey: string = ''
secretKey: string = ''
captchaImage: string = ''
captchaId: string = ''
transaction: string = ''
captchaAnswer: string = ''
authToken: string = ''

generateKeyPair() {
const keypair = this.authService.generateKeyPair()
constructor(private authService: AuthService) {}

generateKeypair() {
const keypair = Keypair.random()
this.publicKey = keypair.publicKey()
this.privateKey = keypair.secret()
this.secretKey = keypair.secret()
}

onSubmit() {
if (this.publicKey) {
this.authService.register(this.publicKey).subscribe(
response => {
console.log('Registration successful', response)
},
error => {
console.error('Registration failed', error)
},
)
}
generateChallenge() {
this.authService.getChallenge(this.publicKey).subscribe(
response => {
this.transaction = response.transaction
this.captchaId = response.captchaId
this.captchaImage = response.captchaImage
},
error => {
console.error('Error generating challenge:', error)
},
)
}
}
32 changes: 12 additions & 20 deletions apps/frontend/src/app/guards/auth.guard.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,20 @@
import { Injectable } from '@angular/core'
import { CanActivate, Router } from '@angular/router'
import { Observable, of } from 'rxjs'
import { catchError, map } from 'rxjs/operators'
import { AuthService } from '../core/auth.service'
import {
ActivatedRouteSnapshot,
CanActivate,
GuardResult,
MaybeAsync,
RouterStateSnapshot,
} from '@angular/router'

@Injectable({
providedIn: 'root',
})
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}

canActivate(): Observable<boolean> {
return this.authService.isAuthenticated().pipe(
map(authenticated => {
if (!authenticated) {
this.router.navigate(['/login'])
return false
}
return true
}),
catchError(() => {
this.router.navigate(['/login'])
return of(false)
}),
)
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
): MaybeAsync<GuardResult> {
return false
}
}
8 changes: 0 additions & 8 deletions apps/frontend/src/proxy.conf.json

This file was deleted.

0 comments on commit d64c34d

Please sign in to comment.