So, the short answer is: you can do this, but you need to write your own CherryPy tool (a before_handler ), and you should not include basic authentication in the CherryPy configuration (that is, you should not do anything like tools.auth.on or tools.auth.basic... etc.) - you need to handle HTTP authentication yourself. The reason for this is that the built-in Basic Authentication stuff seems to be pretty primitive. If you protect something by enabling Basic Authentication, as I did above, it will verify the authentication before verifying the session and your cookies will do nothing.
My decision, in prose
Fortunately, even though CherryPy doesn't have the ability to do both inline and you can use your inline session code. You still have to write your own code to handle the Basic Authentication element, but overall it's not so bad, and using session code is a big win, because creating a custom session manager is a good way to introduce security errors into your webapp.
In the end, I was able to take a lot of things from a page on the CherryPy wiki called Simple Authentication and Access Restrictions . This code uses CP sessions, but instead of Basic Auth, it uses a special page with a login form that sends ?username=USERNAME&password=PASSWORD . What I did is basically nothing more than changing the provided check_auth function to use a special login page using HTTP headers.
In general, you need a function that you can add as a CherryPy tool, namely before_handler . (In the source code, this function was called check_auth() , but I renamed it to protect() .) This function first tries to see if the cookies contain a (valid) session ID, and if that fails, it tries to see if there are any in the headers HTTP authentication.
Then you need an authentication method for this page; I do this with require() plus some conditions that are only callers that return True . In my case, these conditions are zkn_admin() and user_is() functions; if you have more complex needs, you can also look at member_of() , any_of() and all_of() for the source code.
If you do this, you already have a way to log in - you just send valid session cookies or HTTPBA credentials to any URL that you protect with the @require() decorator. Now you need to log out.
(The source code instead has an AuthController class that contains login() and logout() , and you can use the entire AuthController object in the HTTP document tree by simply placing auth = AuthController() inside your CherryPy root class and navigate to it with the URL an address like http://example.com/auth/login and http://example.com/auth/logout . My code doesn't use an authcontroller object, just a few functions.)
Some notes about my code
- Warning. Since I wrote my own parser for the headers of the HTTP headers, it only analyzes what I talked about, which means just HTTP Basic Auth - not, for example, Digest Auth or something else. For my application, this is fine; for yours, this may not be.
- It accepts several functions defined elsewhere in my code:
user_verify() and user_is_admin() - I also use the
debugprint() function, which only outputs the output when the DEBUG variable is set, and I left these calls for clarity. - You can call it
cherrypy.tools.WHATEVER (see last line); I named it zkauth based on the name of my application. Be careful not to call it auth or the name of any other built-in tool. - Then you need to enable
cherrypy.tools.WHATEVER in your CherryPy setup. - As you can see from all TODO messages: this code is still in a stream state, and not 100% checked for cases of edges - sorry for that! Hope this still gives you enough ideas.
My solution is in code
import base64 import re import cherrypy SESSION_KEY = '_zkn_username' def protect(*args, **kwargs): debugprint("Inside protect()...") authenticated = False conditions = cherrypy.request.config.get('auth.require', None) debugprint("conditions: {}".format(conditions)) if conditions is not None:
Now all you have to do is enable embedded sessions and your own cherrypy.tools.WHATEVER in your CherryPy setup. Again, do not enable cherrypy.tools.auth . My configuration looked like this:
config_root = { '/' : { 'tools.zkauth.on': True, 'tools.sessions.on': True, 'tools.sessions.name': 'zknsrv', } }