Django jwt token error with react. «Данный токен не подходит ни для одного типа токенов, token_not_valid».

Я использую Django Rest Framework для аутентификации и Axios во фронтенде (reactjs).

Все работало нормально во время разработки, затем тестировалось на тестовом сервере в моей компании и jwt работал нормально, при развертывании на рабочем сервере я случайно получаю следующую ошибку :

{
  "detail": "Given token not valid for any token type",
  "code": "token_not_valid",
  "messages": [
    {
      "token_class": "AccessToken",
      "token_type": "access",
      "message": "Token is invalid or expired"
    }
  ]
}

Иногда я получаю эту ошибку при первом вызове API после входа, в другой раз я получаю ее после отправки нескольких запросов после входа, также я никогда не получаю ее через postman

Я перечислю мой nginx conf и мой react config

nginx:

upstream api {
    server ${DJANGO_API};
}

server {
   
  listen 80 default_server;
  server_name xx;

  server_tokens off;
  add_header X-Frame-Options "DENY";
  proxy_cookie_flags one samesite=strict;

  client_max_body_size 10M;

  location /api/ {
      proxy_pass http://api$request_uri;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $host;
      proxy_redirect off;
  }


  location /admin/ {
      proxy_pass http://api$request_uri;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $host;
      proxy_redirect off;
  }

  location /swagger/ {
      proxy_pass http://api$request_uri;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $host;
      proxy_redirect off;
  }
    location /redoc/ {
      proxy_pass http://api$request_uri;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $host;
      proxy_redirect off;
  }
  
  location /staticfiles/ {
      alias /home/app/web/staticfiles/;
      expires 1y;
      add_header 'Cache-Control' 'public, immutable';
  }

  # ignore cache frontend
  location ~* (service-worker\.js)$ {    
      add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';    
      expires off;    
      proxy_no_cache 1;
  }
  
  location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
    try_files $uri $uri/ /index.html;
    expires 1y;
    add_header 'Cache-Control' 'public, immutable';

  }

  error_page   500 502 503 504  /50x.html;

  location = /50x.html {
    root   /usr/share/nginx/html;
  }

}

Обратите внимание, что xx - это ip тестового сервера, а не prod-сервера, повлияет ли это на что-нибудь?

reacjs:

const prod = {
    REACT_APP_CURRENT_DOMAIN:"eKYC",
    REACT_APP_CURRENT_VERSION:"1.2",
    REACT_APP_CURRENT_HOST:`${window.location.origin}`,
};

const dev = {
    REACT_APP_CURRENT_DOMAIN:"eKYC",
    REACT_APP_CURRENT_VERSION:"1.0",
    REACT_APP_CURRENT_HOST:"http://localhost:8000",
};

export const config = process.env.NODE_ENV === "development" ? dev : prod;
const setAuthToken = (token, permissions) => {
  if (token) {
    localStorage.setItem("token", token);
    localStorage.setItem("permissions", JSON.stringify(permissions));
    axios.defaults.headers.common.Authorization = `Bearer ${token}`;
    axios.defaults.headers.common["Accept"] = `application/json; version=${config.REACT_APP_CURRENT_VERSION}`;
  } else {
    localStorage.removeItem("token");
    localStorage.removeItem("permissions");
    delete axios.defaults.headers.common.Authorization;
    delete axios.defaults.headers.common["Accept"];
  }
};


export const login = (userData, setReloadModal) => {
  return (dispatch, getState) => {
    dispatch({
      type: actionTypes.SET_CURRENT_USER_LOADING,
      payload: true,
    });
    axios
      .post(
        `${config.REACT_APP_CURRENT_HOST}/api/login/`,
        {
          ...userData,
          entity: getState().auth.bank,
          app_ver: `${config.REACT_APP_CURRENT_VERSION}`,
        },
        {
          timeout: 120000,
          headers: {
            Accept: `application/json; version=${config.REACT_APP_CURRENT_VERSION}`,
          },
        }
      )
      .then((res) => res.data)
      .then((user) => {
       
        setAuthToken(user.access, user.permissions);
        return dispatch({
          type: actionTypes.SET_CURRENT_USER,
          payload: {
            user: { username: user.username, name: user.name },
            bank: user.entity,
            permissions: user.permissions,
            loading: false,
            authenticated: true,
            unauthorized: "",
          },
        });
      })
      .catch((err) => {
        transformErrorResponse(err);
        if (err.response) {
          if (!isUndefined(err.response.data.non_field_errors)) {
            if (
              err.response.data.non_field_errors[0] ===
              "الرجاء اعادة تحميل الصفحة بالضغط على F5 + Ctrl"
            ) {
              setReloadModal(true);
              return dispatch({
                type: actionTypes.SET_CURRENT_USER_LOADING,
                payload: false,
              });
            }
          }
          return dispatch({
            type: actionTypes.SET_CURRENT_USER_FAIL,
            payload: {
              errors: {
                ...getState().auth.errors,
                ...err.response.data,
              },
              loading: false,
            },
          });
        } else {
          return dispatch({
            type: actionTypes.SET_CURRENT_USER_FAIL,
            payload: {
              errors: {
                ...getState().auth.errors,
                non_field_errors: [" غير متصل بالشبكة"],
              },
              loading: false,
            },
          });
        }
      });
  };
};

