How to redirect user to original url he asked after login

3,836 views
Skip to first unread message

sagar

unread,
Sep 16, 2011, 11:12:40 AM9/16/11
to web2py-users

How to redirect user to original url he asked after login

I seen there is auth.settings.login_next but I am not able to get that
original requested url.

Please help.

Richard Vézina

unread,
Sep 16, 2011, 11:15:57 AM9/16/11
to web...@googlegroups.com
It suposed to be managed by default no?

Richard

sagar nigade

unread,
Sep 16, 2011, 11:24:28 AM9/16/11
to web...@googlegroups.com
Thanks for reply richard but not geting you.

sagar nigade

unread,
Sep 16, 2011, 11:31:14 AM9/16/11
to web...@googlegroups.com
By deafult it is not redirected to url he asked
I had definded auth.settings.login_next=url(default url)
but by removing it also not redirecting to the url user asked.

Richard Vézina

unread,
Sep 16, 2011, 11:40:48 AM9/16/11
to web...@googlegroups.com
Do you use the decorator ?

If not try the 

@auth.requires_login()

It maybe the decorator that manage the redirection... 

I have look in my app and can't see that I do something special for allow redirection...

Richard

Anthony

unread,
Sep 16, 2011, 11:44:03 AM9/16/11
to web...@googlegroups.com
On Friday, September 16, 2011 11:31:14 AM UTC-4, sagar wrote:
By deafult it is not redirected to url he asked
I had definded auth.settings.login_next=url(default url)
but by removing it also not redirecting to the url user asked.

If the user goes to a url that requires login but is not logged in, they should be redirected to the login page, and the following query string should be appended to the login url:

_next=original_url

After login, the user should then be redirected to the original url. Is that not happening? Do you see '_next' in the login url query string? Can you show your code for setting up Auth and for an example action that requires login but is not getting redirected properly?

Anthony

sagar nigade

unread,
Sep 16, 2011, 11:45:39 AM9/16/11
to web...@googlegroups.com
yes I have definded decorater and user is get  redirected to login page and after that he get redirected to default page.
 
but after login I want to him  to redirect origanl url he asked.

Massimo Di Pierro

unread,
Sep 16, 2011, 12:57:07 PM9/16/11
to web2py-users
This is in general a security hazard so it needs to be enabled:

auth = Auth(db,auto_redirect=[URL(...),URL(...)])

where URL(...) are the urls where it is safe to redirect to after
login if originally requested. trunk only.

On Sep 16, 10:45 am, sagar nigade <browse2sa...@gmail.com> wrote:
> yes I have definded decorater and user is get  redirected to login page and
> after that he get redirected to default page.
>
> but after login I want to him  to redirect origanl url he asked.
>
> On Fri, Sep 16, 2011 at 9:10 PM, Richard Vézina <ml.richard.vez...@gmail.com
>
>
>
>
>
>
>
> > wrote:
> > Do you use the decorator ?
>
> > If not try the
>
> > @auth.requires_login()
>
> > It maybe the decorator that manage the redirection...
>
> > I have look in my app and can't see that I do something special for allow
> > redirection...
>
> > Richard
>
> > On Fri, Sep 16, 2011 at 11:31 AM, sagar nigade <browse2sa...@gmail.com>wrote:
>
> >> By deafult it is not redirected to url he asked
> >> I had definded auth.settings.login_next=url(default url)
> >> but by removing it also not redirecting to the url user asked.
>
> >> On Fri, Sep 16, 2011 at 8:54 PM, sagar nigade <browse2sa...@gmail.com>wrote:
>
> >>> Thanks for reply richard but not geting you.
>
> >>> On Fri, Sep 16, 2011 at 8:45 PM, Richard Vézina <
> >>> ml.richard.vez...@gmail.com> wrote:
>
> >>>> It suposed to be managed by default no?
>
> >>>> Richard
>

Anthony

unread,
Sep 16, 2011, 1:31:12 PM9/16/11
to web...@googlegroups.com
On Friday, September 16, 2011 12:57:07 PM UTC-4, Massimo Di Pierro wrote:
This is in general a security hazard so it needs to be enabled:

