This is part of The Pile, a partial archive of some open source mailing lists and newsgroups.
Date: Fri, 12 Apr 2002 16:22:53 -0400
From: Fran Fabrizio <ffabrizio@mmrd.com>
To: modperl@apache.org
Subject: Enforcing user logged in from only 1 browser?
Hello all,
I'm looking for a straightforward approach to extend our AuthCookie
sessioning to enforce that a user is only logged in from one browser at
a time. For us, it would suffice that if the user tries to log in from
a 2nd browser, the first session would just be expired.
I was thinking that upon login I could save the AuthCookie key in that
user's db entry as current_session_key and I could blank it out when
they explicitly log out. Then during login, I would be able to see if
there's another key still out there for them. The tricky part for me is
figuring out if that key is still an -active- session as opposed to
having just left their browser open last night or something. And also,
if I do determine that it is another active one, how would I expire it?
Anyone done this type of thing previously?
===
From: <leibniz@lamebulun.net>
To: "Fran Fabrizio" <ffabrizio@mmrd.com>
Cc: "Mod Perl Mailing List" <modperl@perl.apache.org>
Subject: Re: Enforcing user logged in from only 1 browser?
Date: Sat, 13 Apr 2002 02:21:39 +0300
perhaps you can generate a new session id for each page displayed.
for example a user logs in. he gets $sess_id1. automatically the session id
gets changed to $sess_id2 and all the links from that page contain the
second one.
so if he clicks somewhere on the page he will go on to a page with the new
session id
while you generate a third one ,$sess_id3. and so on...
i'm not sure about what you mean about another browser.
another browser window or another browser program.
you can always check their enviroment variables and see if it matches the
previous entry in your db.
but that can cost some more space.
===
To: Fran Fabrizio <ffabrizio@mmrd.com>
Cc: modperl@perl.apache.org
From: gphat@cafes.net
Subject: Re: Enforcing user logged in from only 1 browser?
Date: Fri, 12 Apr 2002 23:35:55 GMT
How are you handling your sessions? I use Apache::Session::Postgres.
In my scenario, if I needed to do this, I would check the list of valid
sessions I have for one that exists for the user. ie, if 'gphat' tries to
login, I check to see if any of the sessions the db are for user gphat. If so,
eliminate it and create a new one.
Using an Apache::Session might not be the answer, as you'd have to check each
active session. Depending on the situation, that might not be acceptable.
You could roll your own session handling, it's not that hard. Then add a
username field to the session table, so you can index and search by it.
===
From: "Peter Bi" <mod_perl@att.net>
To: "Fran Fabrizio" <ffabrizio@mmrd.com>, <modperl@apache.org>
Subject: Re: Enforcing user logged in from only 1 browser?
Date: Fri, 12 Apr 2002 21:43:00 -0700
To make a perfect system like this probably needs users to sign-off
faithfully by every session.
Peter Bi
===
Date: Mon, 15 Apr 2002 09:04:07 -0400
From: Fran Fabrizio <ffabrizio@mmrd.com>
To: gphat@cafes.net
Subject: Re: Enforcing user logged in from only 1 browser?
gphat@cafes.net wrote:
> How are you handling your sessions? I use Apache::Session::Postgres.
I'm using AuthCookie. A customization of AuthCookieDBI to be specific.
However, I also use Apache::Session. Basically, I authenticate
with AuthCookie, then I pass the authenticated username over to
Apache::Session::File (for now, probably soon to become A:S:Postgres)
and use the user id as the session key. In that session I store user
preferences, etc...
> In my scenario, if I needed to do this, I would check the list of valid
> sessions I have for one that exists for the user. ie, if 'gphat' tries to
> login, I check to see if any of the sessions the db are for user gphat. If so,
> eliminate it and create a new one.
I can also detect if the user had an existing session already (I store
the AuthCookie key in the db for each user). However, the question is
just because there's an existing AuthCookie key, it doesn't mean they
have another active session. It could just be an expired one. So, it
seems the logic would go something like:
1. User logs in
2. Check for another session key for this user
3. If found, check to see if it's expired.
4. If not expired, alert user and ask if user wants to expire older session
5. Expire older session
It's #5 that's troublesome. I wasn't sure how I could expire the older
session (since the session key that matters is sitting client side). I
guess I could keep a table of invalidated session keys, and check
against that every time in along with all the other checks going on in
authen_ses_key(). I was just mainly asking if there was an existing
solution out there.
The main requirement that we're trying to solve is that a user cannot be
signed on from more than one location at once. Mainly because this
probably means that they walked away from a computer with an active
session on it, which isn't good. I suppose an inactivity timer might be
helpful, too.
Thanks,
Fran
===
To: Fran Fabrizio <ffabrizio@mmrd.com>, modperl@perl.apache.org
From: gphat@cafes.net
Subject: Re: Enforcing user logged in from only 1 browser?
Date: Mon, 15 Apr 2002 14:47:46 GMT
> It's #5 that's troublesome. I wasn't sure how I could expire the older
> session (since the session key that matters is sitting client side). I
> guess I could keep a table of invalidated session keys, and check
> against that every time in along with all the other checks going on in
> authen_ses_key(). I was just mainly asking if there was an existing
> solution out there.
I'm not sure I follow your session id problem. When I check a session, I ask
the client for it's ID, then look the session up by ID. To 'expire' the
session, I simply delete it from the session store (File or Postgres).
Cory 'G'
Watson
===
Date: Mon, 15 Apr 2002 10:00:48 -0400
From: Fran Fabrizio <ffabrizio@mmrd.com>
To: gphat@cafes.net
Subject: Re: Enforcing user logged in from only 1 browser?
> I'm not sure I follow your session id problem. When I check a session, I ask
> the client for it's ID, then look the session up by ID. To 'expire' the
> session, I simply delete it from the session store (File or Postgres).
The confusion is you aren't using sessions in the authentication sense
of the word. You say you look up the session by ID...but how do you
know the ID? Because they previously authenticated. I'm using sessions
at the authentication step, via AuthCookie.
Unfortunately, there's some terminology muddling...AuthCookie calls it a
session when it establishes that a user is a valid user and sets a
cookie on their browser. Apache::Session considers a session a series
of page hits from the same user. It assumes you've already done
whatever you need to do to assure that the user is valid.
So to expire a session in the sense of AuthCookie, you have to
explicitly tell AuthCookie that a particular key is no longer valid,
because otherwise everything about the AuthCookie cookie looks valid,
and you'll have 'gphat' logged in validly from two different browsers.
That's completely separate from the Apache::Session session, which has
nothing on the client side (other than some method for obtaining the
session id, which could be a cookie, a hidden form field, a munged URL,
or in my case, the userid obtained from AuthCookie authentication).
===
Date: Mon, 15 Apr 2002 11:02:16 -0400
From: Perrin Harkins <perrin@elem.com>
To: Fran Fabrizio <ffabrizio@mmrd.com>
Subject: Re: Enforcing user logged in from only 1 browser?
Fran Fabrizio wrote:
> Unfortunately, there's some terminology muddling...AuthCookie calls it a
> session when it establishes that a user is a valid user and sets a
> cookie on their browser. Apache::Session considers a session a series
> of page hits from the same user. It assumes you've already done
> whatever you need to do to assure that the user is valid.
I think you may find that neither of these does everything you need
without a bit of additional coding. The common way to do this sort of
thing is to use Apache::Session to track sessions (as in a series of
page hits from the same user), and if the user authenticates, you put
his user ID into the session data.
You would have to do the auth part yourself, as well as the actual
cookie handling, or else hack AuthCookie to cooperate with Apache::Session.
===
Date: Mon, 15 Apr 2002 10:22:40 -0400
From: Fran Fabrizio <ffabrizio@mmrd.com>
To: Perrin Harkins <perrin@elem.com>
Subject: Re: Enforcing user logged in from only 1 browser?
> You would have to do the auth part yourself, as well as the actual
> cookie handling, or else hack AuthCookie to cooperate with Apache::Session.
This is exactly what I've done. I've modified AuthCookieDBI to create
an Apache::Session session as soon as it knows it has a valid user.
Then if the user is conscientious and logs out or the AuthCookie key is
expired, AuthCookie will take care of deleting the Apache::Session
session. That part is working. The only thing is AuthCookie needs some
foolproof way to check to see if that same user already has a valid
session from somewhere else that is still active. It's easy enough to
just delete/reset/blow on top of any older Apache::Session objects for
this user. It's not so apparent (to me) how to tell AuthCookie to no
longer accept the older key for this user. What would otherwise happen
is that the user would have two perfectly valid sessions from two
different browsers, potentially blowing over his Apache::Session object
back and forth. Not to mention someone else could hop onto the old
browser and see/do stuff they are not authorized to see/do.
Yes, I realize that a certain amount of this depends simply on not
having idiots for users. =) But I like to help them out as much as
possible. We don't consider the 2 simulataneous logins to be super
crucial to avoid, as our expire times are kept fairly short anyhow, but
we do think it makes for a more robust interface.
A whimsical example of what could happen is that user #1 leaves a
browser session open and goes to a different station and logs on. User
#2 wants to play joke on user #1. User #2 goes to user #1's old session
and changes his style preferences. User #1 is at other workstation, and
the entire look of the site changes instantly before his eyes. Of
course, more serious things can happen if user #1 happens to be an
administrator or user with some advanced privileges to our system and
user #2 happens to be an arse looking to get fired.
Anyways, just talking through the problem repeatedly here has given me
some idea so I am off to try those. =)
===
From: "Jeff" <jaa.perl@aquabolt.com>
To: "'Fran Fabrizio'" <ffabrizio@mmrd.com>
Cc: <modperl@perl.apache.org>
Subject: RE: Enforcing user logged in from only 1 browser?
Date: Mon, 15 Apr 2002 16:36:54 +0100
Perrin Harkins [mailto:perrin@elem.com] wrote:
> Fran Fabrizio wrote:
> > Unfortunately, there's some terminology muddling...AuthCookie calls it
> a
> > session when it establishes that a user is a valid user and sets a
> > cookie on their browser. Apache::Session considers a session a series
>
> > of page hits from the same user. It assumes you've already done
> > whatever you need to do to assure that the user is valid.
>
> I think you may find that neither of these does everything you need
> without a bit of additional coding. The common way to do this sort of
> thing is to use Apache::Session to track sessions (as in a series of
> page hits from the same user), and if the user authenticates, you put
> his user ID into the session data.
>
> You would have to do the auth part yourself, as well as the actual
> cookie handling, or else hack AuthCookie to cooperate with
> Apache::Session.
Forgive a mod_perl newbie for non mod_perl thinking, but this
is (a simplified overview) of how I would approach this:
request for any protected page
- if no existing session data [so not authenticated]
create new session
remember target page in session
redirect to login page
otherwise
allow access to page
login page POST with user id / password.
- if ( valid user / password )
add user info to session
expire previous session [id was saved in db]
save new session id in the database [for next login]
redirect to the originally requested page
otherwise
redirect to login page with error message
If someone now tries to come back with an old session id,
there is no data in the session, so they will be considered
un-authenticated, and will get redirected to login page.
In PHP, I would expire the old session during login, by deleting
the session storage, if it still existed. mod_perlers can probably
best suggest how to empty the contents of a session and / or
remove the session storage.
As the decisions are made based on information on the server,
this should also be safe from users pressing the BACK button,
as BACK to a protected page will redirect to login.
I'm not sure what happens with using History to select the page
that immediately followed login - probably the usual 'Do you
want me to post again?' question from Explorer etc.
I can see two issues with this approach:
1) login ping-pong. Two users using the same id/password will
be logging each other out as they log in (but this seems
to be what you want?)
2) it does not prevent the user from having the same pages
open multiple times within the same browser instance
(eg when the user presses Ctrl-N after having logged in)
===
Date: Mon, 15 Apr 2002 11:58:28 -0400
From: Fran Fabrizio <ffabrizio@mmrd.com>
To: Jeff <jaa.perl@aquabolt.com>
Subject: Re: Enforcing user logged in from only 1 browser?
Jeff wrote:
> Forgive a mod_perl newbie for non mod_perl thinking, but this
> is (a simplified overview) of how I would approach this:
>
> request for any protected page
> - if no existing session data [so not authenticated]
> create new session
> remember target page in session
> redirect to login page
> otherwise
> allow access to page
Yes, this is exactly how AuthCookie works.
> login page POST with user id / password.
> - if ( valid user / password )
> add user info to session
AuthCookie sessions don't also carry user info, but that's why I create
an Apache::Session to do it.
> expire previous session [id was saved in db]
Right-o. How to expire an otherwise valid AuthCookie session is the
question.
>[snip]
> If someone now tries to come back with an old session id,
> there is no data in the session, so they will be considered
> un-authenticated, and will get redirected to login page.
If someone comes in with an old active session (assuming it's not
expired) AuthCookie examines the MD5 hash and says "everything matches
up, you may proceed". I need to add a step here to say "if newer
session for user exists, kill this one". But you've just given me a
great idea! When the old session comes in, there will still be data in
the session (because the new session is using it - sessions are keyed by
user id, not session id). So I can't rely on an empty session data to
be the clue. BUT - I can store the session key that generated this
session data in with the session data, and try to match those up. I
like it. This will work, I think. Thank you. =) Why do I use the user
id instead of the session id for the session key? Because it makes the
code easier for other developers ("a user will always have a session
with the key $r->connection->user") and that gets passed around
automatically rather than me having to expose the developers to the
AuthCookieDBI cookie directly. I just find it easier to rely on
$r->connection->user to always be the session key.
> [snip]
> I can see two issues with this approach:
> 1) login ping-pong. Two users using the same id/password will
> be logging each other out as they log in (but this seems
> to be what you want?)
Yes, or at least a page saying "you're logged in elsewhere, do you want
to ax that one and continue, or abort this login". Even a forced logout
of the older one without the user knowing is fine.
> 2) it does not prevent the user from having the same pages
> open multiple times within the same browser instance
> (eg when the user presses Ctrl-N after having logged in)
This is ok, because we're more concerned with an unmanned, logged-in
station. If they want 5 browser windows, they can go nuts.
Thanks for the dialog, Jeff! I think that clue you gave me above is
what I need. The confusion is again because AuthCookie and
Apache::Session both call themselves sessions. AuthCookie does the
authentication, Apache::Session holds the user data. So I need to write
the piece that coordinates the cleanup of old sessions between the two.
===
From: "Peter Bi" <mod_perl@att.net>
To: "Fran Fabrizio" <ffabrizio@mmrd.com>, "Jeff" <jaa.perl@aquabolt.com>
Cc: <modperl@perl.apache.org>
Subject: Re: Enforcing user logged in from only 1 browser?
Date: Mon, 15 Apr 2002 18:41:18 -0700
> If someone comes in with an old active session (assuming it's not
> expired) AuthCookie examines the MD5 hash and says "everything matches
> up, you may proceed". I need to add a step here to say "if newer
> session for user exists, kill this one". But you've just given me a
> great idea! When the old session comes in, there will still be data in
> the session (because the new session is using it - sessions are keyed by
> user id, not session id). So I can't rely on an empty session data to
> be the clue. BUT - I can store the session key that generated this
> session data in with the session data, and try to match those up. I
> like it. This will work, I think. Thank you. =) Why do I use the user
> id instead of the session id for the session key? Because it makes the
> code easier for other developers ("a user will always have a session
> with the key $r->connection->user") and that gets passed around
> automatically rather than me having to expose the developers to the
> AuthCookieDBI cookie directly. I just find it easier to rely on
> $r->connection->user to always be the session key.
If you touch SessionDBI for every request, why don't go directly to the
Basic Authentication ? Using AuthCookie would 1) slow down the
authentication process (because an extra MD5 hash calculation) and 2) drop
off 10% of users who have disabled the cookie.
One of the nice features in the AuthCookie, and ticketing authentication
systems in general, is that the ticket is self-consistent. So one can check
if the ticket is valid without calling any DBI (instead, it calculates a
MD5). Repeating SELECTs/UPDATEs to DBI does hurt much the performance of a
high traffic site.
===
Date: Tue, 16 Apr 2002 09:33:25 -0400
From: Fran Fabrizio <ffabrizio@mmrd.com>
To: Peter Bi <mod_perl@att.net>
Subject: Re: Enforcing user logged in from only 1 browser?
Peter Bi wrote:
> If you touch SessionDBI for every request, why don't go directly to the
> Basic Authentication ?
1. You can't use a custom log in page
2. You can't log out unless you close your browser
3. It's for use by our employees only. They are told to enable cookies. =)
===
From: "Peter Bi" <mod_perl@att.net>
To: "Fran Fabrizio" <ffabrizio@mmrd.com>
Cc: <modperl@perl.apache.org>
Subject: Re: Enforcing user logged in from only 1 browser?
Date: Tue, 16 Apr 2002 13:39:13 -0700
"Fran Fabrizio" <ffabrizio@mmrd.com> wrote:
> > Peter Bi wrote:
> > > If you touch SessionDBI for every request, why don't go directly to the
> > > Basic Authentication ?
> >
> > 1. You can't use a custom log in page
> > 2. You can't log out unless you close your browser
> > 3. It's for use by our employees only. They are told to enable cookies.
> =)
1) agreed. If a custom login page is needed, one has to look for other
solutions such as cookie access control.
2) that depends. First, for some reasons, Internet is designed without
"Logout". Many seldom logout from those services such as Yahoo mail, and me
too. For the specific question you posted (one login only for an account),
while it can be in principle designed and implemented, in practice, it may
not work that smoothly, because many users still don't run "Logout". Trust
me :-). So BA or cookie doesn't matter. Second, you can make a link to
close the window using javascript, just like a "Logout" button.
3) will be very interesting to hear about your successful implementation!
(BTW, if only the "existence" status of an account is needed to double
check, please consider a lock file (e.g. -e) under Apache::File that may be
much faster than to call SessionDBI)
===
Date: Tue, 16 Apr 2002 17:16:44 -0400
From: Fran Fabrizio <ffabrizio@mmrd.com>
To: Peter Bi <mod_perl@att.net>
Subject: Re: Enforcing user logged in from only 1 browser?
Peter,
> 2) that depends. First, for some reasons, Internet is designed without
> "Logout". Many seldom logout from those services such as Yahoo mail, and me
> too. For the specific question you posted (one login only for an account),
> while it can be in principle designed and implemented, in practice, it may
> not work that smoothly, because many users still don't run "Logout". Trust
> me :-). So BA or cookie doesn't matter. Second, you can make a link to
> close the window using javascript, just like a "Logout" button.
Well that's kind of why I'm here in the first place...looking for a real
solution to users who don't log out. I'd rather force logout their old
sessions rather than just resign to the fact that user's habits make
logouts unpredictable. It's not acceptable for our application's
purposes to just leave active sessions open if it's at all possible to
avoid.
> 3) will be very interesting to hear about your successful implementation!
I would have been done yesterday had I not made a bonehead mistake and
deleted a very important database table. I'm still recovering from that
mistake. :-( Hopefully, I'll be able to tell you all about the
successful implementation early next week.
> (BTW, if only the "existence" status of an account is needed to double
> check, please consider a lock file (e.g. -e) under Apache::File that may be
> much faster than to call SessionDBI)
I'm not too worried about performance...only about 100 users will ever
have an account on this system, a fraction of them actively using the
app at any one time. But I'm not using the DBI for this part anyhow.
I'm using Apache::Session::File, more or less (really using Flex but
only overridding the Generate method). The only time I hit the database
is to check that the user/password are valid and get user preferences
when they submit the login form. The lock file actually isn't a
workable solution, now that I think about it. Just knowing a user has
logged in and not logged out does me no good, I need to know if they
have other valid session keys still in existence so I can expire them.
A lock file could lead to false positives - if the key expired but they
haven't visited again or explicitly logged out since, it'll be there but
the user really doesn't have any active sessions.
I still think Jeff's post yesterday suggesting I store the AuthCookie
key in the Apache::Session data is the answer.
Thanks for the dialog, I'll post the final results.
===