1

I am trying to login from my website developed in Angular to the back end flask API, The backend uses bcrypt for encoding and pymongo for interacting with a MongoDB. In postman, the login endpoint works fine but when an attempt to log in on the client side is made I receive a 401: UNAUTHORIZED ERROR. Can someone advise what the issue is/where I am going wrong?

The login function on the client side:

onLogin() {
  let postData = new FormData();
  postData.append('username', this.loginForm.get('username').value);
  postData.append('password', this.loginForm.get('password').value);

  this.http.post('http://localhost:5000/api/v1.0/login', postData)
  .subscribe((_response: any) => {

  console.log(this.loginForm.value);
  this.loginForm.reset();
})
}

The login endpoint in backend:

app = Flask(__name__)
CORS(app)

@app.route('/api/v1.0/login', methods=['POST'])
def login():

auth = request.authorization

if auth:
    user = users.find_one({'username': auth.username})
    if user is not None:
        if bcrypt.checkpw(bytes(auth.password, 'utf-8'),
                      user["password"]):  
            token = jwt.encode( \
                {'user' : auth.username,
                 'admin': user["admin"],
                'exp' : datetime.datetime.utcnow() + \
                datetime.timedelta(minutes=30)
             }, app.config['SECRET_KEY'])
        return make_response(jsonify( \
            {'token':token.decode('UTF-8')}), 200)
    else:
        return make_response(jsonify( \
            {'message':'Bad password'}), 401)
else:
    return make_response(jsonify( \
    {'message':'Bad username'}), 401)
Moonket
  • 11
  • 6
  • 2
    Are you sending correctly the username and password? Also, you get 401 with which message: Bad Username or Password? – Vítor França Dec 08 '21 at 14:17
  • What headers do you set in postman? Where do you set the username and password to be sent form the angluar app? – Tony Dec 08 '21 at 14:45

1 Answers1

2

In order to use request.authorization you need to send the credentials in the Authorization-header. Try this on the client:

onLogin() {
  const usn = this.loginForm.get('username').value;
  const pwd = this.loginForm.get('password').value;

  this.http.post('http://localhost:5000/api/v1.0/login', null, {
    headers: new HttpHeaders({
      Authorization: `Basic ${btoa(`${usn}:${pwd}`)}`
    })
  }).subscribe((_response: any) => {
    console.log(_response);
  });
}

As a sidenote; the credentials can be easily decoded by using atob(), so make sure you are using a secure connection (HTTPS) when not on localhost.

Lars Johan
  • 314
  • 2
  • 10
  • Thanks for the response, unfortunately I am now getting a CORS error: Access to XMLHttpRequest at 'http://localhost:5000/api/v1.0/login' from origin 'http://localhost:4200' has been blocked by CORS policy: Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response. – Moonket Dec 08 '21 at 14:42
  • Try setting `supports_credentials=True` in your CORS-config. Ref: https://flask-cors.readthedocs.io/en/latest/api.html, and allow the `localhost:4200` origin – Lars Johan Dec 08 '21 at 14:45
  • Still getting the same error. – Moonket Dec 08 '21 at 15:32
  • It should allow all headers by default. Are you specifying the Authorisation-header with a capital A? – Lars Johan Dec 08 '21 at 15:37
  • Also, looking at this answer: https://stackoverflow.com/a/38615675/9593504, you could try to pass in the `withCredentials: true` in your http config – Lars Johan Dec 08 '21 at 15:42
  • Thanks for your help, I imported cross-origin from cors and placed this wrapper: @cross_origin(origin='*',headers=['Content-Type','Authorization']) above the login endpoint and am now able to login successfully – Moonket Dec 08 '21 at 16:08