CodeIgniter Forums

Full Version: Best way to handle authentication with MY_Controller ?
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2

El Forum

[eluser]benoa[/eluser]
I'm currently building a project that requires two layers of authentication : admin and clients.

So I went through many Open Source CI apps in order to see how authentication was managed. Looking at Bamboo Invoice's MY_Controller, I found a smart way to protect a list of controllers :

Code:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

class MY_Controller extends Controller
{

    function My_Controller()
    {
        parent::Controller();

        // a list of unlocked (ie: not password protected) controllers.  We assume
        // controllers are locked if they aren't explicitly on this list
        $unlocked = array('changelog', 'credits', 'donate', 'front', 'help', 'login');

        if ( ! $this->site_sentry->is_logged_in() AND ! in_array(strtolower(get_class($this)), $unlocked))
        {
            redirect('login/');
        }

    }

}

We could store the $unlocked array in a config file, or in a database so that the administrator can decide which parts of the sites are supposed to be restricted access (from the backend). I can see an implementation of roles here too.

I found this was useful, but I would like to know if you ci-mates have another way of managing access to your controllers ?

El Forum

[eluser]Damien K.[/eluser]
I'm leaning towards a hook implementation for authentication at the 'post_controller_constructor' point. I'm a strong believer in the least invasive method for extending a framework.

El Forum

[eluser]benoa[/eluser]
[quote author="Damien K." date="1253338852"]I'm a strong believer in the least invasive method for extending a framework.[/quote]

Me too Smile

Yeah Hooks. I definitely have to consider that solution. I'm just wondering if they are as fast as extending the Controller class in terms of performance... I gotta read the user guide's hook section now Smile

Anyways, if you use a hook, you'll still have to implement some way to restrict access to parts of your site... How would you do that? Like, lock controllers at the application start, or otherwise inside controllers, put ifs and elses? I'm not sure I want to do that... Because I'm DRY!

El Forum

[eluser]jedd[/eluser]
I put my checks in each controller's constructor, viz:

Code:
$this->_ensure_authenticated_user( "Forum" );

_ensure_authenticated_user() is a function in MY_Controller, of course. If you need to be logged in (authenticated) then it never comes back, and instead redirects to the login page.

On controllers where some methods are public and some are not - and at the moment only people/login & people/logout are such exceptions - I have the above code wrapped in a conditional that tests the subsequent url segment. Obviously for publicly visible controllers, I simply do not include that code at all.

El Forum

[eluser]n0xie[/eluser]
I do it the other way around. By definition, you need to be logged in for every controller. You can modify/overrule this behaviour in the constructor of a controller (or add an exception for certain methods). If we would do it your way, if someone 'forgets' to add the check, we'd be in a world of trouble.

El Forum

[eluser]jedd[/eluser]
I think it is so dependent on the nature of your development team, the nature of the project, the standards you have in place, the sensitivity of your data, and your security model (how good your defence in depth approach has been).

For example:
[quote author="n0xie" date="1253378616"]
... your way, if someone 'forgets' to add the check, we'd be in a world of trouble.
[/quote]

