Adding loging logout logic

This commit is contained in:
2023-03-07 14:28:21 +01:00
parent 374f90b3ec
commit b7880dc304
5 changed files with 218 additions and 2 deletions

View File

@@ -4,6 +4,8 @@
<div class="container-fluid">
<div class="row flex-nowrap">
<div class="col-auto col-md-3 col-xl-2 px-sm-2 px-0 bg-dark" style="max-width: 200px;">
<login></login>
<logout></logout>
<sidenav class="sticky-top"></sidenav>
</div>
<div class="col py-3">
@@ -13,3 +15,4 @@
</div>
</div>
</main>

View File

@@ -2,6 +2,8 @@ import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons';
import { ReactiveFormsModule } from "@angular/forms";
import { HttpClientModule, HTTP_INTERCEPTORS } from "@angular/common/http";
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@@ -9,20 +11,31 @@ import { AppComponent } from './app.component';
import { SidenavComponent } from "./layout/sidenav/sidenav.component";
import { FlashmessagesComponent } from "./layout/flashmessages/flashmessages.component";
import { FlashmessagesService } from "./layout/flashmessages/flashmessages.service";
import { LoginComponent, LogoutComponent } from "./layout/auth/auth.component";
import { AuthService } from "./layout/auth/auth.service";
import { AuthInterceptor } from "./layout/auth/auth.interceptor"
@NgModule({
declarations: [
AppComponent,
SidenavComponent,
FlashmessagesComponent
FlashmessagesComponent,
LoginComponent,
LogoutComponent
],
imports: [
BrowserModule,
AppRoutingModule,
NgbModule,
NgxBootstrapIconsModule.pick(allIcons),
ReactiveFormsModule,
HttpClientModule,
],
providers: [
FlashmessagesService,
AuthService,
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
],
providers: [FlashmessagesService],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@@ -0,0 +1,103 @@
import {Component, ElementRef, EventEmitter, Output, ViewChild} from "@angular/core";
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
import {Router} from "@angular/router";
import {AuthService} from "./auth.service";
import {NgbModal} from "@ng-bootstrap/ng-bootstrap";
@Component({
selector: 'login',
template: `
<button class="btn btn-lg btn-outline-primary" (click)="openLoginModal()">Login</button>
<ng-template #loginModal let-modal>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">Login</h4>
<button type="button" class="btn-close" aria-label="Close"
(click)="modal.dismiss('Cross click')"></button>
</div>
<div class="modal-body">
<form [formGroup]="form">
<fieldset>
<legend>Login</legend>
<div class="form-field">
<label>Username:</label>
<input name="username" formControlName="username">
</div>
<div class="form-field">
<label>Password:</label>
<input name="password" formControlName="password"
type="password">
</div>
</fieldset>
<div class="form-buttons">
<button class="button button-primary"
(click)="this.login()">Login</button>
</div>
</form>
</div>
</ng-template>
`
})
export class LoginComponent {
@Output() loginSuccess = new EventEmitter<string>()
@ViewChild('loginModal')
loginModal!: ElementRef;
form: FormGroup;
constructor(private fb:FormBuilder,
private authService: AuthService,
private modalService: NgbModal
) {
this.form = this.fb.group({
username: ['',Validators.required],
password: ['',Validators.required]
});
this.authService.onAuthenticationRequired$.subscribe((required: boolean) => {
if (required) {
this.openLoginModal()
}
})
}
openLoginModal() {
this.modalService.open(this.loginModal, { ariaLabelledBy: 'modal-basic-title' }).result.then(
);
}
login() {
const val = this.form.value;
if (val.username && val.password) {
this.authService.login(val.username, val.password)
.subscribe((answer: string) => {
this.modalService.dismissAll();
this.loginSuccess.emit(answer)
}
);
}
}
}
@Component({
selector: 'logout',
template: `
<button class="btn btn-lg btn-outline-primary" (click)="logout()">Logout</button>
`
})
export class LogoutComponent {
@Output() logoutSuccess = new EventEmitter()
constructor(private authService: AuthService) {
}
logout() {
this.authService.logout()
.subscribe(() => {
this.logoutSuccess.emit()
}
);
}
}

View File

@@ -0,0 +1,34 @@
import { Injectable } from "@angular/core";
import { HttpErrorResponse, HttpHandler, HttpInterceptor, HttpRequest } from "@angular/common/http";
import { Observable, throwError } from 'rxjs';
import { catchError } from "rxjs/operators";
import { AuthService } from './auth.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private auth: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
const authToken = this.auth.getAuthorizationToken();
let authReq: HttpRequest<any>;
if (authToken) {
authReq = req.clone({
headers: req.headers.set('Authorization', authToken)
});
} else {
authReq = req;
}
return next.handle(authReq).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status && error.status == 401) {
this.auth.requestAuth();
}
return throwError(() => error);
})
);
}
}

View File

@@ -0,0 +1,63 @@
import { Injectable } from "@angular/core";
import { HttpClient, HttpHeaders, HttpParams } from "@angular/common/http";
import { map } from "rxjs/operators";
import { Subject } from "rxjs";
import { FlashmessagesService } from "../flashmessages/flashmessages.service";
export interface token_answer {
access_token: string,
token_type: string
}
@Injectable()
export class AuthService {
private access_token: string | null;
private authenticationRequired = new Subject<boolean>();
onAuthenticationRequired$ = this.authenticationRequired.asObservable();
constructor(private http: HttpClient,
private flashMessage: FlashmessagesService) {
this.access_token = localStorage.getItem('authtoken')
}
login(username:string, password:string ) {
const body = new HttpParams()
.set('username', username)
.set('password', password);
return this.http.post<token_answer>('/api/v1/auth/login', body.toString(),
{
headers: new HttpHeaders()
.set('Content-Type', 'application/x-www-form-urlencoded')
}).pipe(map( v => {
localStorage.setItem('authtoken', v.access_token);
this.access_token = v.access_token
this.flashMessage.success('Login successful. Welcome ' + username);
return username
} ));
}
logout() {
return this.http.post<token_answer>('/api/v1/auth/logout', '',
{
headers: new HttpHeaders()
}).pipe(map( v => {
localStorage.removeItem('authtoken');
this.access_token = null;
this.flashMessage.success('Logout successful. Goodbye');
} ));
}
getAuthorizationToken() {
return `Bearer ${this.access_token}`;
}
requestAuth() {
localStorage.removeItem('authtoken');
this.access_token = null;
this.authenticationRequired.next(true);
}
}