Настройки Django:


REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ),
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ],    
    'DEFAULT_THROTTLE_RATES': {
        'anon': '20/min',
        'user': '20/min'
    },
    'EXCEPTION_HANDLER': 'api.utils.custom_exception_handler',
    'DATETIME_FORMAT': "%m/%d/%Y %H:%M:%S",
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.AcceptHeaderVersioning',
    'DEFAULT_VERSION': APP_VER,
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 15
}

SIMPLE_JWT = {
    "ACCESS_TOKEN_LIFETIME": datetime.timedelta(minutes=60),
    "REFRESH_TOKEN_LIFETIME": datetime.timedelta(days=1),
    "ROTATE_REFRESH_TOKENS": False,
    "BLACKLIST_AFTER_ROTATION": False,
    "UPDATE_LAST_LOGIN": False,

    "ALGORITHM": "HS256",
    "VERIFYING_KEY": "",
    "AUDIENCE": None,
    "ISSUER": None,
    "JSON_ENCODER": None,
    "JWK_URL": None,
    "LEEWAY": 0,

    "AUTH_HEADER_TYPES": ("Bearer",),
    "AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",
    "USER_ID_FIELD": "id",
    "USER_ID_CLAIM": "user_id",
    "USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",

    "AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
    "TOKEN_TYPE_CLAIM": "token_type",
    "TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",

    "JTI_CLAIM": "jti",

    "SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",
    "SLIDING_TOKEN_LIFETIME": datetime.timedelta(minutes=5),
    "SLIDING_TOKEN_REFRESH_LIFETIME": datetime.timedelta(days=1),

    "TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer",
    "TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSerializer",
    "TOKEN_VERIFY_SERIALIZER": "rest_framework_simplejwt.serializers.TokenVerifySerializer",
    "TOKEN_BLACKLIST_SERIALIZER": "rest_framework_simplejwt.serializers.TokenBlacklistSerializer",
    "SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer",
    "SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer",
}

DEFENDER_LOGIN_FAILURE_LIMIT = 6
DEFENDER_REDIS_URL=  get_secret("DEFENDER_REDIS_URL")

DEFENDER_COOLOFF_TIME = 900
DEFENDER_DISABLE_USERNAME_LOCKOUT = False
DEFENDER_DISABLE_IP_LOCKOUT = True
DEFENDER_BEHIND_REVERSE_PROXY = True
DEFENDER_REVERSE_PROXY_HEADER = "HTTP_X_FORWARDED_FOR"

мой докер-файл prod :

version: '3.8'

services:
  web:
    image: xxx:5000/ekyc_web:${VER}
    restart: unless-stopped
    build: 
      context: ./django_backend
      dockerfile: Dockerfile.prod
    command: sh -c "python manage.py collectstatic --no-input --clear &&
                    python manage.py makemigrations &&
                    python manage.py migrate &&
                    gunicorn --worker-class gevent --worker-connections 100 django_backend.wsgi:application --bind 0.0.0.0:8005"
    volumes:
      - static_volume:/home/app/web/staticfiles
    ports:
      - 8005
    env_file:
      - ./django_backend/.env.prod
    depends_on: 
      - redis
    secrets:
      - eKYC_django_secrets
    deploy:
      replicas: 3
      placement:
            max_replicas_per_node: 3
      restart_policy:
        condition: on-failure

  app-prod:
    restart: unless-stopped
    image: xxx:5000/ekyc_app-prod:${VER}
    build: 
      context: ./react-frontend
      dockerfile: Dockerfile.prod
    command: >
            /bin/sh -c
            "envsubst '
            $${DJANGO_API}
            '< /etc/nginx/templates/default.conf.template
            > /etc/nginx/conf.d/default.conf
            && nginx -g 'daemon off;'"
    volumes:
      - static_volume:/home/app/web/staticfiles
    ports:
      - '86:80'
    environment:
      - NODE_ENV=production
      - DJANGO_API=web:8005
    depends_on: 
      - web

    deploy:
      replicas: 3
      placement:
            max_replicas_per_node: 3
      restart_policy:
        condition: on-failure


  redis:
    image: xxx:5000/ekyc_redis:${VER}
    build: 
      context: ./redis
      dockerfile: Dockerfile
    ports:
      - "6383:6383"
      - "4382:4382"
    command: redis-server --port 6383 --appendonly yes
    volumes:
      - data:/home/docker/data

    deploy:
      replicas: 3

      placement:
            max_replicas_per_node: 3
      restart_policy:
        condition: on-failure
      

  
volumes:
  static_volume:
  data:

secrets:
  eKYC_django_secrets:
    file: ${LOC}/eKYC_django_secrets.json

Вернуться на верх