auth = Auth(db,auto_redirect=[URL(...),URL(...)])

where URL(...) are the urls where it is safe to redirect to after
login if originally requested. trunk only.

Even without auto_redirect, isn't the original _next functionality still in place? This change (http://code.google.com/p/web2py/source/detail?r=ae2afc33d6d6afe7de9a2700206d787b00ac1da8) improved the security by ensuring _next can only include relative URLs -- isn't that sufficient to plug the security hole? How does auto_redirect interact with the standard _next functionality?

Anthony

Massimo Di Pierro

unread,
Sep 16, 2011, 2:13:48 PM9/16/11
to web2py-users
auto_redirect works for any redierct, even if not relative.
So you send an email like

click here
http://...../app/path

and if http://...../app/path requires login, you get redirected to
login but not back to http://...../app/path unless '/app/path' is in
auto_redirect.
Internally is still uses _next.

Sometimes I think the need for auto_redirect is paranoid.

Massimo

On Sep 16, 12:31 pm, Anthony <abasta...@gmail.com> wrote:
> On Friday, September 16, 2011 12:57:07 PM UTC-4, Massimo Di Pierro wrote:
>
> > This is in general a security hazard so it needs to be enabled:
>
> > auth = Auth(db,auto_redirect=[URL(...),URL(...)])
>
> > where URL(...) are the urls where it is safe to redirect to after
> > login if originally requested. trunk only.
>
> Even without auto_redirect, isn't the original _next functionality still in
> place? This change
> (http://code.google.com/p/web2py/source/detail?r=ae2afc33d6d6afe7de9a2...)

Jonathan Lundell

unread,
Sep 16, 2011, 2:21:07 PM9/16/11
to web...@googlegroups.com
On Sep 16, 2011, at 11:13 AM, Massimo Di Pierro wrote:

> auto_redirect works for any redierct, even if not relative.
> So you send an email like
>
> click here
> http://...../app/path
>
> and if http://...../app/path requires login, you get redirected to
> login but not back to http://...../app/path unless '/app/path' is in
> auto_redirect.
> Internally is still uses _next.
>
> Sometimes I think the need for auto_redirect is paranoid.

What's the hazard? Presumably there's nothing to stop the user from going to the same URL after a successful login, so why not automatically?

Anthony

unread,
Sep 16, 2011, 2:35:20 PM9/16/11
to web...@googlegroups.com
On Friday, September 16, 2011 2:21:07 PM UTC-4, Jonathan Lundell wrote:

> Sometimes I think the need for auto_redirect is paranoid.

What's the hazard? Presumably there's nothing to stop the user from going to the same URL after a successful login, so why not automatically?

Jonathan Lundell

unread,
Sep 16, 2011, 3:00:59 PM9/16/11
to web...@googlegroups.com
But the auto_redirect list doesn't protect against that. 

As I read the code, auto-redirect after login (in the sense of going back to the target URL) a) only uses the output of URL(), with no domain (so no phishing), and b) stores its redirection in session.

I didn't look too closely at the _next= logic, but it doesn't appear to care about the auto_redirect list. If those URLs need to be checked, wouldn't it be adequate to require that they're relative URLs (in the sense of not including a domain)?

Michele Comitini

unread,
Sep 16, 2011, 4:19:57 PM9/16/11
to web...@googlegroups.com
I did not notice that feature

-1

. adds complexity in API and code
. not a security feature

IMHO it is more dangerous than nothing

mic


2011/9/16 Jonathan Lundell <jlun...@pobox.com>:

Anthony

unread,
Sep 16, 2011, 4:27:57 PM9/16/11
to web...@googlegroups.com
Right, I thought you were asking about the general need to restrict what is allowed in _next. I'm also confused about auto_redirect -- looks like it is still limited to URLs internal to the app, which should be handled adequately by the new _next logic that allows only relative URLs.

Anthony 

Massimo Di Pierro

unread,
Sep 16, 2011, 4:56:17 PM9/16/11
to web2py-users
It is not handle adequately if the app uses an external
authentication. perhaps there is a better solution... let me think
about this some more.

Basically the issue is this code in web2py/gluon/tools.py

