Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTTPDigestAuth raise TypeError when get_password() returns None. #5

Closed
kennytm opened this issue Mar 15, 2014 · 7 comments
Closed

HTTPDigestAuth raise TypeError when get_password() returns None. #5

kennytm opened this issue Mar 15, 2014 · 7 comments

Comments

@kennytm
Copy link

kennytm commented Mar 15, 2014

If we implement @auth.get_password as described in the doc:

@auth.get_password
def get_pw(username):
    if username in users:
        return users[username]
    return None

When the user logs in with an invalid user name, the script with raise an error:

  File ".../env/lib/python3.3/site-packages/flask_httpauth.py", line 52, in decorated
    if not self.authenticate(auth, password):
  File ".../env/lib/python3.3/site-packages/flask_httpauth.py", line 108, in authenticate
    a1 = auth.username + ":" + auth.realm + ":" + password
TypeError: Can't convert 'NoneType' object to str implicitly

(BTW the test suite does not catch this because it simply makes every invalid user's password being 'other'. This is totally wrong, and should return some non-string to indicate unconditional rejection.)

@miguelgrinberg
Copy link
Owner

Thanks. I have updated the docs to use users.get(username) which behaves correctly.

@kennytm
Copy link
Author

kennytm commented Mar 16, 2014

Hi.

I don't think 051195d is going to fix anything. We could test with this sample script:

from flask import Flask
from flask.ext.httpauth import HTTPDigestAuth

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret key here'
auth = HTTPDigestAuth()

users = {
    "john": "hello",
    "susan": "bye"
}

@auth.get_password
def get_pw(username):
    if username in users:
        return users.get(username)  # this is equivalent to users[username] in this context anyway.
    return None

@app.route('/')
@auth.login_required
def index():
    return "Hello, %s!" % auth.username()

if __name__ == '__main__':
    app.run()

If we login with a known user name and wrong password e.g. http://john:zzzz@127.0.0.1:5000/, we correctly get the HTTP 401 "unauthorized access" page. However, if we login with a wrong user name e.g. http://nobody:zzzz@127.0.0.1:5000/, we will get an HTTP 500 page instead, which is unexpected.

The real problem is that, in lines 49–52 of flask_httpauth.py,

            password = self.get_password_callback(auth.username)
            #if not password:
            #    return self.auth_error_callback()
            if not self.authenticate(auth, password):

when the user name is invalid, password will be None, and then in HTTPDigestAuth.authenticate() we have

        a1 = auth.username + ":" + auth.realm + ":" + password

where password is None, which will cause TypeError because we are trying to concatenate a string with None.

@miguelgrinberg
Copy link
Owner

Okay, I've finally understood the problem, and (of course) you were correct. I have fixed this and added a test for this specific condition.

Thanks!

alfg added a commit to alfg/murmur-rest that referenced this issue Apr 15, 2014
alfg added a commit to alfg/murmur-rest that referenced this issue Apr 15, 2014
@orencio
Copy link

orencio commented Jun 17, 2014

Hi, I'm probing the same example (https://github.com/miguelgrinberg/Flask-HTTPAuth#digest-authentication-example), but I always get an "Unathorized Access" when I make a request.

Example:

~$  curl -v --digest -u john:hello http://127.0.0.1:5000/
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 5000 (#0)
* Server auth using Digest with user 'john'
> GET / HTTP/1.1
> User-Agent: curl/7.37.0
> Host: 127.0.0.1:5000
> Accept: */*
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 401 UNAUTHORIZED
< Content-Type: text/html; charset=utf-8
< Content-Length: 19
< WWW-Authenticate: Digest realm="Authentication Required",nonce="bd371cbdb5f144d8dcfb9557bb1de941",opaque="fe97f3f602eabfad9fbb186ca5cabb7f"
< Set-Cookie: session=.eJwly00OQDAQBtC7zNpC6Y-6jMw304lVS8JK3B2xf-8iPo91qa1KoZmgY3ICRTDnvU4qhhxCApyW7B11v28b7-cXrORko8V-KAxjzfbSKQoHYSAZ3Q8Qyh8v.BoIgfQ.HJZG1TN4BE4J_G5-XUayEnzDKOQ; HttpOnly; Path=/
< Server: Werkzeug/0.9.6 Python/3.4.1
< Date: Tue, 17 Jun 2014 18:54:53 GMT
< 
* Closing connection 0
* Issue another request to this URL: 'http://127.0.0.1:5000/'
* Hostname was found in DNS cache
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 5000 (#1)
* Server auth using Digest with user 'john'
> GET / HTTP/1.0
> Authorization: Digest username="john", realm="Authentication Required", nonce="bd371cbdb5f144d8dcfb9557bb1de941", uri="/", response="f1e03c23d2bb20db978360565e2ca80f", opaque="fe97f3f602eabfad9fbb186ca5cabb7f"
> User-Agent: curl/7.37.0
> Host: 127.0.0.1:5000
> Accept: */*
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 401 UNAUTHORIZED
< Content-Type: text/html; charset=utf-8
< Content-Length: 19
* Authentication problem. Ignoring this.
< WWW-Authenticate: Digest realm="Authentication Required",nonce="e853310af9d60336b41337e274f3f945",opaque="dd4e81a2f44b91b0d875a86d8c50db7d"
< Set-Cookie: session=.eJwlyzEOgCAMAMC_dHYAWqD4GVMoxAk0kcn4d2Pc726Qee1bH71UWKGyR7RGWtJgEEMmixiri9SwJfKw_H4ccs4vqFJlK64R5WSzUY5eOCgXbzRHhecFl8EclA.BoIgfQ.6btCp7oBk4w28CqZ5BhWO2C45PI; HttpOnly; Path=/
< Server: Werkzeug/0.9.6 Python/3.4.1
< Date: Tue, 17 Jun 2014 18:54:53 GMT
< 
* Closing connection 1

What am I doing wrong?

@miguelgrinberg
Copy link
Owner

The digest module requires session storage. In the default configuration
this is a cookie that Flask sends back to the client. This cookie will
have to be sent back with the request. In a production setup it would
make a lot more sense to use server-side sessions, maybe something like
Flask-KVSession.

Miguel

----- Original Message -----
From: orencio notifications@github.com
To: miguelgrinberg/Flask-HTTPAuth Flask-HTTPAuth@noreply.github.com
Cc: Miguel Grinberg miguelgrinberg50@gmail.com
Date: Tue, 17 Jun 2014 11:59:42 -0700
Subject: Re: [Flask-HTTPAuth] HTTPDigestAuth raise TypeError when
get_password() returns None. (#5)

Hi, I'm probing the same example (Digest authentication example of
https://github.com/miguelgrinberg/Flask-HTTPAuth), but I always get an
"Unathorized Access" when I make a request.

Example:

~$  curl -v --digest -u john:hello http://127.0.0.1:5000/
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 5000 (#0)
* Server auth using Digest with user 'john'
> GET / HTTP/1.1
> User-Agent: curl/7.37.0
> Host: 127.0.0.1:5000
> Accept: */*
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 401 UNAUTHORIZED
< Content-Type: text/html; charset=utf-8
< Content-Length: 19
< WWW-Authenticate: Digest realm="Authentication 

Required",nonce="bd371cbdb5f144d8dcfb9557bb1de941",opaque="fe97f3f602eabfad9fbb186ca5cabb7f"

< Set-Cookie:

session=.eJwly00OQDAQBtC7zNpC6Y-6jMw304lVS8JK3B2xf-8iPo91qa1KoZmgY3ICRTDnvU4qhhxCApyW7B11v28b7-cXrORko8V-KAxjzfbSKQoHYSAZ3Q8Qyh8v.BoIgfQ.HJZG1TN4BE4J_G5-XUayEnzDKOQ;
HttpOnly; Path=/

< Server: Werkzeug/0.9.6 Python/3.4.1
< Date: Tue, 17 Jun 2014 18:54:53 GMT
<

  • Closing connection 0
  • Issue another request to this URL: 'http://127.0.0.1:5000/'
  • Hostname was found in DNS cache
  • Trying 127.0.0.1...
  • Connected to 127.0.0.1 (127.0.0.1) port 5000 (Support for hashed passwords? #1)
  • Server auth using Digest with user 'john'

    GET / HTTP/1.0
    Authorization: Digest username="john", realm="Authentication Required",
    nonce="bd371cbdb5f144d8dcfb9557bb1de941", uri="/",
    response="f1e03c23d2bb20db978360565e2ca80f",
    opaque="fe97f3f602eabfad9fbb186ca5cabb7f"
    User-Agent: curl/7.37.0
    Host: 127.0.0.1:5000
    Accept: /

  • HTTP 1.0, assume close after body
    < HTTP/1.0 401 UNAUTHORIZED
    < Content-Type: text/html; charset=utf-8
    < Content-Length: 19
  • Authentication problem. Ignoring this.
    < WWW-Authenticate: Digest realm="Authentication

Required",nonce="e853310af9d60336b41337e274f3f945",opaque="dd4e81a2f44b91b0d875a86d8c50db7d"

< Set-Cookie:

session=.eJwlyzEOgCAMAMC_dHYAWqD4GVMoxAk0kcn4d2Pc726Qee1bH71UWKGyR7RGWtJgEEMmixiri9SwJfKw_H4ccs4vqFJlK64R5WSzUY5eOCgXbzRHhecFl8EclA.BoIgfQ.6btCp7oBk4w28CqZ5BhWO2C45PI;
HttpOnly; Path=/

< Server: Werkzeug/0.9.6 Python/3.4.1
< Date: Tue, 17 Jun 2014 18:54:53 GMT
<

  • Closing connection 1
What am I doing wrong?

---
Reply to this email directly or view it on GitHub:

https://github.com/miguelgrinberg/Flask-HTTPAuth/issues/5#issuecomment-46350266

@sergiy-kozak
Copy link

Hi,
maybe my question is not directly to this issue, but anyway. You said, that since digest module requires session storage, it would make a lot more sense to use server-side sessions. I would like still to see how it (digest auth) can be done in case of the default configuration, where this is about working with cookie that Flask sends back to client. Do you have examples how to demonstrate digest http auth, using some client like curl?

@miguelgrinberg
Copy link
Owner

I don't have an example ready, but you can look at the unit test code for an example app. To use curl you do the same thing you do for basic auth, but add --digest to make it use the digest protocol.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants