refactor: rewrite oauth login and 2fa for the new api

This commit is contained in:
Polianin Nikita 2024-12-28 08:35:48 +03:00
parent a7542eaf32
commit 1d691ccc09
3 changed files with 102 additions and 29 deletions

View File

@ -7,6 +7,7 @@ import {AvailableOAuthProvidersResponse} from "@api/v1/availableProvidersRespons
import {OAuthProvider} from "@model/oAuthProvider";
import {TwoFactorAuthentication} from "@model/twoFactorAuthentication";
import {TwoFactorAuthRequest} from "@api/v1/twoFactorAuthRequest";
import {OAuthAction} from "@model/oAuthAction";
export interface OAuthProviderData extends AvailableOAuthProvidersResponse {
icon: string;
@ -83,9 +84,10 @@ export default class AuthApiService extends ApiService {
}
}
public availableProviders(): Observable<OAuthProviderData[]> {
public availableProviders(callback: string): Observable<OAuthProviderData[]> {
let request = this.createRequestBuilder()
.setEndpoint('AvailableProviders')
.setQueryParams({callback: callback})
.setWithCredentials()
.build;
@ -97,4 +99,21 @@ export default class AuthApiService extends ApiService {
}) as OAuthProviderData);
}));
}
private handleTokenRequest(token: string, action: OAuthAction) {
return this.createRequestBuilder()
.setEndpoint('HandleToken')
.setQueryParams({token: token, action: action})
.setWithCredentials()
.build;
}
public loginOAuth(token: string) {
return this.get<TwoFactorAuthentication>(this.handleTokenRequest(token, OAuthAction.Login));
}
public linkAccount(token: string): Observable<null> {
const request = this.handleTokenRequest(token, OAuthAction.Bind);
return this.addAuth(request).get(request);
}
}

View File

@ -1,36 +1,53 @@
<mat-sidenav-container class="formLogin">
<mat-card>
<p class="mat-h3">
Вход в систему
</p>
<form [formGroup]="loginForm">
<mat-form-field color="accent">
<mat-label>Имя пользователя/email</mat-label>
<input matInput
formControlName="user"
matTooltip='Укажите имя пользователя используя латинские буквы и цифры без пробелов или email'
required
focusNext="passwordNextFocus">
@if (!requiresTwoFactorAuth) {
<mat-form-field color="accent">
<mat-label>Имя пользователя/email</mat-label>
<input matInput
formControlName="user"
matTooltip='Укажите имя пользователя используя латинские буквы и цифры без пробелов или email'
required
focusNext="passwordNextFocus">
@if (loginForm.get('user')?.hasError('required')) {
<mat-error>
Имя пользователя или email является <i>обязательным</i>
</mat-error>
}
@if (loginForm.get('user')?.hasError('required')) {
<mat-error>
Имя пользователя или email является <i>обязательным</i>
</mat-error>
}
@if (loginForm.get('user')?.hasError('minlength')) {
<mat-error>
Количество символов должно быть не менее 4
</mat-error>
}
</mat-form-field>
@if (loginForm.get('user')?.hasError('minlength')) {
<mat-error>
Количество символов должно быть не менее 4
</mat-error>
}
</mat-form-field>
<password-input [focusNext]="'loginNextFocus'" [formGroup]="loginForm"/>
<password-input [focusNext]="'loginNextFocus'" [formGroup]="loginForm"/>
} @else {
<mat-form-field color="accent">
<mat-label>Код 2FA</mat-label>
<input matInput
formControlName="twoFactorCode"
matTooltip="Введите код из приложения"
required
focusNext="loginNextFocus">
@if (loginForm.get('twoFactorCode')?.hasError('required')) {
<mat-error>
Код 2FA обязателен.
</mat-error>
}
</mat-form-field>
}
</form>
<OAuthProviders/>
@if (!requiresTwoFactorAuth) {
<OAuthProviders (oAuthLoginResult)="loginOAuth($event)"/>
}
<mat-error>
{{ errorText }}
@ -42,7 +59,7 @@
} @else {
<button mat-flat-button color="accent"
[disabled]="loginButtonIsDisable"
(click)="login()"
(click)="requiresTwoFactorAuth ? login2Fa() : login()"
id="loginNextFocus">
Войти
</button>

View File

@ -5,7 +5,7 @@ import {MatInput} from "@angular/material/input";
import {MatTooltip} from "@angular/material/tooltip";
import {MatButton} from "@angular/material/button";
import {MatCard} from "@angular/material/card";
import {FormBuilder, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms";
import {FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms";
import {FocusNextDirective} from "@/directives/focus-next.directive";
import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.component";
import AuthApiService from "@api/v1/authApiService";
@ -40,6 +40,7 @@ export class LoginComponent {
protected loaderActive: boolean = false;
protected loginButtonIsDisable: boolean = true;
protected errorText: string = '';
protected requiresTwoFactorAuth: boolean = false;
constructor(private formBuilder: FormBuilder, private auth: AuthApiService, private router: Router) {
this.auth.getRole()
@ -69,6 +70,22 @@ export class LoginComponent {
});
}
private updateTwoFactorValidation(data: TwoFactorAuthentication) {
if (data === TwoFactorAuthentication.None) {
this.router.navigate(['admin']).then();
return;
}
this.requiresTwoFactorAuth = true;
this.loginForm.addControl(
'twoFactorCode',
new FormControl('', Validators.required)
);
this.loginForm.removeControl('user');
this.loginForm.removeControl('password');
this.loginButtonIsDisable = !this.loginForm.valid;
}
protected login() {
this.loaderActive = true;
@ -85,11 +102,31 @@ export class LoginComponent {
.subscribe(x => {
this.loaderActive = false;
this.errorText = '';
if (x == TwoFactorAuthentication.None)
this.router.navigate(['admin']).then();
else
this.router.navigate(['two-factor']).then();
this.updateTwoFactorValidation(x);
});
}
protected login2Fa() {
this.loaderActive = true;
this.auth.twoFactorAuth({
code: this.loginForm.get('twoFactorCode')?.value,
method: TwoFactorAuthentication.TotpRequired
})
.pipe(catchError(error => {
this.loaderActive = false;
this.errorText = error.error.detail;
this.loginButtonIsDisable = true;
throw error;
}))
.subscribe(_ => {
this.loaderActive = false;
this.errorText = '';
this.router.navigate(['admin']).then();
});
}
protected loginOAuth(result: TwoFactorAuthentication) {
this.updateTwoFactorValidation(result);
}
}