if auto_redirect and URL() in auto_redirect:
if not self.user:
if not session._auth_next:
session._auth_next = URL(args=request.args,
vars=request.get_vars)
if auto_redirect and not URL() in auto_redirect and \
self.user and session._auth_next:
next = session._auth_next
session._auth_next = None
redirect(next)



should it just be?

if not self.user:
if not session._auth_next:
session._auth_next = URL(args=request.args,
vars=request.get_vars)
if self.user and session._auth_next and not self.user and
session._auth_next.startswith(URL()):
next = session._auth_next
session._auth_next = None
redirect(next)

and get rid of auto_redirect?

Jonathan Lundell

unread,
Sep 16, 2011, 5:07:40 PM9/16/11
to web...@googlegroups.com

I think so. You've got a contradictory self.user test there, though. Is the startswith test needed? Right? If we're already at that URL, why redirect?

Jonathan Lundell

unread,
Sep 16, 2011, 7:19:06 PM9/16/11
to web...@googlegroups.com
I'm a little confused.

The auth requires decorators redirect to (say) the login page, and stuff _next=URL() into vars. This is a bit of a hazard because vars can get corrupted in the outside world, and we check it somewhat (I'm not 100% convinced by the check, and it's all so not-DRY, but that's another story).

OTOH, Auth.__init__ has some auto_redirect logic that stores the next-URL in the session (good, that's more secure). But presumably that path is different from the decorator logic (otherwise we'd have two possible next-URLs, one in vars, one in the session).

So what's the Auth() logic used for? Could these logic paths be unified? Wouldn't it be better to always store the next-URL link in the session instead of exposing it in the redirect URL?

Straighten me out, please.

Anthony

unread,
Sep 16, 2011, 9:28:14 PM9/16/11
to web...@googlegroups.com
On Friday, September 16, 2011 4:56:17 PM UTC-4, Massimo Di Pierro wrote:

should it just be?

        if not self.user:
                if not session._auth_next:
                    session._auth_next = URL(args=request.args,
                                             vars=request.get_vars)

Won't the above store the first URL in the app visited by the non-logged-in user in _auth_next? But don't we want it to be the first URL visited that requires login? The user might start at /a/c/index (which doesn't require login) and then go to /a/c/mysite (which does require login, so redirects to the login page), but then would get sent back to /a/c/index instead of /a/c/mysite after login, no?
 
        if self.user and session._auth_next and not self.user and
session._auth_next.startswith(URL()):
            next = session._auth_next
            session._auth_next = None
            redirect(next)

How does this improve upon the current _next logic, which limits _next to relative URLs? Doesn't this code only store relative URLs from the current app in _auth_next? Does this have something to do with the possibility of login via CAS, so need to handle _next in __init__() rather than login()?

Anthony

Jonathan Lundell

unread,
Sep 16, 2011, 9:44:54 PM9/16/11
to web...@googlegroups.com
Be nice to consolidate all this into one set of shared logic, using the session instead of the query string, with a uniform way of setting it and redirecting.

Massimo Di Pierro

unread,
Sep 17, 2011, 12:32:49 AM9/17/11
to web2py-users
OK. So I followed the advice and rewrote all the next logic in trunk
(for Auth, not Crud) to use sessions.

I tested with normal login, janrain and cas. It seems to work. Yet if
you can test it too would be best.

there are two weird cases (and they were weird before):
- app A redirect to CAS provider app B. user does not have account in
app B and needs register. app B does not redirect to app A
- user tries to visit page in app A that requires login, after login
the app forces a redirection to edit profile, app A does not redirect
use to original requested page

PLEAE CHECK THIS! There are subtleties.

Jonathan Lundell

unread,
Sep 17, 2011, 12:48:30 AM9/17/11
to web...@googlegroups.com
On Sep 16, 2011, at 9:32 PM, Massimo Di Pierro wrote:

> OK. So I followed the advice and rewrote all the next logic in trunk
> (for Auth, not Crud) to use sessions.
>
> I tested with normal login, janrain and cas. It seems to work. Yet if
> you can test it too would be best.
>
> there are two weird cases (and they were weird before):
> - app A redirect to CAS provider app B. user does not have account in
> app B and needs register. app B does not redirect to app A
> - user tries to visit page in app A that requires login, after login
> the app forces a redirection to edit profile, app A does not redirect
> use to original requested page
>
> PLEAE CHECK THIS! There are subtleties.

It's certainly cleaner, anyway.

One thing I don't understand (and it's not new, just relocated):

+def is_relative(url):
+ return url and not url[0] == '/' and url[:4] != 'http'
+

I don't see how this can work, since the output of URL() typically (always?) starts with '/'. Doesn't it?

Jonathan Lundell

unread,
Sep 17, 2011, 1:00:45 AM9/17/11
to web...@googlegroups.com
On Sep 16, 2011, at 9:32 PM, Massimo Di Pierro wrote:

> OK. So I followed the advice and rewrote all the next logic in trunk
> (for Auth, not Crud) to use sessions.
>
> I tested with normal login, janrain and cas. It seems to work. Yet if
> you can test it too would be best.
>
> there are two weird cases (and they were weird before):
> - app A redirect to CAS provider app B. user does not have account in
> app B and needs register. app B does not redirect to app A
> - user tries to visit page in app A that requires login, after login
> the app forces a redirection to edit profile, app A does not redirect
> use to original requested page
>
> PLEAE CHECK THIS! There are subtleties.

I also don't entirely understand this:

def pop_next(self):
next = current.session._auth_next
if next and next.startswith(URL()):
next = current.session._auth_next = None
return next

The startswith test: are we simply saying that if the startswith test is met, then we're already at the destination, so don't redirect?

But shouldn't we be setting _auth_next to None in the redirection case?

Massimo Di Pierro

unread,
Sep 17, 2011, 10:51:40 AM9/17/11
to web2py-users
> I also don't entirely understand this:
>
>     def pop_next(self):
>         next = current.session._auth_next
>         if next and next.startswith(URL()):
>             next = current.session._auth_next = None
>         return next
>
> The startswith test: are we simply saying that if the startswith test is met, then we're already at the destination, so don't redirect?

I guess it go both ways.

I am worried the new session mechanism may break when multiple windows
are open, or when IFRAME and LOAD are used. I may have to revert this.

Jonathan Lundell

unread,
Sep 17, 2011, 11:06:09 AM9/17/11
to web...@googlegroups.com

It seems to me that there are two issues here. One is cleaning up the logic to make it uniform, DRY and understandable. The other is deciding where to put the next link (and doing proper validation of the URL).

I understand (I think) the basic use case for @requires_login, I think.

What's the use case for saving the return link in Auth()?

Does it make sense to try to save a next link in cases like change-password? Profile editing?

I'm fine with reverting for now, but I really think that this logic is due for a review and cleanup. Maybe starting with a spec: what are we really trying to do? And how do these dynamic _next links relate to the various next-URL links in auth.settings?

weheh

unread,
Sep 17, 2011, 11:34:42 AM9/17/11
to web2py-users
@sagar: FWIW, this is what I usually do:

def login():
return dict(form=auth.login(next=request.vars._next or
auth.settings.login_next))

My site combines pages that don't require login with those that do.

Massimo Di Pierro

unread,
Sep 17, 2011, 11:36:41 AM9/17/11
to web2py-users
> It seems to me that there are two issues here. One is cleaning up the logic to make it uniform, DRY and understandable. The other is deciding where to put the next link (and doing proper validation of the URL).
>
> I understand (I think) the basic use case for @requires_login, I think.
>
> What's the use case for saving the return link in Auth()?
>
> Does it make sense to try to save a next link in cases like change-password? Profile editing?
>
> I'm fine with reverting for now, but I really think that this logic is due for a review and cleanup.

I agree with that. I think we may have to release 1.99.1 with the
_next solution and then try improve it.

> Maybe starting with a spec: what are we really trying to do? And how do these dynamic _next links relate to the various next-URL links in auth.settings?

I will try write more about this later today.

Massimo

Massimo Di Pierro

unread,
Sep 17, 2011, 11:45:40 AM9/17/11
to web2py-users
The basic use case is this:

User clicks on a link that requires_login and gets redirected to the
login page. After login the user is redirected to the original
requested page.

Exceptions:
- the login is outsourced to janrain
- the login is outsourced to cas or other open-id
- the login is not possible and the user must first register
- after login is redirected to the intended page but the app logic
finds this user has incomplete profile and redirects to profile
editing (*)
- what if the user is impersonating another user? (?)
- the user is visiting a page that does not require login but LOADs a
component that does (?)
- the user is visiting a page that does not require login but LOADs a
component that does
- the user has another window open (**)

(*) is not currently supported. (?) not sure if it works (**) worked
with _next but not not with session._auth_next.

Massimo Di Pierro

unread,
Sep 17, 2011, 11:46:26 AM9/17/11
to web2py-users
Fixed a typo:

The basic use case is this:
User clicks on a link that requires_login and gets redirected to the
login page. After login the user is redirected to the original
requested page.
Exceptions:
- the login is outsourced to janrain
- the login is outsourced to cas or other open-id
- the login is not possible and the user must first register
- after login is redirected to the intended page but the app logic
finds this user has incomplete profile and redirects to profile
editing (*)
- what if the user is impersonating another user? (?)
- the user is visiting a page that does not require login but LOADs a
component that does (?)
- the user is visiting a page that does not require login but IFRAMEs

Jonathan Lundell

unread,
Sep 17, 2011, 12:26:49 PM9/17/11
to web...@googlegroups.com

The old logic saves a next link in session in Auth(). What's that for?

Anthony

unread,
Sep 17, 2011, 4:24:40 PM9/17/11
to web...@googlegroups.com
Sounds like this will be completely re-thought, so maybe comments on the current (trunk) code aren't necessary, but here are some observations (not sure if these are correct because I haven't tested anything, just quickly looked at the code):
  • It still looks like _auth_next will be the first URL visited during the session, whether or not it requires login, but then it can't get overwritten. So won't the redirect end up going to the first URL of the session rather than the last URL right before login (we want the latter, no)?
  • _auth_next is reset to None if it starts with the current URL, but don't we want it to be an exact match for the current URL (including args and vars) in order to conclude that the redirect has happened?
  • I'm not sure I'm following properly, but does this involve an additional redirect -- one immediately after login, and then another when that redirect hits Auth.__init__?
  • This is repeated many times:
        if is_relative(next):
            next = self.url(next.replace('[id]', str(form.vars.id))) 

