HTTP 401 response needed for state-driven HTTP client

22 views
Skip to first unread message

ozwyzard

unread,
Nov 24, 2010, 12:48:08 AM11/24/10
to TurboGears, ozwy...@gmail.com

When a user uses a typical browser to access a page that requires
authentication, TG2 automatically responds with redirect to the login
page (along with 'came_from 'parameter in the login url).

However, I have a scenario where a state-machine driven program
accesses the same page, and expects an HTTP 401 response in order to
retry with authentication credentials.

I would like TG2 to redirect to login page for some browsers, but not
for some others. How would I go about achieving this? I have control
over the program which expects the 401 response. Should I send some
special X-header in the HTTP request and handle it by means of (a)
some magic (or config) in repoze.who, or (b) some other TG2 config, or
(c) some middleware?

I was unable to locate any specific info in the 'Pylons' book to solve
the above.

Thanks.

Diez B. Roggisch

unread,
Nov 24, 2010, 5:58:13 AM11/24/10
to turbo...@googlegroups.com

This can be done using repoze.who plugins. Search this list for mentionings of
it, there have been discussions about this before. And yes, you need of course
to have some distinguishing criteria for them to render a different response.

Diez

ozwyzard

unread,
Nov 25, 2010, 12:24:38 AM11/25/10
to TurboGears
Thank you for the response. I could not find much by searching for
'repoze.who' in this (TG2) group.

My code uses:
allow_only = predicates.not_anonymous()

which is imported from repoze.what. I am trying to dig further to see
if I can tinker with stuff in the environ.

Meanwhile the request call flow is quite involved, and any mod I make
would have to be portable (with newer versions)
http://turbogears.org/2.0/docs/main/RequestFlow.html

I am digging around.

Thanks.

ozwyzard

unread,
Nov 25, 2010, 5:29:32 PM11/25/10
to TurboGears
Thanks. I am new to this and trying to figure this out.

Could you point me to location where the default who.plugin is
configured in TG2?

I am thinking one possible solution is to somehow intercept the
Challenge phase of the repoze.who request processing. Is this the
right approach? I am assuming the behaviour is as follows:
a) The plugins are configured in some who.ini file;
b) The order of the declaration of the plugins equates to the order of
invocation;
c) If a particular plugin cannot handle a request it returns None, so
that the request flow proceeds to the next plugin (in the same
category if required)

If my understanding is correct, then I guess I could add a challenger
plugin, that would check a request header and handle it somehow (... i
don't know yet), such that the resultant behaviour is for the
application to send a 401 (but not a login form redirection).

http://docs.repoze.org/who/1.0/narr.html#lifecycle-of-a-request

I would be obliged if you can point me to more reading material.

Repoze request classifier is one another aspect I should probably
learn about.

Thanks.


On Nov 24, 2:58 am, "Diez B. Roggisch" <de...@web.de> wrote:

Diez B. Roggisch

unread,
Nov 27, 2010, 3:56:29 AM11/27/10
to turbo...@googlegroups.com
It looks as if the google group of this list suffers from severe memory loss.

I'm on the road right now, but maybe I can look up something in my personal copies next week.

Diez

> --
> You received this message because you are subscribed to the Google Groups "TurboGears" group.
> To post to this group, send email to turbo...@googlegroups.com.
> To unsubscribe from this group, send email to turbogears+...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/turbogears?hl=en.
>

Alessandro Molina

unread,
Nov 27, 2010, 2:36:01 PM11/27/10
to turbo...@googlegroups.com
On Wed, Nov 24, 2010 at 6:48 AM, ozwyzard <ozwy...@gmail.com> wrote:
>
> When a user uses a typical browser to access a page that requires
> authentication, TG2 automatically responds with redirect to the login
> page (along with 'came_from 'parameter in the login url).
>
> However, I have a scenario where a state-machine driven program
> accesses the same page, and expects an HTTP 401 response in order to
> retry with authentication credentials.
>

You can also use the came_from parameter inside the post_login method
to change the behaviour.

for example you can use:

if came_from == '/api':
if not request.identity:
return 'FAILED
else:
return 'OK'

instead of letting the code flow until the redirect.
Then your software can call /login?came_from=/api and check for
'FAILED' to retry login.

ozwyzard

unread,
Nov 27, 2010, 4:06:22 PM11/27/10
to TurboGears
Thank you Diez & Alessandro.

I will look into the approach Alessandro suggested. In this approach
the state-driven client would have to call /login?came_from=/api
before attempting any other operation, which sounds okay. However, I
suspect (correct me if I am wrong), if the session times out then the
issue resurfaces. Speaking of which TG1.x had 'visit.xxx' config
items, but TG2 does not (probably hidden in some repoze default
config).

Thanks.

On Nov 27, 11:36 am, Alessandro Molina <alessandro.mol...@gmail.com>
wrote:

ozwyzard

unread,
Dec 8, 2010, 12:58:34 AM12/8/10
to TurboGears

I tried implemented a sample 'Challenge_Decider' plugin and inserted
it as middleware.

Question:
Q1) Does the who.ini have to have complete set of config items, or
just the ones that are over-ridden? See Section 1 below.

Q2) By the time the response comes into the challenge_decider plugin,
the HTTP status is ALREADY set to 302, instead of 401; so the
challenge_decider cannot override the original behavior. Hmmm...??
See Section 2 below. Code in Section 3 below.

Where in the response flow is a 401 being replaced by 302?


