vimalloc / flask-jwt-extended

An open source Flask extension that provides JWT support (with batteries included)!
http://flask-jwt-extended.readthedocs.io/en/stable/
MIT License
1.55k stars 239 forks source link

422 Error with @jwt_required() in Flask App Deployed on VPS with Nginx #563

Open mhammadzahi opened 5 days ago

mhammadzahi commented 5 days ago

Hi everyone, I'm facing an issue while deploying my Flask application on a VPS server with Nginx. The app works fine when tested locally and also when hosted on Heroku. However, when deployed on my VPS, any route protected by @jwt_required() throws a 422 error.

Server Setup: Ubuntu VPS Nginx as a reverse proxy Flask Development Server to run the Flask app (just for test) Flask-JWT-Extended for JWT authentication

What Works: All API routes without the @jwt_required() decorator work perfectly. The same application works as expected (even with @jwt_required()) when deployed on Heroku.

What Fails: Any route using the @jwt_required() decorator on the VPS setup returns a 422 error. Removing @jwt_required() allows the routes to work normally.

I suspect it might have something to do with Nginx configurations, but I can’t figure out what. Could it be related to how headers or JSON bodies are being handled by the server? Any suggestions or troubleshooting tips would be greatly appreciated! Thanks in advance!

vimalloc commented 4 days ago

Hrm... this sounds vaguely familiar, but it's been a very long time since I've done any nginx configuration, and I'm not seeing anything specific off hand. Some questions to help troubleshoot this further:

mhammadzahi commented 4 days ago

Hi Lily, I hope you're doing well, Thank you for your support!

the Errors says: 422 Unprocessable Entity,

I am using only Flask development server (jsut for test)

when I remove @jwt_required() decorator the request works fine and print the header normally! The same application works as expected (even with @jwt_required()) when deployed on Heroku.

@app.route('/upload', methods=['POST']) @jwt_required() def upload_file_view(): print(request.headers) ... ...

here is Nginx config:

server { listen 443 ssl; server_name name.com www.name.com;

ssl_certificate /etc/letsencrypt/live/name.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/name.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;

client_max_body_size 100M;  # Set maximum body size for POST requests
client_body_timeout 240s;
client_header_timeout 240s;
keepalive_timeout 240s;
send_timeout 240s;

location / {
    # CORS headers
    add_header 'Access-Control-Allow-Origin' '*' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
    add_header 'Access-Control-Allow-Credentials' 'true' always;

    # Handle preflight OPTIONS request
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        add_header 'Content-Length' 700;
        add_header 'Access-Control-Max-Age' 1728000;
        return 204;
    }

    # Forward Authorization header for JWT
    proxy_set_header Authorization $http_authorization;

    # Forward Content-Type header for JSON or other request types
    proxy_set_header Content-Type $http_content_type;

    # Proxy pass to Flask app
    proxy_pass http://127.0.0.1:5000;  # Flask app address
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

}

darknetehf commented 2 days ago

@mhammadzahi , you need to add proper logging in your application to troubleshoot issues, without it we can only guess. Maybe adding a temp route like this could give you a useful stack trace? (Assuming you have already defined a logger to output to console and/or file)

@app.errorhandler(422)
def handle_exception(ex):
    logger.exception(ex)

If you can pinpoint the exact line of code that triggers the error, that would be a start. I don't know if your app runs as a service, but maybe try to run your app manually over SSH and look at console messages.