1

I'm trying to do a straightforward authentication with some JQuery on the client side and a python with flask for the server. The idea is that I give the username and the password in the input field, I click the connect button, JQuery get the username and the password, parse them in json and send them with a post method to the server at localhost/auth, the server check if the username and the password exist in the database, if it doesn't he return a fail else success (like I said, straightforward). But when I do it I get the infamous error

Response to preflight request doesn't pass access control check: 
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'null' is therefore not allowed access. The response
had HTTP status code 404.

So the problem seems to come from the header Access-Control-Allow-Origin on the server side but the server doesn't seem to even get the request (in the flask console I don't get an "OPTION" method or anything) and more than that I did set the header. Here is my javascript + html:

<!DOCTYPE html>
  <html>
  <script src="jquery-3.1.1.min.js"></script>
  <head>
    <meta charset="utf-8">
    <title>DIMAC</title>
  </head>
  <body>
  <script>
  $(function() {
    $('#connect').on('click',function(){
      username = $('#user').val()
      password = $('#pass').val()
      if(username != "" && password != ""){
          $.ajax({
              url: 'http://127.0.0.1/auth',
              type: "POST",
              dataType:"application/json; charset=utf-8",
              headers: {'Access-Control-Allow-Origin':'*'},
              data: JSON.stringify({
                  username: username,
                  password: password
              }),
              xhrFields: {withCredentials: true},
              processData: false,
              crossDomain: true,
              success: function () {
                  alert("success");
              },
              error: function (xhr, ajaxOptions, thrownError) {
                  alert(ajaxOptions);
                  alert(thrownError);
              },
        });
      }
    })

      })
  </script>
      <h1 id="title">Test</h1>
  <div>
      <label> Username</label>
      <input id="user" type="text"/>
      <label> Password</label>
      <input id="pass"type="password"/>
  <button id="connect" name"connect"> Connect </button>
  </div>
  </body>

I put the header access control allow origin = * here too for the sake of it.

And for my python:

from flask import Flask, request, make_response
from flask_cors import CORS, cross_origin
import json
from source import database_manager as database
app = Flask(__name__)
cors = CORS(app, resources={r"/api/*": {"origins": "http://localhost/auth"}})
app.debug = True

@app.route('/auth', methods=['POST'])
def auth_client():
    data = request.get_json()
    # Init the database
    db = database.DatabaseManager("127.0.0.1", "5432", "postgres")
    print(data)
    # Get the Users with this name and this password (so one and one only if the credentials are right)
    user = db.select('SELECT * FROM Users WHERE usr_name = %(usr_name)s and     usr_password = %(usr_password)s;', {
        'usr_name': data["username"],
        'usr_password': data["password"]
    })
    db.close()
    # If the db didn't return one and only one user
    if len(user) != 1:
        resp = response("Invalid credentials", 401)
        # resp.headers['WWW-Authenticate'] = 'Basic realm="Credentials required"'
        # Debug
        print("Wrong")
        return resp

    else:
        resp = make_response(json.dumps("Success"))
        resp.status_code = 201
        resp.headers['Access-Control-Allow-Origin'] = 'http://127.0.0.1/auth'
        resp.headers['Access-Control-Allow-Methods'] = '*'
        resp.headers['Access-Control-Allow-Domain'] = '*'
        resp.headers['Access-Control-Allow-Credentials'] = True
        # Debug
        print("Right")
        return resp

The db thingy I use call the database with psycopg2, it's legit if you're asking. I also try CORS(app) But this doesn't work either.

Edit:

Following Maresh advice I now only talk about localhost in all of my code, I also give the port in my ajax part ( url is now http://localhost:5000/auth) and so I get a new error (so it appear that I'm going forward here):

Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested 
resource. Origin 'null' is therefore not allowed access.

And my server get an OPTION request now so it's almost working!

Raphael Todo
  • 188
  • 1
  • 1
  • 13

1 Answers1

2

The browser does a preflight HTTP OPTIONS call before making the post call so you also need to return the CORS policy there.

I tend to prefer leaving the CORS handling to the http server (nginx/apache), it gives more flexibility to the people deploying your app.

e.g: you don't need to change the code when changing domain.

Edit: In your case I see several mistake, first you define the origin as localhost:

cors = CORS(app, resources={r"/api/*": {"origins": "http://localhost/auth"}})

Then you use 127.0.0.1:

resp.headers['Access-Control-Allow-Origin'] = 'http://127.0.0.1/auth'

So which one do you want? localhost or 127.0.0.1? It's about domain here so it's not the same thing. For the rest of the answer I'll assume you want localhost

Both lines have the mistakes of adding the path /auth you only want the protocol://domain optionally adding the :portnumber if it's non-standard.

Additionally you don't need the CORS plugin if you add the header manually.

And finally the error that you see is a 404 so it means that your ajax call URL is wrong, or your server is not running on your localhost. Since it doesn't go through your code the cors headers are never set.

The response had HTTP status code 404.

So one dirty quickfix would be:

cors = CORS(app, resources={r"/api/*": {"origins": "http://localhost"}})
...
@app.route('/auth', methods=['POST'])
def auth_client():
        ...
        resp.headers['Access-Control-Allow-Origin'] = 'http://localhost'
        resp.headers['Access-Control-Allow-Methods'] = '*'
        resp.headers['Access-Control-Allow-Domain'] = '*'
        resp.headers['Access-Control-Allow-Credentials'] = True
        # Debug
        print("Right")
        return resp

@app.route('/auth', methods=['OPTIONS'])
    def preflight():
            resp = make_response("OK")
            resp.status_code = 201
            resp.headers['Access-Control-Allow-Origin'] = 'http://localhost'
            resp.headers['Access-Control-Allow-Methods'] = '*'
            resp.headers['Access-Control-Allow-Domain'] = '*'
            resp.headers['Access-Control-Allow-Credentials'] = True
            # Debug
            print("Right")
            return resp

And in the ajax call also use: localhost And serve your frontend code from localhost

Community
  • 1
  • 1
Maresh
  • 4,246
  • 18
  • 29