pw reset
This commit is contained in:
@@ -5,6 +5,7 @@ import { FormDataRequest } from 'nestjs-form-data';
|
|||||||
import { Client } from 'src/model/client.entity';
|
import { Client } from 'src/model/client.entity';
|
||||||
import { CustomLogger } from 'src/core/custom.logger';
|
import { CustomLogger } from 'src/core/custom.logger';
|
||||||
import { CreateUserDto } from 'src/model/dto/create-user.dto';
|
import { CreateUserDto } from 'src/model/dto/create-user.dto';
|
||||||
|
import { RequestResetPwDto, ResetPWDto } from 'src/model/dto';
|
||||||
|
|
||||||
@Controller('auth')
|
@Controller('auth')
|
||||||
export class AuthController {
|
export class AuthController {
|
||||||
@@ -20,6 +21,11 @@ export class AuthController {
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('reset')
|
||||||
|
async resetPw(@Body() b: RequestResetPwDto | ResetPWDto) {
|
||||||
|
return this.usersService.resetPw(b);
|
||||||
|
}
|
||||||
|
|
||||||
@Post('login')
|
@Post('login')
|
||||||
async login(
|
async login(
|
||||||
@Body('username') username: string,
|
@Body('username') username: string,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { SessionKey, SessionKeyRepository } from 'src/model/session-key.entity';
|
|||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
import { Role, RoleRepository } from 'src/model/role.entity';
|
import { Role, RoleRepository } from 'src/model/role.entity';
|
||||||
import { DatabaseModule } from 'src/core/database/database.module';
|
import { DatabaseModule } from 'src/core/database/database.module';
|
||||||
|
import { MailModule } from 'src/application/mail/mail.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
providers: [
|
providers: [
|
||||||
@@ -49,6 +50,7 @@ import { DatabaseModule } from 'src/core/database/database.module';
|
|||||||
]),
|
]),
|
||||||
LoggerModule,
|
LoggerModule,
|
||||||
DatabaseModule,
|
DatabaseModule,
|
||||||
|
MailModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AuthModule {}
|
export class AuthModule {}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
import { Client, ClientRepository } from 'src/model/client.entity';
|
import { Client, ClientRepository } from 'src/model/client.entity';
|
||||||
import { Log, LogRepository } from 'src/model/log.entity';
|
import { Log, LogRepository } from 'src/model/log.entity';
|
||||||
import { RedirectRepository, RedirectUri } from 'src/model/redirect-uri.entity';
|
import { RedirectRepository, RedirectUri } from 'src/model/redirect-uri.entity';
|
||||||
|
import { ResetPwCode, ResetPwCodeRepository } from 'src/model/reset-pw.entity';
|
||||||
import { SessionKey, SessionKeyRepository } from 'src/model/session-key.entity';
|
import { SessionKey, SessionKeyRepository } from 'src/model/session-key.entity';
|
||||||
import { User, UserRepository } from 'src/model/user.entity';
|
import { User, UserRepository } from 'src/model/user.entity';
|
||||||
|
|
||||||
@@ -17,6 +18,7 @@ const ENTITIES = [
|
|||||||
AuthorizationCode,
|
AuthorizationCode,
|
||||||
SessionKey,
|
SessionKey,
|
||||||
Log,
|
Log,
|
||||||
|
ResetPwCode,
|
||||||
];
|
];
|
||||||
const REPOSITORIES = [
|
const REPOSITORIES = [
|
||||||
UserRepository,
|
UserRepository,
|
||||||
@@ -25,6 +27,7 @@ const REPOSITORIES = [
|
|||||||
SessionKeyRepository,
|
SessionKeyRepository,
|
||||||
RedirectRepository,
|
RedirectRepository,
|
||||||
LogRepository,
|
LogRepository,
|
||||||
|
ResetPwCodeRepository,
|
||||||
];
|
];
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { LoggerModule } from '../logger.module';
|
|||||||
import { AuthGuard } from './guards/auth.guard';
|
import { AuthGuard } from './guards/auth.guard';
|
||||||
import { DatabaseModule } from '../database/database.module';
|
import { DatabaseModule } from '../database/database.module';
|
||||||
import { RolesGuard } from './guards/roles.guard';
|
import { RolesGuard } from './guards/roles.guard';
|
||||||
|
import { MailModule } from 'src/application/mail/mail.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -22,6 +23,7 @@ import { RolesGuard } from './guards/roles.guard';
|
|||||||
NestjsFormDataModule,
|
NestjsFormDataModule,
|
||||||
DatabaseModule,
|
DatabaseModule,
|
||||||
LoggerModule,
|
LoggerModule,
|
||||||
|
MailModule,
|
||||||
],
|
],
|
||||||
providers: [UsersService, ClientService, AuthGuard, RolesGuard],
|
providers: [UsersService, ClientService, AuthGuard, RolesGuard],
|
||||||
exports: [JwtModule, UsersService, AuthGuard, RolesGuard],
|
exports: [JwtModule, UsersService, AuthGuard, RolesGuard],
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
export * from './create-user.dto';
|
export * from './create-user.dto';
|
||||||
export * from './login-user.dto';
|
export * from './login-user.dto';
|
||||||
|
export * from './resetPw.dto';
|
||||||
|
export * from './request-reset-pw.dto';
|
||||||
|
|||||||
3
idp/src/model/dto/request-reset-pw.dto.ts
Normal file
3
idp/src/model/dto/request-reset-pw.dto.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export interface RequestResetPwDto {
|
||||||
|
username: string;
|
||||||
|
}
|
||||||
4
idp/src/model/dto/resetPw.dto.ts
Normal file
4
idp/src/model/dto/resetPw.dto.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export interface ResetPWDto {
|
||||||
|
password: string;
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
33
idp/src/model/reset-pw.entity.ts
Normal file
33
idp/src/model/reset-pw.entity.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import {
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
ManyToOne,
|
||||||
|
DataSource,
|
||||||
|
Repository,
|
||||||
|
CreateDateColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { User } from './user.entity';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class ResetPwCode {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => User, (user) => user.sessionKeys, { eager: true })
|
||||||
|
user: User;
|
||||||
|
|
||||||
|
@CreateDateColumn()
|
||||||
|
createdAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ResetPwCodeRepository extends Repository<ResetPwCode> {
|
||||||
|
constructor(dataSource: DataSource) {
|
||||||
|
super(ResetPwCode, dataSource.createEntityManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
findById(id: string): Promise<ResetPwCode> {
|
||||||
|
return this.findOneBy({ id });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HttpException, Injectable } from '@nestjs/common';
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
import { User, UserRepository } from 'src/model/user.entity';
|
import { User, UserRepository } from 'src/model/user.entity';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import * as bcrypt from 'bcrypt';
|
import * as bcrypt from 'bcrypt';
|
||||||
@@ -12,7 +12,9 @@ import { CustomLogger } from 'src/core/custom.logger';
|
|||||||
import { CreateUserDto } from 'src/model/dto/create-user.dto';
|
import { CreateUserDto } from 'src/model/dto/create-user.dto';
|
||||||
import { SessionKeyRepository } from 'src/model/session-key.entity';
|
import { SessionKeyRepository } from 'src/model/session-key.entity';
|
||||||
import { Client } from 'src/model/client.entity';
|
import { Client } from 'src/model/client.entity';
|
||||||
import { LoginUserDto } from 'src/model/dto';
|
import { LoginUserDto, RequestResetPwDto, ResetPWDto } from 'src/model/dto';
|
||||||
|
import { ResetPwCodeRepository } from 'src/model/reset-pw.entity';
|
||||||
|
import { MailService } from 'src/application/mail/mail.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UsersService {
|
export class UsersService {
|
||||||
@@ -23,6 +25,8 @@ export class UsersService {
|
|||||||
private tokenRepo: AuthorizationCodeRepository,
|
private tokenRepo: AuthorizationCodeRepository,
|
||||||
private sessionRepo: SessionKeyRepository,
|
private sessionRepo: SessionKeyRepository,
|
||||||
private logger: CustomLogger,
|
private logger: CustomLogger,
|
||||||
|
private resetPwRepo: ResetPwCodeRepository,
|
||||||
|
private mailService: MailService,
|
||||||
) {}
|
) {}
|
||||||
async createUser(userDto: CreateUserDto): Promise<User> {
|
async createUser(userDto: CreateUserDto): Promise<User> {
|
||||||
const hashedPassword = await bcrypt.hash(userDto.password, 10);
|
const hashedPassword = await bcrypt.hash(userDto.password, 10);
|
||||||
@@ -101,7 +105,7 @@ export class UsersService {
|
|||||||
throw new HttpException('Invalid client', 401);
|
throw new HttpException('Invalid client', 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await this.sessionRepo.findOneByOrFail({ id: sessionKey });
|
const session = await this.sessionRepo.findOneBy({ id: sessionKey });
|
||||||
if (!session) {
|
if (!session) {
|
||||||
throw new HttpException('Invalid session key', 401);
|
throw new HttpException('Invalid session key', 401);
|
||||||
}
|
}
|
||||||
@@ -265,4 +269,42 @@ export class UsersService {
|
|||||||
user.session_key = session.id;
|
user.session_key = session.id;
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async resetPw(dto: RequestResetPwDto | ResetPWDto) {
|
||||||
|
if (dto['username'] != null) {
|
||||||
|
// Send Mail
|
||||||
|
dto = dto as RequestResetPwDto;
|
||||||
|
const user = await this.userRepo.findOneBy({ username: dto.username });
|
||||||
|
|
||||||
|
const code = await this.resetPwRepo.save(
|
||||||
|
this.resetPwRepo.create({ user }),
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.mailService.sendResetMail({
|
||||||
|
code: code.id,
|
||||||
|
name: user.firstName,
|
||||||
|
to: user.username,
|
||||||
|
url: 'pw-reset',
|
||||||
|
});
|
||||||
|
return { success: true };
|
||||||
|
} else if (dto['password'] && dto['code']) {
|
||||||
|
// neues PW setzen
|
||||||
|
dto = dto as ResetPWDto;
|
||||||
|
|
||||||
|
const savedCode = await this.resetPwRepo.findOne({
|
||||||
|
where: { id: dto.code },
|
||||||
|
relations: ['user'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (savedCode && savedCode.user) {
|
||||||
|
const hashedPassword = await bcrypt.hash(dto.password, 10);
|
||||||
|
savedCode.user.password = hashedPassword;
|
||||||
|
await this.userRepo.save(savedCode.user);
|
||||||
|
await this.resetPwRepo.remove(savedCode);
|
||||||
|
await this.sessionRepo.delete({ user: { id: savedCode.user.id }});
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new HttpException('unprocessible entity', HttpStatus.UNAUTHORIZED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ import { LoginComponent } from './auth/login/login.component';
|
|||||||
import { RegisterComponent } from './auth/register/register.component';
|
import { RegisterComponent } from './auth/register/register.component';
|
||||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||||
import { SessionKeyGuard } from './core/guards/session-key.guard';
|
import { SessionKeyGuard } from './core/guards/session-key.guard';
|
||||||
|
import { ResetPwComponent } from './auth/reset-pw/reset-pw.component';
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{ path: 'login', component: LoginComponent, canActivate: [SessionKeyGuard] },
|
{ path: 'login', component: LoginComponent, canActivate: [SessionKeyGuard] },
|
||||||
{ path: 'register', component: RegisterComponent },
|
{ path: 'register', component: RegisterComponent },
|
||||||
|
{ path: 'pw-reset', component: ResetPwComponent },
|
||||||
{ path: 'dashboard', component: DashboardComponent, canActivate: [SessionKeyGuard] },
|
{ path: 'dashboard', component: DashboardComponent, canActivate: [SessionKeyGuard] },
|
||||||
{ path: '', component: LoginComponent, canActivate: [SessionKeyGuard] },
|
{ path: '', component: LoginComponent, canActivate: [SessionKeyGuard] },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -12,10 +12,13 @@
|
|||||||
<input type="password" formControlName="password" class="login__input" placeholder="Password">
|
<input type="password" formControlName="password" class="login__input" placeholder="Password">
|
||||||
</div>
|
</div>
|
||||||
<button class="button login__submit" (click)="login()" [disabled]="loginForm.invalid || isLoading">
|
<button class="button login__submit" (click)="login()" [disabled]="loginForm.invalid || isLoading">
|
||||||
<span class="button__text">Log In Now</span>
|
<span class="button__text">Einloggen</span>
|
||||||
<i class="button__icon fas fa-chevron-right"></i>
|
<i class="button__icon fas fa-chevron-right"></i>
|
||||||
</button>
|
</button>
|
||||||
<div class="login-register" (click)="toRegister()" >Register...</div>
|
<div class="flex-row">
|
||||||
|
<div class="login-register" style="color: black;" (click)="toResetPw()" >Pw vergessen...</div>
|
||||||
|
<div class="login-register" (click)="toRegister()" >Registrieren...</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="screen__background">
|
<div class="screen__background">
|
||||||
|
|||||||
@@ -106,4 +106,8 @@ export class LoginComponent {
|
|||||||
toRegister() {
|
toRegister() {
|
||||||
this.router.navigate(['/register'], { queryParams: this.route.snapshot.queryParams });
|
this.router.navigate(['/register'], { queryParams: this.route.snapshot.queryParams });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toResetPw() {
|
||||||
|
this.router.navigateByUrl('/pw-reset')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
51
idp_client/src/app/auth/reset-pw/reset-pw.component.html
Normal file
51
idp_client/src/app/auth/reset-pw/reset-pw.component.html
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<div class="container">
|
||||||
|
<div class="screen">
|
||||||
|
<div class="screen__content">
|
||||||
|
|
||||||
|
@if (resetCode == null) {
|
||||||
|
<form class="login" [formGroup]="resetPw" >
|
||||||
|
<div>
|
||||||
|
Gib deine Emailadresse ein um einen Link zum zurücksetzen des Passworts per Mail zu bekommen.
|
||||||
|
</div>
|
||||||
|
<div class="login__field">
|
||||||
|
<i class="login__icon fas fa-user user"></i>
|
||||||
|
<input formControlName="username" type="text" class="login__input" placeholder="User name / Email">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="button login__submit" (click)="resetPassword()" [disabled]="resetPw.invalid || isLoading">
|
||||||
|
<span class="button__text">Passwort zurücksetzen</span>
|
||||||
|
<i class="button__icon fas fa-chevron-right"></i>
|
||||||
|
</button>
|
||||||
|
<div class="login-register" (click)="toLogin()" >Login...</div>
|
||||||
|
</form>
|
||||||
|
} @else {
|
||||||
|
<form class="login" [formGroup]="setNewPwForm" >
|
||||||
|
<div>
|
||||||
|
Das neue Passwort muss mindestens 4 Zeichen lang sein.
|
||||||
|
</div>
|
||||||
|
<div class="login__field">
|
||||||
|
<i class="login__icon fas fa-user user"></i>
|
||||||
|
<input formControlName="password" type="password" class="login__input" placeholder="Passwort">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="login__field">
|
||||||
|
<i class="login__icon fas fa-user user"></i>
|
||||||
|
<input formControlName="repeatPassword" type="password" class="login__input" placeholder="Passwort wiederholen">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="button login__submit" (click)="setNewPassword()" [disabled]="setNewPwForm.invalid || isLoading">
|
||||||
|
<span class="button__text">Passwort zurücksetzen</span>
|
||||||
|
<i class="button__icon fas fa-chevron-right"></i>
|
||||||
|
</button>
|
||||||
|
<div class="login-register" (click)="toLogin()" >Login...</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="screen__background">
|
||||||
|
<span class="screen__background__shape screen__background__shape4"></span>
|
||||||
|
<span class="screen__background__shape screen__background__shape3"></span>
|
||||||
|
<span class="screen__background__shape screen__background__shape2"></span>
|
||||||
|
<span class="screen__background__shape screen__background__shape1"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
23
idp_client/src/app/auth/reset-pw/reset-pw.component.spec.ts
Normal file
23
idp_client/src/app/auth/reset-pw/reset-pw.component.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ResetPwComponent } from './reset-pw.component';
|
||||||
|
|
||||||
|
describe('ResetPwComponent', () => {
|
||||||
|
let component: ResetPwComponent;
|
||||||
|
let fixture: ComponentFixture<ResetPwComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ResetPwComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ResetPwComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
76
idp_client/src/app/auth/reset-pw/reset-pw.component.ts
Normal file
76
idp_client/src/app/auth/reset-pw/reset-pw.component.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Component, inject } from '@angular/core';
|
||||||
|
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { HotToastService } from '@ngxpert/hot-toast';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-reset-pw',
|
||||||
|
standalone: true,
|
||||||
|
imports: [FormsModule, ReactiveFormsModule],
|
||||||
|
templateUrl: './reset-pw.component.html',
|
||||||
|
styleUrl: '../auth.scss'
|
||||||
|
})
|
||||||
|
export class ResetPwComponent {
|
||||||
|
private router = inject(Router);
|
||||||
|
private http = inject(HttpClient);
|
||||||
|
private route = inject(ActivatedRoute);
|
||||||
|
private toast = inject(HotToastService);
|
||||||
|
|
||||||
|
isLoading = false;
|
||||||
|
|
||||||
|
resetCode;
|
||||||
|
|
||||||
|
resetPw = new FormGroup({
|
||||||
|
username: new FormControl('', [Validators.required, Validators.email])
|
||||||
|
})
|
||||||
|
|
||||||
|
setNewPwForm = new FormGroup({
|
||||||
|
password: new FormControl(null, [Validators.required, Validators.minLength(4), Validators.maxLength(100)]),
|
||||||
|
repeatPassword: new FormControl(null, [Validators.required, Validators.minLength(4), Validators.maxLength(100)]),
|
||||||
|
code: new FormControl(null)
|
||||||
|
})
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.resetCode = this.route.snapshot.queryParams["resetcode"];
|
||||||
|
this.setNewPwForm.patchValue({code: this.resetCode});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
resetPassword() {
|
||||||
|
this.http.post('api/auth/reset', this.resetPw.value).subscribe({
|
||||||
|
next: res => {
|
||||||
|
console.log(res);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setNewPassword() {
|
||||||
|
const val = this.setNewPwForm.value;
|
||||||
|
if (val.password != val.repeatPassword) {
|
||||||
|
this.toast.error('Die Passwörter stimmen nicht überein');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.http.post('api/auth/reset', this.setNewPwForm.value)
|
||||||
|
.pipe(
|
||||||
|
this.toast.observe({
|
||||||
|
loading: 'Setze neues Passwort',
|
||||||
|
success: 'Passwort gespeichert',
|
||||||
|
error: 'Passwort konnte nicht gespeichert werden!'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe({
|
||||||
|
next: res => {
|
||||||
|
console.log(res);
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
this.router.navigateByUrl('/login');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
toLogin() {
|
||||||
|
this.router.navigateByUrl("/login");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -57,6 +57,8 @@ export class SessionKeyGuard {
|
|||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
window.localStorage.removeItem("auth_session_key")
|
||||||
|
this.router.navigateByUrl('/login');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user