My way, someone, and we - are all the same person in that sentence. It's unlikely that I'd 'forget' to put a security check in my template (it's already there) for new controllers, and it's unlikely that I'd not test this as a non-authenticated user before releasing anything to public view too. And when I say 'unlikely', I mean 'it won't happen'.

In environments with many people contributing to the codebase, and some of those people being the easily confused type, then inverting the logic might make more sense. If you tend to release your software to the world and wait for people to point out that, as an unauthenticated person, they don't have access to something they should (and they know this despite not knowing the thing exists in the first place due to the security defaults) rather than testing visibility of each component before release, then, again, I can see how this approach provides greater comfort.

I suppose part of my problem, too, is working out how you can have an authentication check in MY_Controller, that sends you straight to la-la login land if you're not authenticated, going straight past GO and your destination controller, and yet .. and yet still somehow then allow access in some of your controller constructors (despite that code never being run).

El Forum

[eluser]n0xie[/eluser]
I don't know if you meant you didn't understand how we could override the default behaviour if a controller is protected by default but here is an example in its most basic form. Maybe someone can learn something from it.

Code:
class Frontend_Controller extends Controller {
    
    protected $public = FALSE;

    function Frontend_Controller()
    {
        parent::Controller();

        if($this->public === TRUE)
        {
            // open for public access
        }
        else
        {
            $this->load->library('auth');
            if ($this->auth->is_loggedin() === FALSE)
            {
                redirect(site_url('login'));
            }

        }
      }

class Somecontroller extends Frontend_Controller {

    // allow public access
        protected $public = TRUE;
    
    function Somecontroller()
    {
        parent::Frontend_Controller();
    }

This could easily be extended to have an 'allowed method' list using the same convention so you can allow some methods(pages) to be publicly available or add admin rights checks if you want to :
Code:
protected $public_methods = array();

The easy part is that if you don't specifically set public to TRUE, it defaults to FALSE, following the logic of white listing, instead of blacklisting basically disallowing everything by default unless specifically overruled.

El Forum

[eluser]Damien K.[/eluser]
[quote author="benoa" date="1253376612"]
I'm just wondering if they are as fast as extending the Controller class in terms of performance...

Anyways, if you use a hook, you'll still have to implement some way to restrict access to parts of your site... How would you do that? Like, lock controllers at the application start, or otherwise inside controllers, put ifs and elses? I'm not sure I want to do that... Because I'm DRY![/quote]

I'll leave it up to you re: performance. Performance is usually a lower priority on my checklist -- I don't have traffic like Google does.

The hook would call Auth->validate() after the contructor of each controller. Auth->validate() does what you would normally do:
1) Validate if is_logged_in
2) If not, redirect to login page; if yes, match uri rules
3) If you have roles, you match roles first then match the uri rules

That's the gist of it. You should not have to add code to each of your controllers manually. Generally speaking, n0xie's example above is exactly what I'm trying to avoid -- too invasive for my liking.

El Forum

[eluser]n0xie[/eluser]
[quote author="Damien K." date="1253399584"]You should not have to add code to each of your controllers manually. [/quote]
I understand your reasons for doing it with an hook. However the reason 'we' do it this way, is because if we did it with a hook, and a year from now someone else would look at the code, he would have absolutely no idea why or how a controller function is authenticated. I'm not saying your method is better or worse, I'm just saying if you work with a group of people, clearness of intent is one of the key factors to maintainable code.

It's much easier to look at a controller and see that some function is called in the construct (either blacklisting (jedd) or whitelisting (mine)) and figure out that that is the part that does all the work, then having to guess that 'somewhere', 'some' hook is called which automagically does stuff.

Although I have to admit it might be a 'cleaner' solution, I think that it's less 'readable'.

El Forum

[eluser]Damien K.[/eluser]
My apologies. When I said "You should not", I meant to say "My preference is to not". Every web application is different and decisions made are a result of many factors, including non-technical factors such as organization culture. I respect decisions made by others.

I have a preference for n-tier architecture and separation of concerns. Hooks is one way to achieve this, hence why I am leaning towards it as an implementation. It is less invasive and I like the flexibility to swap out my authentication system for a single-sign-on system in the future, or to handle it through other means such as .htaccess. Furthermore, I rather not have my controllers to determine whether it is public/private -- that is outside of their role for my applications.

I would have to say that I disagree with n-tier architecture (which is the case here) adding more maintenance nightmare or compromise readability. There is a trend heading towards Inversion of Control (IoC) (think Dependency Injection) and Aspect-Oriented Programming (AOP), which I am not an advocate of because if not done right can really make one go "huh".

I appreciate all the different approaches in solving any one problem. It is always a good read. There's no real right way or wrong way in solving a general problem in the software industry.
Pages: 1 2