assign trainers to user; add email to jwt token

This commit is contained in:
Roland Schneider
2021-10-04 18:13:32 +02:00
parent 439fb12b6e
commit 7a55ca9ff3
21 changed files with 671 additions and 350 deletions

View File

@@ -6823,6 +6823,11 @@
"set-immediate-shim": "~1.0.1"
}
},
"jwt-decode": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
"integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
},
"karma": {
"version": "6.3.4",
"resolved": "https://registry.npmjs.org/karma/-/karma-6.3.4.tgz",

View File

@@ -28,6 +28,7 @@
"@ngxs/logger-plugin": "^3.7.2",
"@ngxs/store": "^3.7.2",
"bootstrap": "^5.1.1",
"jwt-decode": "^3.1.2",
"moment": "^2.24.0",
"ngx-bootstrap": "^5.0.0",
"ngx-toastr": "^14.1.3",

View File

@@ -1,31 +1,37 @@
import { Injectable } from "@angular/core";
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from "@angular/common/http";
import { Observable } from "rxjs";
import {Injectable} from "@angular/core";
import {HttpInterceptor, HttpRequest, HttpHandler, HttpEvent} from "@angular/common/http";
import {Observable} from "rxjs";
import {Store} from "@ngxs/store";
import {AppState} from "../state/app.state";
/*
The JWT interceptor intercepts the incoming requests from the application/user and adds JWT token to the request's Authorization header, only if the user is logged in.
This JWT token in the request header is required to access the SECURE END API POINTS on the server
This JWT token in the request header is required to access the SECURE END API POINTS on the server
*/
@Injectable()
export class JwtInterceptor implements HttpInterceptor{
constructor(){}
export class JwtInterceptor implements HttpInterceptor {
constructor(private store: Store) {
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>{
// check if the current user is logged in
// if the user making the request is logged in, he will have JWT token in it's local storage, which is set by Authorization Service during login process
let currentUser = JSON.parse(localStorage.getItem('currentUser'));
if(currentUser && currentUser.token){
// clone the incoming request and add JWT token in the cloned request's Authorization Header
request = request.clone({
setHeaders: {
Authorization: `Bearer ${currentUser.token}`
}
});
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// check if the current user is logged in
// if the user making the request is logged in, he will have JWT token in it's local storage, which is set by Authorization Service during login process
// let currentUser = JSON.parse(localStorage.getItem('currentUser'));
const token = this.store.selectSnapshot<string>(AppState.getToken);
if (token) {
// clone the incoming request and add JWT token in the cloned request's Authorization Header
request = request.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
// handle any other requests which went unhandled
return next.handle(request);
});
}
}
// handle any other requests which went unhandled
return next.handle(request);
}
}

View File

@@ -67,11 +67,11 @@ export class FitNavigationComponent implements OnInit {
if ( item.roles && item.roles.length ){
let firstRole = item.roles[0];
if ( firstRole == '!'){
return !this.authenticationService.user.value;
return !this.authenticationService.isLoggedIn();
}else if ( firstRole == '*'){
return true;
}else if ( firstRole == '@'){
return this.authenticationService.user.value;
return this.authenticationService.isLoggedIn();
}
}

View File

@@ -1,4 +1,7 @@
<fit-navigation></fit-navigation>
<div class="container " >
<div class="row ">
<div class="col-12">Bejelentkezve: {{username | async}}</div>
</div>
<router-outlet></router-outlet>
</div>

View File

@@ -1,4 +1,7 @@
import { Component, OnInit } from '@angular/core';
import {Select} from "@ngxs/store";
import {AppState} from "../../state/app.state";
import {Observable} from "rxjs";
@Component({
selector: 'app-secured-layout',
@@ -6,6 +9,7 @@ import { Component, OnInit } from '@angular/core';
styleUrls: ['./secured-layout.component.scss']
})
export class SecuredLayoutComponent implements OnInit {
@Select(AppState.getUsername) public username: Observable<string>;
constructor() { }

View File

@@ -1,57 +1,71 @@
import { Injectable } from '@angular/core';
import { HttpClient } from "@angular/common/http";
import { map } from 'rxjs/operators';
import {Injectable} from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {catchError, map} from 'rxjs/operators';
import {Endpoints} from "./endpoints";
import {BehaviorSubject} from "rxjs";
import { throwError} from "rxjs";
import {PasswordChangeRequest} from "../app.types";
import jwtDecode, {JwtPayload} from "jwt-decode";
import {Store} from "@ngxs/store";
import {LoginAction} from "../state/app.actions";
import {AppState} from "../state/app.state";
@Injectable({
providedIn: 'root'
})
export class AuthenticationService {
private _user: BehaviorSubject<any> = new BehaviorSubject(null);
constructor(private http: HttpClient){
let user = localStorage.getItem('currentUser' );
if ( user ){
this.user.next( JSON.stringify(user));
}
constructor(private http: HttpClient, private store: Store) {
store.dispatch(new LoginAction(this.getToken()))
}
public getToken() {
let currentUser = JSON.parse(localStorage.getItem('currentUser'));
return currentUser?.token;
}
// login
login(username: string, password:string){
return this.http.post<any>(Endpoints.POST_USERS_AUTHENTICATE(), {username:username,password:password})
login(username: string, password: string) {
return this.http.post<any>(Endpoints.POST_USERS_AUTHENTICATE(), {username: username, password: password})
.pipe(
// the backend service sends an instance of the user
// user: any (because .post<any>)
map(user => {
// login successful if the response has jwt token
if(user && user.token){
if (user && user.token) {
// store user details and jwt token in the local storage to keep the user logged in between page refreshes
localStorage.setItem('currentUser', JSON.stringify(user));
this.user.next(user);
const decoded = jwtDecode<JwtPayload>(user.token);
console.info("decoded", decoded);
// this.user.next(user);
this.store.dispatch(new LoginAction(user.token));
}
return user;
})
}),
catchError(err => {
localStorage.removeItem('currentUser');
this.store.dispatch(new LoginAction(null));
return throwError(err);
}),
);
}
passwordChange(passwordChangeRequest: PasswordChangeRequest){
passwordChange(passwordChangeRequest: PasswordChangeRequest) {
return this.http.post<any>(Endpoints.POST_USER_PASSWORD_CHANGE(), passwordChangeRequest)
}
get user() {
return this._user;
}
public isLoggedIn(){
return this.user.value;
// get user() {
// return this._user;
// }
//
public isLoggedIn() {
return this.store.selectSnapshot(AppState.getToken);
}
// logout
logout(){
logout() {
// remove user from local storage
localStorage.removeItem('currentUser');
this.user.next(null);
this.store.dispatch(new LoginAction(null));
// this.user.next(null);
}
}

View File

@@ -6,3 +6,9 @@ export class FilterTimeTableAction {
constructor(public idTrainer: number, public idEventType: number) {}
}
export class LoginAction {
// For debug / console output upon dev environment
static readonly type = "[App] LoginAction";
constructor(public token: string) {}
}

View File

@@ -1,12 +1,15 @@
import {Action, Selector, State, StateContext, Store} from "@ngxs/store";
import {Action, Selector, State, StateContext} from "@ngxs/store";
import {Injectable} from "@angular/core";
import {
FilterTimeTableAction,
FilterTimeTableAction, LoginAction,
} from "./app.actions";
import {TimeTableFilter} from "../app.types";
import jwtDecode, {JwtPayload} from "jwt-decode";
export interface AppStateModel {
username: string;
token: string;
filterTimeTable: FilterTimeTableAction;
}
@@ -14,6 +17,8 @@ export interface AppStateModel {
@State<AppStateModel>({
name: "app",
defaults: {
username: null,
token: null,
filterTimeTable: {
idTrainer: -1,
idEventType: -1
@@ -23,8 +28,7 @@ export interface AppStateModel {
export class AppState {
constructor(
) {
constructor() {
}
@Selector()
@@ -32,6 +36,17 @@ export class AppState {
return state.filterTimeTable;
}
@Selector()
public static getUsername(state: AppStateModel): string {
return state.username;
}
@Selector()
public static getToken(state: AppStateModel): string {
return state.token;
}
@Action(FilterTimeTableAction)
dispatchFilterTimeTable(ctx: StateContext<AppStateModel>, {idEventType, idTrainer}: FilterTimeTableAction): void {
ctx.patchState({
@@ -42,4 +57,23 @@ export class AppState {
});
}
@Action(LoginAction)
dispatchLogin(ctx: StateContext<AppStateModel>, {token}: LoginAction): void {
let username = null;
try {
const decoded = jwtDecode<JwtPayload & { username: string }>(token);
username = decoded?.username;
} catch (e) {
// user not logged in
token = null;
}
ctx.patchState({
username: username,
token: token
});
}
}