Authentication Poll & Community Design [PLEASE JOIN!]

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

Your authentication is...

Homegrown
61
91%
PEAR
4
6%
Non-PEAR but third-party
0
No votes
Borrowed from other software coexisting with app
2
3%
 
Total votes: 67

User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post by Ambush Commander »

Hahahaha! Code typo! Didn't run it through php -l or anything, and it's our first bug report. I meant $manager instead of $authentication_manager, and, to top things off, we were missing closing parenthesese.

Code: Select all

<?php

// no access control built in!
// returns whether or not a user is authenticated,
// and registers AuthenticationManager to appropriate persistence if they are.
function authtools_quick_authenticate(
    $data_adapter       = false, // false to use the automatic classes
    $session_adapter    = false,
    $credential_adapter = false)
{
    $manager = new AuthTools_AuthenticationManager();
   
    // give the credentials AuthenticationManager (nothing happens yet)
    if (!$credential_adapter) $credentials = new AuthTools_Credentials_Auto();
    $manager->setCredentials($credentials);
   
    // likewise we need some to check the credentials against (nothing happens)
    // the automatic detection is NOT recommended
    if (!$data_adapter) $data_adapter = new AuthTools_Datasource_AutoMySQL();
    $manager->setDataAdapter($data_adapter);
   
    // the user gets authenticated.
    //   authenticateUser optionally takes a specific authentication command,
    // otherwise, it passes the credentials to a controller which determines
    // which methods should the user be authenticated by
    $manager->authenticateUser();
   
    // propagate the authentication manager to the entire app. This gives the app a dependency,
    // but it should be trivial to write wrappers for the most important
    // methods: isUserAuthenticated() and getUserId()
    if (!$session_adapter) $session_adapter = new AuthTools_Session_Auto();
    $session_adapter->setAuthenticationManager($manager);
   
    // for small scripts that don't want to interface with the manager
    return $manager->isUserAuthenticated();
}

?>
And... to answer Nathaniel's questions...
could this be either the session cookie or the $_POST['username'] / $_POST['password']?
Yup. It's all neatly tucked away inside AuthTools_Credentials_Auto(). This is so in the case that it's actually in $_POST['login_username'], we can just let them pass an amended AuthTools_Credentials object. It'll be clearer with code for that class. (actually, we do have code for that class. It's in the TeX.)
this would be the datasource to check the session cookie or $_POST data against...?
Precisely. And you bring up an important point: the manager has to know about the session adapter too! I'll add that in later, it might be a pickle due to bidirectional references.
ok, I'm really lost. This isn't returned or added to the $manager object - what is it doing?
The authentication manager needs to be known by the entire application. So that's what the session_adapter does. Admittedly, it isn't the cleanest of ideas. It might not even belong there. Will reconsider.

Community coding works! Wow.
User avatar
Nathaniel
Forum Contributor
Posts: 396
Joined: Wed Aug 31, 2005 5:58 pm
Location: Arkansas, USA

Post by Nathaniel »

Ambush Commander wrote:Hahahaha! Code typo! Didn't run it through php -l or anything, and it's our first bug report. I meant $manager instead of $authentication_manager, and, to top things off, we were missing closing parenthesese.
Sweet. :)
Ambush Commander wrote:The authentication manager needs to be known by the entire application. So that's what the session_adapter does. Admittedly, it isn't the cleanest of ideas. It might not even belong there. Will reconsider.
Ok... that kinda makes sense. I do agree it could be cleaner, but I'm unsure how at the moment.
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post by Ambush Commander »

Ok... that kinda makes sense. I do agree it could be cleaner, but I'm unsure how at the moment.
Why... by adding a $global_adapter! (now that's going a little too far. We don't want too many adaptors). I'll think of something.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

Perhaps is should be:

Code: Select all

if (!$session_adapter) $session_adapter = new AuthTools_Session_Auto();
    $manager->setSessionAdapter($session_adapter);
