Skip to content

Аутентификация

Система аутентификации в Integ Admin основана на JWT токенах с поддержкой refresh tokens.

Обзор

Аутентификация включает в себя:

  • ✅ Вход по email и паролю
  • ✅ Регистрация новых пользователей
  • ✅ JWT access и refresh токены
  • ✅ Автоматическое добавление токена к запросам
  • ✅ Защита маршрутов через guards
  • ✅ Автоматический logout при 401

Архитектура

┌─────────────┐
│   User      │
└──────┬──────┘


┌─────────────────┐     ┌──────────────┐
│  Login Page     │────▶│ AuthService  │
└─────────────────┘     └──────┬───────┘


                        ┌─────────────┐
                        │  API        │
                        │  /auth/*    │
                        └──────┬──────┘


                        ┌─────────────┐
                        │ localStorage│
                        │ - access_token
                        │ - refresh_token
                        └─────────────┘

Функциональность

Вход в систему

Страница: /auth/login

  1. Пользователь вводит email и пароль
  2. Отправка запроса POST /auth/sign-in
  3. Получение access и refresh токенов
  4. Сохранение токенов в localStorage
  5. Редирект на /admin/dashboard

Пример кода:

typescript
@Component({
	selector: "app-login",
	standalone: true,
	imports: [ReactiveFormsModule, MatFormFieldModule, MatInputModule],
	templateUrl: "./login.component.html"
})
export class LoginComponent {
	private readonly authService = inject(AuthService);
	private readonly router = inject(Router);

	loginForm = new FormGroup({
		email: new FormControl("", [Validators.required, Validators.email]),
		password: new FormControl("", [Validators.required])
	});

	onSubmit() {
		if (this.loginForm.valid) {
			const { email, password } = this.loginForm.value;

			this.authService.login(email!, password!).subscribe({
				next: () => {
					this.router.navigate(["/admin/dashboard"]);
				},
				error: (error) => {
					console.error("Login failed:", error);
				}
			});
		}
	}
}

Регистрация

Страница: /auth/register

  1. Пользователь заполняет форму регистрации
  2. Отправка запроса POST /auth/sign-up
  3. Автоматический вход после успешной регистрации

Выход из системы

Выход происходит при:

  • Нажатии кнопки "Выход"
  • Получении 401 ошибки
  • Истечении токена
typescript
logout() {
  this.authService.logout();
  // Токены удаляются
  // Редирект на /auth/login
}

AuthService API

Методы

login(email: string, password: string): Observable<void>

Выполняет вход пользователя.

typescript
this.authService.login("user@example.com", "password123").subscribe(() => {
	console.log("Успешный вход");
});

logout(): void

Выполняет выход и редирект.

typescript
this.authService.logout();

me(): Observable<IUser>

Получает информацию о текущем пользователе.

typescript
this.authService.me().subscribe((user) => {
	console.log(user.email);
});

isAuthenticated: boolean

Проверка авторизации.

typescript
if (this.authService.isAuthenticated) {
	// Пользователь авторизован
}

isAuthenticated$: Observable<boolean>

Observable для реактивной проверки авторизации.

typescript
this.authService.isAuthenticated$.subscribe((isAuth) => {
	this.showAdminPanel = isAuth;
});

Guards

authGuard

Защищает маршруты, требующие авторизации.

Использование:

typescript
const routes: Routes = [
	{
		path: "admin",
		canActivate: [authGuard],
		loadComponent: () => import("./admin.component")
	}
];

Логика:

typescript
export const authGuard: CanActivateFn = () => {
	const authService = inject(AuthService);
	const router = inject(Router);

	if (!authService.isAuthenticated) {
		return router.createUrlTree(["/auth/login"]);
	}

	return true;
};

noAuthGuard

Редиректит авторизованных пользователей (для страниц login/register).

Использование:

typescript
const routes: Routes = [
	{
		path: "auth/login",
		canActivate: [noAuthGuard],
		loadComponent: () => import("./login.component")
	}
];

Interceptors

Auth Interceptor

Автоматически добавляет JWT токен ко всем запросам.

typescript
export const authInterceptor: HttpInterceptorFn = (req, next) => {
	const token = localStorage.getItem("access_token");

	if (token) {
		req = req.clone({
			setHeaders: {
				Authorization: `Bearer ${token}`
			}
		});
	}

	return next(req);
};

Error Interceptor

Обрабатывает 401 ошибки и выполняет logout.

typescript
export const errorInterceptor: HttpInterceptorFn = (req, next) => {
	const authService = inject(AuthService);

	return next(req).pipe(
		catchError((error: HttpErrorResponse) => {
			if (error.status === 401) {
				authService.logout();
			}
			return throwError(() => error);
		})
	);
};

Хранение токенов

Токены хранятся в localStorage:

typescript
// Ключи
ACCESS_TOKEN_KEY = "access_token";
REFRESH_TOKEN_KEY = "refresh_token";

// Сохранение
localStorage.setItem("access_token", token);

// Получение
const token = localStorage.getItem("access_token");

// Удаление
localStorage.removeItem("access_token");

SSR

Код использует isPlatformBrowser для проверки окружения перед работой с localStorage, что необходимо для корректной работы SSR.

JWT Claims

Токен можно декодировать для получения claims:

typescript
import { jwtDecode } from "jwt-decode";

const claims = this.authService.getTokenClaims();
// claims содержит: userId, email, exp, iat и т.д.

Безопасность

Best Practices

Что делается:

  • Токены в localStorage (не в cookies)
  • HTTPS обязателен для production
  • Токены автоматически прикрепляются к запросам
  • Автоматический logout при 401

Что НЕ делается:

  • XSS защита — ответственность разработчика
  • CSRF — не применимо при использовании JWT без cookies

Рекомендации

  1. Всегда используйте HTTPS в production
  2. Устанавливайте короткое время жизни access токена
  3. Используйте refresh токен для обновления
  4. Валидируйте токены на backend'е

API Endpoints

МетодEndpointОписание
POST/auth/sign-inВход в систему
POST/auth/sign-upРегистрация
GET/auth/meПолучить текущего пользователя
POST/auth/refreshОбновить токен (если реализовано)

Следующие шаги

SaaS Admin Documentation