Instead of the is_relative function, how about a replace_id function that checks for a relative URL and does the replace? So, the above would just be:

        next = replace_id(next,form)
 

Anthony

Jonathan Lundell

unread,
Sep 17, 2011, 4:30:45 PM9/17/11
to web...@googlegroups.com
On Sep 17, 2011, at 1:24 PM, Anthony wrote:

Sounds like this will be completely re-thought, so maybe comments on the current (trunk) code aren't necessary, but here are some observations (not sure if these are correct because I haven't tested anything, just quickly looked at the code):
  • It still looks like _auth_next will be the first URL visited during the session, whether or not it requires login, but then it can't get overwritten. So won't the redirect end up going to the first URL of the session rather than the last URL right before login (we want the latter, no)?
  • _auth_next is reset to None if it starts with the current URL, but don't we want it to be an exact match for the current URL (including args and vars) in order to conclude that the redirect has happened?
  • I'm not sure I'm following properly, but does this involve an additional redirect -- one immediately after login, and then another when that redirect hits Auth.__init__?
  • This is repeated many times:
        if is_relative(next):
            next = self.url(next.replace('[id]', str(form.vars.id))) 

Instead of the is_relative function, how about a replace_id function that checks for a relative URL and does the replace? So, the above would just be:

        next = replace_id(next,form)
 