=== SECTION 1 ===
At first I tried a who.ini with just the following entries, but it did
not work.
-----

[general]
request_classifier = repoze.who.classifiers:default_request_classifier
challenge_decider = myapp.config.auth:ApiClientChallengeDeciderPlugin

-----


So I had to add other config items. I will check docs to see if this
is a complete set.
-----

[general]
request_classifier = repoze.who.classifiers:default_request_classifier
challenge_decider =
uhoopla.config.auth:ApiClientChallengeDeciderPlugin

[plugin:form]
use = repoze.who.plugins.form:make_plugin
rememberer_name = auth_tkt

[plugin:auth_tkt]
use = repoze.who.plugins.auth_tkt:make_plugin
secret = something

[identifiers]
plugins =
form;browser
auth_tkt

[challengers]
plugins =
form;browser

-----



=== SECTION 2 ===

2010-12-07 21:37:14,978 -- repoze.who request started (/
restricted_url/) --
2010-12-07 21:37:14,978 request classification: browser
2010-12-07 21:37:14,979 identifier plugins registered [<FormPlugin
64267088>, <AuthTktCookiePlugin 64266832>]
2010-12-07 21:37:14,979 identifier plugins matched for classification
"browser": [<FormPlugin 64267088>, <AuthTktCookiePlugin 64266832>]
2010-12-07 21:37:14,979 no identity returned from <FormPlugin
64267088> (None)
2010-12-07 21:37:14,980 no identity returned from <AuthTktCookiePlugin
64266832> (None)
2010-12-07 21:37:14,980 identities found: []
2010-12-07 21:37:14,980 no identities found, not authenticating
21:37:15,014 WARNI [myapp.config.auth.api_client_challenge_decider]
ApiClientChallengeDeciderPlugin __call__ type(environ)=<type 'dict'>
status=302 Found type(headers)=<type 'list'>
21:37:15,014 WARNI [myapp.config.auth.api_client_challenge_decider]
ApiClientChallengeDeciderPlugin __call__ headers=[('Set-Cookie',
'webflash=%7B%22status%22%3A%20%22warning%22%2C%20%22message%22%3A
%20%22The%20current%20user%20must%20have%20been%20authenticated%22%7D;
Path=/'), ('location', '/login?came_from=http%3A%2F
%2F192.168.1.111%3A8080%2Frestricted_url%2F'), ('content-type', 'text/
html')]
2010-12-07 21:37:15,015 no challenge required
2010-12-07 21:37:15,015 -- repoze.who request ended (/restricted_url/)
--


2010-12-07 21:37:15,105 -- repoze.who request started (/login) --
2010-12-07 21:37:15,105 request classification: browser
2010-12-07 21:37:15,105 identifier plugins registered [<FormPlugin
64267088>, <AuthTktCookiePlugin 64266832>]
2010-12-07 21:37:15,106 identifier plugins matched for classification
"browser": [<FormPlugin 64267088>, <AuthTktCookiePlugin 64266832>]
2010-12-07 21:37:15,106 no identity returned from <FormPlugin
64267088> (None)
2010-12-07 21:37:15,106 no identity returned from <AuthTktCookiePlugin
64266832> (None)
2010-12-07 21:37:15,106 identities found: []
2010-12-07 21:37:15,106 no identities found, not authenticating
21:37:15,130 WARNI [myapp.config.auth.api_client_challenge_decider]
ApiClientChallengeDeciderPlugin __call__ type(environ)=<type 'dict'>
status=200 OK type(headers)=<type 'list'>
21:37:15,131 WARNI [myapp.config.auth.api_client_challenge_decider]
ApiClientChallengeDeciderPlugin __call__ headers=[('Pragma', 'no-
cache'), ('Cache-Control', 'no-cache'), ('Content-Type', 'text/html;
charset=utf-8'), ('Set-Cookie', 'webflash=; expires="Fri, 03-Dec-2010
05:37:15 GMT"; Max-Age=0; Path=/'), ('Content-Length', '1791')]
2010-12-07 21:37:15,131 no challenge required
2010-12-07 21:37:15,131 -- repoze.who request ended (/login) --



=== SECTION 3 ===

# -*- coding: utf-8 -*-

"""My api_client auth middleware."""

from repoze.who.interfaces import IChallengeDecider
from zope.interface import implements
import logging

__all__ = ['ApiClientChallengeDeciderPlugin']

log = logging.getLogger(__name__)


class ApiClientChallengeDeciderPlugin(object):
"""
WSGI middleware for ApiClient authentication.
"""
implements(IChallengeDecider)

def __call__(self, environ, status, headers):
log.warn('ApiClientChallengeDeciderPlugin __call__
type(environ)=%s status=%s type(headers)=%s', \
type(environ), status, type(headers))
log.warn('ApiClientChallengeDeciderPlugin __call__ headers=
%s', headers)
h_dict = dict(headers)
if status.startswith('401 '):
if 'X-Api-Client' in h_dict:
return False
return False #True ... FOR NOW JUST TEST whether 401
remains intact in response
return False

Thanks.

ozwyzard

unread,
Dec 29, 2010, 10:40:50 PM12/29/10
to TurboGears
I was able to find a solution on the following thread (after I
searched this group for the term "FriendlyForm").

http://groups.google.com/group/turbogears/browse_thread/thread/c3457df9fa48f743/dffe6704cb2c94ec?lnk=gst&q=FriendlyForm#dffe6704cb2c94ec
Reply all
Reply to author
Forward
0 new messages