← Назад к вопросам

Как передать токен авторизации в каждом запросе?

2.0 Middle🔥 191 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Передача токена авторизации в API запросах

Токен авторизации необходимо отправлять в заголовке Authorization каждого запроса. Есть несколько способов сделать это правильно.

1. Базовый способ - передача через headers

Просто добавь Authorization в каждый запрос:

const token = localStorage.getItem('authToken');

fetch('/api/users', {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${token}` // Bearer token
  }
})
  .then(response => response.json())
  .then(data => console.log(data));

2. Создай функцию-обёртку для fetch

Лучше всего вынести логику в отдельную функцию:

// lib/api.ts
const API_URL = process.env.REACT_APP_API_URL || 'https://api.example.com';

function getToken() {
  return localStorage.getItem('authToken');
}

export async function apiCall(
  endpoint: string,
  options: RequestInit = {}
) {
  const token = getToken();
  
  const headers = new Headers(options.headers || {});
  headers.set('Content-Type', 'application/json');
  
  if (token) {
    headers.set('Authorization', `Bearer ${token}`);
  }

  const response = await fetch(`${API_URL}${endpoint}`, {
    ...options,
    headers
  });

  // Обработай 401 (Unauthorized)
  if (response.status === 401) {
    // Токен истёк, перенаправь на логин
    localStorage.removeItem('authToken');
    window.location.href = '/login';
  }

  if (!response.ok) {
    throw new Error(`API Error: ${response.status}`);
  }

  return response.json();
}

// Использование:
export const api = {
  users: {
    getAll: () => apiCall('/api/users'),
    getById: (id: string) => apiCall(`/api/users/${id}`),
    create: (data: any) => apiCall('/api/users', {
      method: 'POST',
      body: JSON.stringify(data)
    })
  },
  posts: {
    getAll: () => apiCall('/api/posts'),
    getById: (id: string) => apiCall(`/api/posts/${id}`)
  }
};

3. Использование axios с перехватчиками

Если используешь axios, это проще всего:

// lib/api.ts
import axios from 'axios';

const apiClient = axios.create({
  baseURL: process.env.REACT_APP_API_URL || 'https://api.example.com'
});

// Request перехватчик - добавляет токен ко ВСЕМ запросам
apiClient.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('authToken');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

// Response перехватчик - обработка ошибок авторизации
apiClient.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      // Токен истёк
      localStorage.removeItem('authToken');
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

export default apiClient;

// Использование:
import apiClient from '@/lib/api';

// Токен добавляется автоматически!
apiClient.get('/api/users');
apiClient.post('/api/posts', { title: 'New Post' });

4. Context + useContext для хранения токена

Лучше всего хранить токен в контексте, а не в localStorage:

// contexts/AuthContext.tsx
import React, { createContext, useState, useCallback } from 'react';

interface AuthContextType {
  token: string | null;
  setToken: (token: string | null) => void;
  isAuthenticated: boolean;
}

export const AuthContext = createContext<AuthContextType | null>(null);

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [token, setTokenState] = useState<string | null>(
    localStorage.getItem('authToken')
  );

  const setToken = useCallback((newToken: string | null) => {
    if (newToken) {
      localStorage.setItem('authToken', newToken);
    } else {
      localStorage.removeItem('authToken');
    }
    setTokenState(newToken);
  }, []);

  return (
    <AuthContext.Provider value={{
      token,
      setToken,
      isAuthenticated: !!token
    }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = React.useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within AuthProvider');
  }
  return context;
}
// lib/api.ts
import { useAuth } from '@/contexts/AuthContext';

export function useApi() {
  const { token } = useAuth();

  return useCallback(async (
    endpoint: string,
    options: RequestInit = {}
  ) => {
    const headers = new Headers(options.headers || {});
    headers.set('Content-Type', 'application/json');
    
    if (token) {
      headers.set('Authorization', `Bearer ${token}`);
    }

    const response = await fetch(
      `${process.env.REACT_APP_API_URL}${endpoint}`,
      { ...options, headers }
    );

    if (response.status === 401) {
      // Токен истёк - выйти из системы
      throw new Error('Unauthorized');
    }

    return response.json();
  }, [token]);
}

5. Использование в компонентах

function UserList() {
  const { token } = useAuth();
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchUsers = async () => {
      try {
        const response = await fetch('/api/users', {
          headers: {
            'Authorization': `Bearer ${token}`,
            'Content-Type': 'application/json'
          }
        });
        const data = await response.json();
        setUsers(data);
      } catch (error) {
        console.error('Ошибка:', error);
      } finally {
        setLoading(false);
      }
    };

    if (token) {
      fetchUsers();
    }
  }, [token]); // Повторить запрос если изменился токен

  if (loading) return <div>Загрузка...</div>;
  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

6. Обновление токена при истечении (refresh token)

Если используешь refresh token:

const apiClient = axios.create({
  baseURL: process.env.REACT_APP_API_URL
});

apiClient.interceptors.request.use((config) => {
  const token = localStorage.getItem('authToken');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

apiClient.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config;

    // Если 401 и это не refresh запрос, попробуй обновить токен
    if (error.response?.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;

      try {
        const refreshToken = localStorage.getItem('refreshToken');
        const response = await axios.post('/api/auth/refresh', {
          refresh_token: refreshToken
        });

        const { access_token } = response.data;
        localStorage.setItem('authToken', access_token);

        // Повторить исходный запрос с новым токеном
        originalRequest.headers.Authorization = `Bearer ${access_token}`;
        return apiClient(originalRequest);
      } catch (err) {
        // Refresh не удался, перенаправь на логин
        localStorage.removeItem('authToken');
        localStorage.removeItem('refreshToken');
        window.location.href = '/login';
        return Promise.reject(err);
      }
    }

    return Promise.reject(error);
  }
);

7. Типизация с TypeScript

interface ApiRequestInit extends RequestInit {
  headers?: Record<string, string>;
}

class ApiClient {
  private baseURL: string;
  private token: string | null = null;

  constructor(baseURL: string) {
    this.baseURL = baseURL;
    this.token = localStorage.getItem('authToken');
  }

  setToken(token: string | null) {
    this.token = token;
    if (token) {
      localStorage.setItem('authToken', token);
    } else {
      localStorage.removeItem('authToken');
    }
  }

  private getHeaders(): Record<string, string> {
    const headers: Record<string, string> = {
      'Content-Type': 'application/json'
    };

    if (this.token) {
      headers['Authorization'] = `Bearer ${this.token}`;
    }

    return headers;
  }

  async request<T>(
    endpoint: string,
    options: ApiRequestInit = {}
  ): Promise<T> {
    const response = await fetch(`${this.baseURL}${endpoint}`, {
      ...options,
      headers: {
        ...this.getHeaders(),
        ...options.headers
      }
    });

    if (response.status === 401) {
      this.setToken(null);
      throw new Error('Unauthorized');
    }

    if (!response.ok) {
      throw new Error(`HTTP Error: ${response.status}`);
    }

    return response.json();
  }

  get<T>(endpoint: string) {
    return this.request<T>(endpoint, { method: 'GET' });
  }

  post<T>(endpoint: string, body: any) {
    return this.request<T>(endpoint, {
      method: 'POST',
      body: JSON.stringify(body)
    });
  }
}

export const api = new ApiClient(
  process.env.REACT_APP_API_URL || 'https://api.example.com'
);

Лучшие практики

  1. Никогда не храни чувствительные данные в localStorage - используй httpOnly cookies для более безопасного хранения
  2. Используй перехватчики (axios interceptors) для добавления токена ко всем запросам
  3. Обработай 401 ошибку - перенаправь пользователя на логин при истечении сессии
  4. Реализуй refresh token - чтобы пользователь не выходил из системы
  5. Передавай токен в заголовке Authorization - используй формат "Bearer {token}"
  6. Используй Context для хранения токена - вместо localStorage в каждом компоненте
  7. Типизируй API запросы - используй TypeScript для безопасности типов

Правильная организация авторизации - это критически важно для безопасности приложения.

Как передать токен авторизации в каждом запросе? | PrepBro