diff --git a/kontor-angular/src/app/common/auth/auth-service.ts b/kontor-angular/src/app/common/auth/auth-service.ts index ce89fe5..712de07 100644 --- a/kontor-angular/src/app/common/auth/auth-service.ts +++ b/kontor-angular/src/app/common/auth/auth-service.ts @@ -1,5 +1,6 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; +import { catchError, throwError } from 'rxjs'; interface AuthResponseData { @@ -10,6 +11,11 @@ interface AuthResponseData { expiresIn: string; } +export interface TokenData { + access_token: string; + token_type: string; +} + @Injectable({ providedIn: 'root' }) @@ -19,11 +25,30 @@ export class AuthService { } signup(email: string, password: string) { - return this.http.post('http://localhost:8800/signup', + return this.http.post('http://localhost:8800/signup', { - email: email, - password: password, - returnToken: true + username: email, + password: password + } + ).pipe(catchError(errorRes => { + let errorMessage = 'An unknown error occurred!'; + const err = Error(errorMessage); + if (!errorRes.error) { + return throwError(() => err); + } + switch(errorRes.statusText) { + case 'ERROR': + errorMessage = 'Uups...'; + } + return throwError(() => Error(errorMessage)); + })); + } + + login(email: string, password: string) { + return this.http.post('http://127.0.0.1:8800/api/login/token', + { + username: email, + password: password } ); } diff --git a/kontor-angular/src/app/common/auth/auth.html b/kontor-angular/src/app/common/auth/auth.html index 6a6e91e..9511125 100644 --- a/kontor-angular/src/app/common/auth/auth.html +++ b/kontor-angular/src/app/common/auth/auth.html @@ -1,22 +1,34 @@
-
-
- - + @if(error) { +
+

{{ error }}

-
- - + } + @if(isLoading) { +
+
-
- - -
- + } + @else { +
+
+ + +
+
+ + +
+
+ + +
+
+ }
diff --git a/kontor-angular/src/app/common/auth/auth.ts b/kontor-angular/src/app/common/auth/auth.ts index af237a0..c6ad305 100644 --- a/kontor-angular/src/app/common/auth/auth.ts +++ b/kontor-angular/src/app/common/auth/auth.ts @@ -1,21 +1,54 @@ import { Component } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { NgForm } from '@angular/forms'; +import { AuthService, TokenData } from './auth-service'; +import { LoadingSpinner } from "../../shared/loading-spinner/loading-spinner"; +import { Observable } from 'rxjs'; +import { HttpErrorResponse } from '@angular/common/http'; @Component({ selector: 'app-auth', - imports: [FormsModule], + imports: [FormsModule, LoadingSpinner], templateUrl: './auth.html', styleUrl: './auth.css' }) export class Auth { isLoginMode = true; + isLoading = false; + error: string | null = null; + + constructor(private authService: AuthService) {} onSwitchMode() { this.isLoginMode =!this.isLoginMode; } + onSubmit(form: NgForm) { - console.log(form.value); + if (!form.valid) { + return; + } + const email = form.value.email; + const password = form.value.password; + + let authObservable: Observable; + + this.isLoading = true; + if (this.isLoginMode) { + authObservable = this.authService.login(email, password); + } else { + authObservable = this.authService.signup(email, password) + } + + authObservable.subscribe( + token => { + console.log(token); + this.isLoading = false; + }, + (err: HttpErrorResponse) => { + console.log(err); + this.isLoading = false; + } + ); form.reset(); } } diff --git a/kontor-angular/src/app/shared/loading-spinner/loading-spinner.ts b/kontor-angular/src/app/shared/loading-spinner/loading-spinner.ts new file mode 100644 index 0000000..d46bf34 --- /dev/null +++ b/kontor-angular/src/app/shared/loading-spinner/loading-spinner.ts @@ -0,0 +1,11 @@ +import { Component } from "@angular/core" + +@Component({ + selector: 'app-loading-spinner', + template: '
', + styleUrls: ['./loading.spinner.css'] +}) +export class LoadingSpinner {} + + + diff --git a/kontor-angular/src/app/shared/loading-spinner/loading.spinner.css b/kontor-angular/src/app/shared/loading-spinner/loading.spinner.css new file mode 100644 index 0000000..413e640 --- /dev/null +++ b/kontor-angular/src/app/shared/loading-spinner/loading.spinner.css @@ -0,0 +1,41 @@ + +.lds-ring, +.lds-ring div { + box-sizing: border-box; +} +.lds-ring { + display: inline-block; + position: relative; + width: 80px; + height: 80px; +} +.lds-ring div { + box-sizing: border-box; + display: block; + position: absolute; + width: 64px; + height: 64px; + margin: 8px; + border: 8px solid currentColor; + border-radius: 50%; + animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; + border-color: currentColor transparent transparent transparent; +} +.lds-ring div:nth-child(1) { + animation-delay: -0.45s; +} +.lds-ring div:nth-child(2) { + animation-delay: -0.3s; +} +.lds-ring div:nth-child(3) { + animation-delay: -0.15s; +} +@keyframes lds-ring { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + diff --git a/kontor-api/src/core/security.py b/kontor-api/src/core/security.py index 3a45720..5ed2b2d 100644 --- a/kontor-api/src/core/security.py +++ b/kontor-api/src/core/security.py @@ -45,9 +45,7 @@ class OAuth2PasswordBearerWithCookie(OAuth2): super().__init__(flows=flows, scheme_name=scheme_name, auto_error=auto_error) async def __call__(self, request: Request) -> Optional[str]: - authorization: str = request.cookies.get( - "access_token" - ) # changed to accept access token from httpOnly Cookie + authorization: str = request.cookies.get("access_token") # changed to accept access token from httpOnly Cookie scheme, param = get_authorization_scheme_param(authorization) if not authorization or scheme.lower() != "bearer":