What is the id logic, anyway? Some CRUD thing?

Massimo Di Pierro

unread,
Sep 17, 2011, 11:06:06 PM9/17/11
to web2py-users
There are cases when the original "next" got lost. I did not full
track the cause of the problem.
The code in Auth was a quick hack to handle it.

Massimo Di Pierro

unread,
Sep 17, 2011, 11:07:47 PM9/17/11
to web2py-users
In the crud case

cud.create(...,next='action/[id]')

automatically fills in the id of the record being created.

Auth allows you to the same when a new user is created.

Massimo Di Pierro

unread,
Sep 18, 2011, 1:12:18 PM9/18/11
to web2py-users
I rewrite the login once more... I reverted to the old mechanism of
using vars=dict(_next=....) to carry one the location where to
redirect after login. The problem is that this _next gets lost when
login is outsourced (cas, janrain, others). This is difficult to fix
without changing the logic of many login_methods (details below). So
we still need to use the session logic to deal with this case. I moved
such logic from Auth() to auth.login(). Does this break anybody's
code?

The problem

you visit
http://..../app1/default/xxx
it requires login so it redirects to
http://..../app1/default/user/login?_next=/app1/default/xxx
it requires federate auth so it redirects to (*)
http://..../app2/default/user/login?service=http://..../app1/default/user/login
which does its thing and redirects back to
http://..../app1/default/user/login