My reasoning is that ultimatly you are going to want to inject the manager into a controller, so it would make more sense to just pass one object around.
(#10850)
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post by Ambush Commander »

My reasoning is that ultimatly you are going to want to inject the manager into a controller, so it would make more sense to just pass one object around.
The problem is if you do that, then the manager gets destroyed: nothing has a reference to it anymore. And since the authentication manager can be independent of a session (such as HTTP authorization), it's inappropriate to stash it in the session. This goes way back to our Propogate Authentication Manager discussion way back when. Actually, a $global_adapter wouldn't be a too bad idea.

The manager does need to know about sessions though. So we'd be adding that code in.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

Ambush Commander wrote:The problem is if you do that, then the manager gets destroyed: nothing has a reference to it anymore. And since the authentication manager can be independent of a session (such as HTTP authorization), it's inappropriate to stash it in the session. This goes way back to our Propogate Authentication Manager discussion way back when. Actually, a $global_adapter wouldn't be a too bad idea.

The manager does need to know about sessions though. So we'd be adding that code in.
I don't mind the manager being recreated each request. It does not to be saved in the session -- only the persistent data it uses. But is could be persisted as well I suppose. Since the session adapter comes from the app I think we should be dependent on it, that way if it is not working they can fix it. I think I would rather the session adapter deals with the magic internally and our stuff is just a plain old object that gets created. That way in cases where there is no persistence needed (such as HTTP authorization) the thing works identically (just without the setSessionAdapter() call) to the cases where it needs persistence.
(#10850)
santosj
Forum Contributor
Posts: 157
Joined: Sat Apr 29, 2006 7:06 pm

Post by santosj »

Well, I'm not sure that it would be such a good idea to store the Authentication at all. I suppose it wouldn't matter if the session was hijacked, if you cached the manager or not. The manager wouldn't have to use __wakeup() and __sleep() since it won't have any file handlers or Database connections. The class that do however would need to have those methods, unless the SPL Serializable class is used, but I forget if it is either PHP 5.x or 5.1.x. For backwards compatiablity, __wakeup() and __sleep(), I believe, are builtin to PHP 4 also (I will confirm this).

I would rather the manager be stored in a separate file from the session one and reference in the session. Not the path to the file, but the unique identifier to find the file. The cache system would also be good since then you wouldn't need to access the database each time the person logs in (which is nonsense since the user wouldn't sign in at every page request). However, it would also be neat if the coder uses HTTP Basic Authentication, it can be stored so that the person wouldn't have to sign in every time they have to access that page or similar pages with the same log in information.

Internally, this would not add bloat since the coder wouldn't use it or have to know it exists, it would just seem to work 'faster' and better for HTTP Basic since they wouldn't have to keep placing their username and password in.

However caching should be able to be enabled (disabled by default if implemented at all) by the developer or Adapter that needs it.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

santosj wrote:Well, I'm not sure that it would be such a good idea to store the Authentication at all.
I am not sure I understand this, but mainly because terminology for this stuff is so unspecific. Authenication, to me, is checking credentials provided by the user again a know datasource. The datasource obviously stores data, and the credentials are only stored for the current request. But that only describes the first step in Access Control -- which is Authentication.

After Authentication we then need to allow/deny access on a request by request basis. The application will define the requirements to allow access for a specific request. It will then check to see if the user meets those requirements. In most small to medium PHP applications with simple Access Control -- which is the bulk of them -- it probably makes sense to cache some or all of the pertinent user's data in the session. That data would be valid until the user logged-out or the sesson expired. In more complex systems the session adapter would fetch the data from a datasource each request, but that is really application dependent as this system does not really care where the data comes from. It sounds like we are going to implement some Best Practices code for common types of implementations.
(#10850)
santosj
Forum Contributor
Posts: 157
Joined: Sat Apr 29, 2006 7:06 pm

Post by santosj »

Well, yeah, I meant serializing the class and storing that, so you would access it like so.

Code: Select all

$_SESSION['authenticate'] = new AuthTools_Authenticate;
// ...
$_SESSION['authenticate']->method();
Or.

Code: Select all

$store_auth = serialize($manager);
$_SESSION['authenticate'] = $store_auth;
// ...
$manager = unserialize($_SESSION['authenticate']);
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

As object (like all vars) are automatically serialized by the session manager before being saved. I guess the reason you would want to serialize an object is so that you could manually include the class before unserializing it (but after starting the session). The autoload function can handle that -- unfortunately you can have only one autoload currently. If we wanted to have our own loader we could serialize I suppose. I think it is really an application decision though, so we should push the responsiblity out to the app. This also provides implementation flexiblity.
(#10850)
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post by Ambush Commander »

Sorry, I wasn't clear enough. If we decide to tuck away important info in the AuthenticationManager (as we did for the authentication status), the rest of the application needs to be able to access the manager during the entire request. Of course, we could assume that the application doesn't need that extra info if they're using authtools_quick_authorize(). Choices, choices.
I don't mind the manager being recreated each request.
I'll take it a step further: it should be recreated each request. This is because the session itself is an authentication factor, so if you bind it to the session, you've got something somewhat cyclical.

Plus, Santosj brings valid points about the classes the manager will have references to. No. Only store persistent data. If necessary, we can create an AuthTools_ManagerCache object.
I am not sure I understand this, but mainly because terminology for this stuff is so unspecific. Authenication, to me, is checking credentials provided by the user again a know datasource. The datasource obviously stores data, and the credentials are only stored for the current request.
Right. However, even though the session is created from a valid username/password, it doesn't know about the username/password. While the two are connected, they're also seperate bits of authentication. So the datasource to check credentials from a session from would be... the session itself (which is automatically initialized).
After Authentication we then need to allow/deny access on a request by request basis. The application will define the requirements to allow access for a specific request. It will then check to see if the user meets those requirements. In most small to medium PHP applications with simple Access Control -- which is the bulk of them -- it probably makes sense to cache some or all of the pertinent user's data in the session. That data would be valid until the user logged-out or the sesson expired. In more complex systems the session adapter would fetch the data from a datasource each request, but that is really application dependent as this system does not really care where the data comes from. It sounds like we are going to implement some Best Practices code for common types of implementations.
Personally, I think that grabbing the access information each request from the database is the cleanest way to go about doing things, if only because there's no easy way to invalidate data in a session except by just letting it expire. Suppose you had time-based authorization: if a session extends past the end point of the authorization, the session will still think the user is authorized, while in reality it isn't.
As object (like all vars) are automatically serialized by the session manager before being saved. I guess the reason you would want to serialize an object is so that you could manually include the class before unserializing it (but after starting the session). The autoload function can handle that -- unfortunately you can have only one autoload currently. If we wanted to have our own loader we could serialize I suppose. I think it is really an application decision though, so we should push the responsiblity out to the app. This also provides implementation flexiblity.
It's a bit of a pickle, I agree. There's a php.ini configuration that automatically initializes sessions: if that's the case, you plain flat out CANNOT put objects inside the session without __autoload(). Default setting, if we DO need a CachedAuthenticationManager, should be to serialize it and store it. The session adapter should make this transparent, so we're not actually ever directly interfacing with the superglobal $_SESSION.

This is what I meant:

Code: Select all

<?php

// no access control built in!
// returns whether or not a user is authenticated,
// and registers AuthenticationManager to appropriate persistence if they are.
function authtools_quick_authenticate(
    $data_adapter       = false, // false to use the automatic classes
    $session_adapter    = false,
    $credential_adapter = false,
    $global_adapter     = false)
{
    $manager = new AuthTools_AuthenticationManager();
    
    // give the credentials AuthenticationManager (nothing happens yet)
    if (!$credential_adapter) $credentials = new AuthTools_Credentials_Auto();
    $manager->setCredentials($credentials);
    
    // likewise we need some to check the credentials against (nothing happens)
    // the automatic detection is NOT recommended
    if (!$data_adapter) $data_adapter = new AuthTools_Datasource_AutoMySQL();
    $manager->setDataAdapter($data_adapter);
    
    // give the session to the authentication manager. Even though parts
    // of the session count as credentials, the manager also has write
    // access to the session, making it special.
    if (!$session_adapter) $session_adapter = new AuthTools_Session_Auto();
    $manager->setSessionAdapter($session_adapter);
    
    // the user gets authenticated.
    //   authenticateUser optionally takes a specific authentication command,
    // otherwise, it passes the credentials to a controller which determines
    // which methods should the user be authenticated by
    $manager->authenticateUser();
    
    // propagate the authentication manager. This gives the app a dependency,
    // but it should be trivial to write wrappers for the most important
    // methods: isUserAuthenticated() and getUserId()
    $global_adapter->registerAuthenticationManager($manager);
    
    // for small scripts that don't want to interface with the manager
    return $manager->isUserAuthenticated();
}

?>
No support for caching the authentication manager, because that's how I think it should be.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

Ambush Commander wrote:Right. However, even though the session is created from a valid username/password, it doesn't know about the username/password. While the two are connected, they're also seperate bits of authentication. So the datasource to check credentials from a session from would be... the session itself (which is automatically initialized).
I agree and I think we agree earlier that you would be able to limit/control/specify what gets copied from the datasource to the session. You can choose nothing but the minimum or whatever you need/want.
Ambush Commander wrote:Personally, I think that grabbing the access information each request from the database is the cleanest way to go about doing things, if only because there's no easy way to invalidate data in a session except by just letting it expire. Suppose you had time-based authorization: if a session extends past the end point of the authorization, the session will still think the user is authorized, while in reality it isn't.
I think grabbing access information each request is one implementation of the session/persistence adapter. I was suggesting that a simpler one would be to just stuff what you need in the session and use it until the session expires or the user logs-out and we destroy it. If you have special needs like time limited access the you can implement an adapter to do that -- but for your average PHP site the basic session based one with be simple and performant.
Ambush Commander wrote:It's a bit of a pickle, I agree. There's a php.ini configuration that automatically initializes sessions: if that's the case, you plain flat out CANNOT put objects inside the session without __autoload(). Default setting, if we DO need a CachedAuthenticationManager, should be to serialize it and store it. The session adapter should make this transparent, so we're not actually ever directly interfacing with the superglobal $_SESSION.
My proposal for pickles is to push them onto the application. If we later we find a good solution for some of them we can provide canned solutions.
(#10850)
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post by Ambush Commander »

Okay then. A bit of what we're discussing has to do with code we can't see from authtools_basic_auth. Based on the current code, the interfaces are:

Code: Select all

<?php

//// Interfaces!
// all in one location for browsing convenience... for now

// Some of them have _Interface in their name, and some of them don't.
// When a default/auto class is highly suggested to be used, the
// interface will be in the namespace AuthTools_Interface_*
// the end. This applies to AuthenticationManager. For others, like
// the data adapter, you should be making your own implementation, so
// the interface is really just the name itself: AuthTools_*

// Of course, that may be too complicated.

interface AuthTools_GlobalAdapter
{
    public function registerAuthenticationManager();
}

interface AuthTools_Interface_AuthenticationManager
{
    public function setCredentials();
    public function setDataAdapter();
    public function setSessionAdapter();
    
    public function authenticateUser($type = false);
    public function isUserAuthenticated($type = false);
}

// no interface defined yet!

interface AuthTools_DataAdapter {}
abstract  AuthTools_Credentials {} //the user may want to set them externally
interface AuthTools_SessionAdapter {}

// some concrete classes we defined, also no behaviors

class AuthTools_DataAdapter_AutoMySQL implements AuthTools_DataAdapter {}
class AuthTools_Credentials_Auto implements AuthTools_Credentials {}
class AuthTools_SessionAdapter_Auto implements AuthTools_SessionAdapter {}

?>
I think this will come a little bit clearer once we start fleshing out these classes.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

Good grief ... after all that ... it actually seems to be fairly clean, clear and straightforward! What do Nathaniel, Maugrim and others think?
(#10850)
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post by Ambush Commander »

There is a major problem, though.

Where does logout belong? I don't think that "authenticating" a user should log them out...

Here's what I think - the concept of "Sessions" is relevant even outside of the HTTP environment. For instance, for many encrypted protocols, you use slower assymetrical encryption to pass a common shared symmetrical encryption key that's valid for the session only. When you stop talking, the key gets destroyed.

The logical result of authentication() is that, in order to conserve resources that would otherwise be sent constantly sending the purest (and most valuable to hackers) credentials, username+password, we attempt to switch them to a session based authentication. If we can't, they'll have to send us the username+password each time.

The most important thing to note, however, is that the two are different.

What a logout does is it closes a session. Therefore, the code should reflect that.
Post Reply