Forwarding
Arguably the most flexible and powerful system for authentication is for the
middleware to internally forward the request to a different part of the
application if a 401 status is intercepted. The application itself is then
responsible for displaying that page to take care of displaying a sign in form,
authenticate a user, set a cookie and sign the user out in whichever
way the application author thinks is best.
The foward middleware sets up the cookie middleware used by the form method
but you still need to manually set the cookie after the user has signed in
using your own custom sign in form which you would have implemented yourself.
The cookie can be set like this:
1 | environ['paste.auth_tkt.set_user'](username)
|
To use paste.auth_tkt.set_user, you need to first include cookie in the
authkit.setup.method setting like this:
1 | authkit.setup.method = forward, cookie
|
If you specify a cookie_signoutpath when you set up the authentication
middleware the user will be signed out automatically when visiting that path
but you will still need to display a "signed out" path at that URL or the user
will see a 404 page and think they haven't been signed out.
Alternatively you can sign out the user manually by setting a cookie with the
same name but no value or using the environ['paste.auth_tkt.logout_user']()
method documented here: http://pythonpaste.org/module-paste.auth.auth_tkt.html
Here is a simple complete example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 | from authkit.authenticate import middleware
def sample_app(environ, start_response):
"""
A sample WSGI application that returns a 401 status code when the path
``/private`` is entered, triggering the authenticate middleware to
forward to ``/signin`` where the user is prompted to sign in.
If the sign in is successful a cookie is set and the user can visit
the ``/private`` path.
The path ``/signout`` will display a signed out message if
and sign the user out if cookie_signout = '/signout' is specified in
the middelware config.
The path ``/`` always displays the environment.
"""
if environ['PATH_INFO'] == '/private' and 'REMOTE_USER' not in environ:
start_response('401 Not signed in', [])
elif environ['PATH_INFO'] == '/signout':
start_response('200 OK', [('Content-type', 'text/plain')])
if 'REMOTE_USER' in environ:
return ["Signed Out"]
else:
return ["Not signed in"]
elif environ['PATH_INFO'] == '/signin':
page = """
<html>
<body>
%s
<form action="/signin">
Username: <input type="text" name="username" />
Password: <input type="password" name="password" />
<br />
<input type="submit" value="Sign in" />
</body>
</html>
"""
if 'QUERY_STRING' not in environ:
start_response(
'200 Sign in required',
[('Content-type', 'text/html')]
)
return [page % '<p>Please Sign In</p>']
else:
# Quick and dirty sign in check, do it properly in your code
params = {}
for part in environ['QUERY_STRING'].split('&'):
params[part.split("=")[0]] = part.split('=')[1]
if params['username'] and params['username'] == params['password']:
start_response('200 OK', [('Content-type', 'text/html')])
environ['paste.auth_tkt.set_user'](params['username'])
return ["Signed in."]
else:
start_response('200 OK', [('Content-type', 'text/html')])
return [page % '<p>Invalid details</p>']
start_response('200 OK', [('Content-type', 'text/plain')])
result = ['You Have Access To This Page.\n\nHere is the environment...\n\n']
for k,v in environ.iteritems():
result.append('%s: %s\n'%(k,v))
return result
app = middleware(
sample_app,
setup_method='forward,cookie',
forward_internalpath = '/signin',
cookie_signoutpath = '/signout',
cookie_secret = 'somesecret',
)
if __name__ == '__main__':
from paste.httpserver import serve
serve(app, host='0.0.0.0', port=8080)
|
Note
If you are using any of the examples shown so far with permissions objects (described later)
you will also need to include the paste httpexceptions middleware otherwise
the NotAuthorizedError and NotAuthenticatedError errors will not be
converted to status codes which the authenticate middleware use.
You can set this up by adding the following lines before you add the
authenticate middleware:
1
2 | from paste import httpexceptions
test_app = httpexceptions.middleware(test_app)
|
A full description of the cookie options you can use is described in the cookie options
section.