and _next is lost.
At step lost we could pass
service=urllib.quote(http://..../app1/default/user/login?_next=/app1/
default/xxx)

but I do not know for a fact how single sign on services deal with
variables in the service url. Each one is different It may be
implementation dependent.

Also is there a security risk? What if the _next is a private urls
that includes a uuid? Do we want to disclose it to the openid
provider?

Massimo





On Sep 17, 10:06 pm, Massimo Di Pierro <massimo.dipie...@gmail.com>
wrote:

Michele Comitini

unread,
Sep 18, 2011, 3:55:01 PM9/18/11
to web...@googlegroups.com

Passing _next to the authenticating app is exactly what oauth specification does for the same problem.
The callback URL must be under an agreed domain and path.

Mic

Massimo Di Pierro

unread,
Sep 18, 2011, 4:16:29 PM9/18/11
to web2py-users
Yes but the problem is that there are two "_next" variables

- one is the page oauth should redirect to so that the oauth consumer
knows the user is being authenticated (usually that's 'user/login')
- one is the page web2py should redirect to after oauth returns.

that means that these should be a _next inside the _next when calling
oauth, and that creates the problem.


On Sep 18, 2:55 pm, Michele Comitini <michele.comit...@gmail.com>
wrote:
> Passing _next to the authenticating app is exactly what oauth specification
> does for the same problem.
> The callback URL must be under an agreed domain and path.
>
> Mic
> Il giorno 18/set/2011 19:12, "Massimo Di Pierro" <massimo.dipie...@gmail.com>

Michele Comitini

unread,
Sep 18, 2011, 5:11:03 PM9/18/11
to web...@googlegroups.com

Massimo Di Pierro

unread,
Sep 18, 2011, 6:52:53 PM9/18/11
to web2py-users
In principle. Yes. In practice, this will require changing many (all?)
of the login_methods/*.py, not just tools.py.

For example we never pass the return url to cas_auth.py, it builds it
automatically based on a prefix.

It would be hard to check all the login_methods.

Second, should we expose URLs to the openid provider? There could be a
security implication there.



On Sep 18, 4:11 pm, Michele Comitini <michele.comit...@gmail.com>
wrote:
> Proper url encoding does not solve the trouble?
>
> http://oauthserver/auth?_next=http://web2py/app/default/user/login?_n...
>
> mic
>
> 2011/9/18 Massimo Di Pierro <massimo.dipie...@gmail.com>:

Michele Comitini

unread,
Sep 18, 2011, 7:30:37 PM9/18/11
to web...@googlegroups.com
> Second, should we expose URLs to the openid provider? There could be a
> security implication there.

I would consider this minor, you are already naked when you use third
party authorization... ;-)

Massimo Di Pierro

unread,
Sep 18, 2011, 8:05:23 PM9/18/11
to web2py-users
Consider google docs. You have the option to make a document readable/
writable to anybody with the URL. Imagine of creating an app similar
to that. If you use openid now the openid provider stores the uuids in
the web server logs.

I agree it is not a major concern but I am rising anyway so we can
think about it.

Massimo

On Sep 18, 6:30 pm, Michele Comitini <michele.comit...@gmail.com>
wrote:
Reply all
Reply to author
Forward
0 new messages