Skip to content

Commit

Permalink
Merge pull request #3 from dkhrunov/development
Browse files Browse the repository at this point in the history
Refactoring and Fixes
  • Loading branch information
dkhrunov authored May 14, 2022
2 parents 1fc9794 + 6578f28 commit f015d5b
Show file tree
Hide file tree
Showing 17 changed files with 267 additions and 120 deletions.
116 changes: 80 additions & 36 deletions README.md

Large diffs are not rendered by default.

90 changes: 67 additions & 23 deletions doc/ru/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# Ngx JWT Auth
<a href="https://jwt.io/">
<img src="https://jwt.io/img/badge-compatible.svg">
</a>

Библиотека для Token-Based Authentication на основе Access и Refresh токенов.
Библиотека для Token-Based Authentication на основе Access и Refresh токенов для Angular приложений.

Эта библиотека настраивается для любых вариантов использования.

<a href="https://jwt.io/">
<img src="https://jwt.io/img/badge-compatible.svg">
</a>

## Содержание
- [Описание](#описание)
- [Настройка и применение](#настройка-и-применение)
Expand All @@ -21,9 +22,9 @@
Данная библиотека реализует управление аутентификацией на сайте.

Позволяет:
- выбирать где будут храниться токены, выбирая хранилище токенов (подробнее далее);
- изменять хранилища токенов прямо в рантайме (подробнее далее);
- создать свое кастомное хранилище токенов (подробнее далее);
- выбирать где будут храниться токены, выбирая хранилище токенов;
- изменять хранилища токенов прямо в рантайме;
- создать свое кастомное хранилище токенов;
- автоматически обновлять токен доступа (access token). Обновление происходит либо по истечению срока валидности токена доступа, либо указать коэффициент протухания токена `refreshThreshold` по достижению которого будет выполнено обновление токена, для этих целей используется interceptor [JwtAuthInterceptor](../../projects/ngx-jwt-auth/src/lib/interceptors/jwt-auth.interceptor.ts).
- ограничивать доступ на определенные роуты для не авторизованных пользователей, используя [AuthGuard](../../projects/ngx-jwt-auth/src/lib/guards/auth.guard.ts);
- ограничивать доступ на определенные роуты для авторизованных пользователей, используя [UnAuthGuard](../../projects/ngx-jwt-auth/src/lib/guards/un-auth.guard.ts);
Expand All @@ -36,11 +37,12 @@
1. Импортировать `JwtAuthModule` в root/core модуль вашего приложения с вызовом метода `forRoot`, и в данный метод передать параметры:

```typescript
import { NgModule } from '@angular/core';
import { JwtAuthModule } from '@dekh/ngx-jwt-auth';

@NgModule({
imports: [
JwtAuthModule.forRoot(options),
JwtAuthModule.forRoot({ ... }),
],
})
export class AppModule {}
Expand All @@ -49,6 +51,15 @@ export class AppModule {}
2. Необходимо создать Api-сервис, реализуя базовый класс [BaseAuthApiService](../../projects/ngx-jwt-auth/src/lib/services/base-auth-api-service.ts). Данный класс обязует реализовать 3 метода `login`, `logout` и `refresh`. Методы `login` и `refresh` должны возвращать Observable cо значение `{ accessToken: string; refreshToken?: string; }`, если ваш сервер в методе авторизации `login` и\или в методе обновления токена доступа `refresh` возвращает другой формат, то достаточно просто можно смаппить значение оператором `map` из rxjs в нужный формат. Пример такого сервиса:

```typescript
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BaseAuthApiService, AuthResponseTokens } from '@dekh/ngx-jwt-auth';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { environments } from 'environment/environments';

import { Login, Registration } from '../../models';

@Injectable({
providedIn: 'root',
})
Expand Down Expand Up @@ -93,15 +104,20 @@ export class AuthApiService extends BaseAuthApiService {
}
```

3. Далее нужно передать в параметры `JwtAuthModule.forRoot(options)` обязательные параметры:
3. Далее нужно передать в параметры `JwtAuthModule.forRoot(options)` обязательные параметры: `authApiService`, `tokenStorage` и `authTokenStorage`.
- `authApiService: Type<BaseAuthApiService>` - Класс реализующий BaseAuthApiService и выполняющий запросы к серверу.
- `tokenStorage: Type<BaseTokenStorage>` - Хранилище обычных jwt токенов (не авторизационных).
- `authTokenStorage: Type<BaseTokenStorage>` - Хранилище авторизационных токенов.

```typescript
import { NgModule } from '@angular/core';
import {
JwtAuthModule,
InMemoryTokenStorage,
LocalStorageTokenStorage
} from '@dekh/ngx-jwt-auth';
import { AuthApiService } from '../services';

import { AuthApiService } from './auth/services/auth-api.service';

@NgModule({
imports: [
Expand All @@ -125,13 +141,16 @@ export class AppModule {}
Пример:

```typescript
import { NgModule } from '@angular/core';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import {
JwtAuthModule,
InMemoryTokenStorage,
LocalStorageTokenStorage,
JwtAuthInterceptor
} from '@dekh/ngx-jwt-auth';
import { AuthApiService } from '../services';

import { AuthApiService } from './auth/services/auth-api.service';

@NgModule({
imports: [
Expand All @@ -156,17 +175,25 @@ export class AppModule {}

Например:

На форме авторизации при ее отправки нужно использовать `JwtAuthService` и вызывать метод `login(...args[]: any)` все перданные аргументы в данный метод будут прокинуты в метод `login(...args[]: any)` нашего ранее созданного Api-сервиса для авторизации `AuthApiService` (все параметры прокидываются для каждого метода определенного в `BaseAuthApiService`):
> На форме авторизации при ее отправки нужно использовать `JwtAuthService` и вызывать метод `login(...args[]: any)` все перданные аргументы в данный метод будут прокинуты в метод `login(...args[]: any)` нашего ранее созданного Api-сервиса для авторизации `AuthApiService` (все параметры прокидываются для каждого метода определенного в `BaseAuthApiService`):
```typescript
import { Component, ChangeDetectionStrategy, OnDestroy } from "@angular/core";
import { FormGroup, FormBuilder, Validators } from "@angular/forms";
import { BehaviorSubject, Subject, tap, catchError, EMPTY, finalize } from "rxjs";
import { JwtAuthService } from "./jwt-auth.service";

import { Login, ServerErrorDto } from '../../models';
import { HttpError } from '../../exceptions';

@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LoginComponent implements OnDestroy {
public form: FormGroup;
public form!: FormGroup;

private readonly _isLoading$ = new BehaviorSubject<boolean>(false);
public readonly isLoading$ = this._isLoading$.asObservable();
Expand Down Expand Up @@ -194,7 +221,7 @@ export class LoginComponent implements OnDestroy {
// Login class it`s Domain model
const credentials = new Login(this.form.value);

this._authService
this._jwtAuthService
.login(credentials)
.pipe(
tap(() => this._loginError$.next(null)),
Expand Down Expand Up @@ -223,6 +250,7 @@ export class LoginComponent implements OnDestroy {
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard, UnAuthGuard } from '@dekh/ngx-jwt-auth';

import { LoginComponent, RegistrationComponent } from '../auth';
import { DashboardComponent } from '../dashboard';

Expand Down Expand Up @@ -335,12 +363,23 @@ export class MyCustomTokenStorage extends BaseTokenStorage {

```typescript
// app.module.ts
import { JwtAuthModule } from '@dekh/ngx-jwt-auth';
import { MyCustomTokenStorage } from '../auth';
import { NgModule } from '@angular/core';
import {
JwtAuthModule,
LocalStorageTokenStorage,
InMemoryTokenStorage
} from '@dekh/ngx-jwt-auth';

import { AuthApiService } from './auth/services/auth-api.service';
import { MyCustomTokenStorage } from './auth/token-storage/my-custom-token-storage';

@NgModule({
imports: [
AppRoutingModule,
JwtAuthModule.forRoot({
authApiService: AuthApiService,
tokenStorage: LocalStorageTokenStorage,
authTokenStorage: MyCustomTokenStorage,
customTokenStorages: [new MyCustomTokenStorage()],
}),
],
Expand All @@ -352,20 +391,23 @@ export class AppModule {}

```typescript
// app.service.ts
import { NgModule } from '@angular/core';
import {
JwtAuthModule,
LocalStorageTokenStorage,
InMemoryTokenStorage,
TokenStorageRegistry
} from '@dekh/ngx-jwt-auth';
import { AuthApiService } from '../services';
import { MyCustomTokenStorage } from '../auth';

import { AuthApiService } from './auth/services/auth-api.service';
import { MyCustomTokenStorage } from './auth/token-storage/my-custom-token-storage';

@NgModule({
imports: [
JwtAuthModule.forRoot({
authApiService: AuthApiService,
tokenStorage: LocalStorageTokenStorage,
authTokenStorage: InMemoryTokenStorage,
authTokenStorage: MyCustomTokenStorage,
}),
],
})
Expand All @@ -384,21 +426,23 @@ export class AppModule {

```typescript
// token-storage-changer.service.ts
import { Injectable } from '@angular/core';
import {
AuthTokenStorageManager,
TokenStorageRegistry,
CookiesTokenStorage,
BaseTokenStorage,
} from '@dekh/ngx-jwt-auth';
import { MyCustomTokenStorage } from '../auth';

import { MyCustomTokenStorage } from './auth/token-storage/my-custom-token-storage';

@Injectable({
provideIn: 'root'
})
export class TokenStorageChangerService {
constructor(
private readonly _authTokenStorageManager: AuthTokenStorageManager,
private readonly _tokenStorageRegistry: TokenStrageRegistry,
private readonly _tokenStorageRegistry: TokenStorageRegistry,
) {
this._tokenStorageRegistry.register(new MyCustomTokenStorage());
}
Expand All @@ -422,7 +466,7 @@ export class TokenStorageChangerService {
// const cookiesStorage = this._tokenStorageRegistry.get(new CookiesTokenStorage());
// or
// const cookiesStorage = this._tokenStorageRegistry.get('CookiesTokenStorage');
this.changeAuthStorage(storage);
this.changeAuthStorage(cookiesStorage);
}

public changeAuthStorage(storage: BaseTokenStorage): void {
Expand All @@ -437,4 +481,4 @@ export class TokenStorageChangerService {

Причинной данной обишбки - цикличный вызов `JwtAuthInterceptor`. Так как interceptor обработавыает каждый запрос, за исключением тех зопросов url которые указаны в параметре конфига `unsecuredUrls`, запрос на обновление токена создает цикличную зависимость.

Решением данной проблемы является указать в массиве `unsecuredUrls` URL или path запроса на обновление accessToken'а, либо указать корневой path для всех запросов связанных с авторизацией/регистрацией пользователя, например: `"/auth/"`, тогда все запросы c path auth будут исключены из проверки interceptor'a - `server.api/auth/login`, `server.api/auth/register`, `server.api/auth/refresh` и подобные.
Решением данной проблемы является указать в массиве `unsecuredUrls` URL или path запроса на обновление accessToken'а, либо указать корневой path для всех запросов связанных с авторизацией/регистрацией пользователя, например: `"/auth/"`, тогда все запросы с path `auth` будут исключены из проверки interceptor'a - `server.api/auth/login`, `server.api/auth/register`, `server.api/auth/refresh` и подобные.
Loading

0 comments on commit f015d5b

Please sign in to comment.