My question is a little hard to explain, but I'll try..
Basically, in the tank_auth example script there is this code to redirect the user if they are not already logged on;
if (!$this->tank_auth->is_logged_in()) {
redirect('/auth/login/');
} else {
$data['user_id'] = $this->tank_auth->get_user_id();
$data['username'] = $this->tank_auth->get_username();
$this->load->view('welcome', $data);
}
Which is great if you have a login page and the user starts at the beginning each time. (And I'm comfortable doing it that way)
But I want the user to be able to jump in to website at (almost) any controller and have a login bar come up across the top. When logging in, it should not redirect them to another page. They should end up on the same page they tried to visit.
For example, my user might load straight away example.com/food/burgers. I'd like a blank page to come up, but just with a login bar across the top. Then when they log in, they end up back on the 'burgers' page but this time there is also a list of burgers and the bar accross the top that tells them they are logged in, with the option to log-off.
So how do I do this? Do I need to call the auth/login method from every controller? Do I do it as an "include"? No idea.
Firstly you will want to create a base controller that all your controllers will extend from. You would check for authentication in this base controller. If they aren't logged in, then save the entry point uri in a cookie and redirect to the login page.
// application/core/My_Controller.php
class MY_Controller extends CI_Controller
{
public function __construct()
{
parent::__construct();
$this->load->library('session');
$this->load->model('tank_auth');
if (!$this->tank_auth->is_logged_in()) {
// save the visitors entry point and redirect to login
$this->session->set_userdata('redirect', $this->uri->uri_string());
redirect('auth/login');
}
}
}
Your main controllers will extend MY_Controller and don't need to worry about authentication.
class Welcome extends MY_Controller
{
public function index()
{
$data['user_id'] = $this->tank_auth->get_user_id();
$data['username'] = $this->tank_auth->get_username();
$this->load->view('welcome', $data);
}
}
Your authentication controller wouldn't extend MY_Controller otherwise it will get stuck in a redirect loop.
class Auth extends CI_Controller
{
public function login()
{
$this->load->library('session');
if (auth_success) {
// redirect the user back to the entry point or the welcome controller
if ($uri = $this->session->userdata('redirect')) {
redirect($uri);
} else {
redirect('welcome');
}
}
// handle authentication failure
}
}
Instead of using sessions to store the redirect uri, you could also pass it along as a GET parameter.
Related
My CI website has csrf protection.
$config['csrf_protection'] = TRUE;
So, when I resubmit form by refresh I am getting the following error.
The action you have requested is not allowed
Instead of showing this message, I want it to return to last page.
So, I try to override csrf_show_error() method by extending the CI_Security file.
This is my class located in application/core/My_Security.php
class MY_Security extends CI_Security {
public function __construct()
{
parent::__construct();
$this->load->library('user_agent');
}
public function csrf_show_error()
{
// show_error('The action you have requested is not allowed.'); // default code
// force page "refresh" - redirect back to itself
// a page refresh restores the CSRF cookie
if ($this->agent->is_referral())
{
redirect(site_url());
} else {
redirect($_SERVER['HTTP_REFERER']);
}
}
}
I am getting the following error
Call to a member function library() on a non-object
Insted of changing the core classes, I extended the MY_Securtiy class in core folder of application. and redirecting to past page.
File Location: application\core\MY_Security.php
class MY_Security extends CI_Security {
public function __construct()
{
parent::__construct();
}
public function csrf_show_error()
{
header('Location: ' . htmlspecialchars($_SERVER['REQUEST_URI']), TRUE, 200);
}
}
Thanks for your solution, but it seems better with a return code 302 by changing the request type of the new request to GET, regardless of the type employed in the original request (e.g. POST). The next refresh will not ask any question.
I am using the Default Controller to make the user authentication. What I am trying to do is whatever is the page the user request news/add or news/index or themes/all or maps/view, if he is not logged in, he or she will be directed to the log in page and then redirected to the page he wanted to go, not always the same page.
You can your the
CodeIgniter User Agent Library and Session Library to store and use the referring url. The user agent library is basicly accessing the $_SERVER['HTTP_REFERER'] value.
NOTE: from the php.net website:
Not all user agents will set this, and some provide the ability to modify HTTP_REFERER as a feature. In short, it cannot really be trusted.
so this is not a foolproof method.
if ($this->agent->is_referral()) {
$this->session->set_userdata('prev_url', $this->agent->referrer());
}
// later, when login is successful
$prev_url = $this->session->userdata('prev_url');
if( $prev_url ) {
redirect($prev_url);
}
one way is to do it in the constructor of your controller. that way they are redirected before going to the news/add etc.
so for example you create a model called "sentry" and a "getUser()" method to check the browser cookie to see if the user is authorized. if they are not authorized have it return false. if they are authorized have it return $user so then you have it available for your other methods.
function __construct() {
parent::__construct();
$this->load->model( 'sentry' );
if ( ! $this->user = $this->sentry->_getUser() )
{ redirect( '/login/', 'refresh' ); }
}
so then for example you could have $this->user->name etc etc available to any method in the controller. And $this->user will also automatically be available in all the view files of this controller.
I do this by extending my controller and I check in constructor if person is logged in or not, if person is logged in I save to the session current URL, and redirect person to the login page (if same constructor is applied (controller one) I make exception to not save current URL to the session) after logging in I call redirect function to the session variable.
How to extend your controller is done here http://philsturgeon.co.uk/blog/2010/02/CodeIgniter-Base-Classes-Keeping-it-DRY
note that when your controller is extended you use $this->data['variable_sent_to_view'] and you can omit second parameter of $this->load->view()
here is some example code assuming you know how your login controller works
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
class MY_Controller extends CI_Controller {
function __construct() {
parent::__construct();
$this->output->enable_profiler(FALSE);
if ($refer = $this->session->flashdata('refer')) {
$this->data['refer_page'] = $refer; // $this->data['refer_page'] is variable that you are interested in
unset($refer);
} else {
$this->data['refer_page'] = base_url(); //default refer_page
}
//check if user is NOT logged in
if (!$logged_in) {
$this->_setRefer(); //this is private function
}
// else dont care about it
}
private function _setRefer() {
$invalid_method = array('search', 'login'); // if method is 'search' or 'login' url will not save in session (it will stay same as was before)
$valid_refer = TRUE;
if (in_array($this->router->method, $invalid_method)) {
$valid_refer = FALSE;
}
if (!(count($_POST) > 0) && $valid_refer === TRUE && !$this->input->is_ajax_request()) {
$this->session->set_flashdata('refer', current_url());
} else {
$this->session->set_flashdata('refer', $this->data['refer_page']);
}
}
}
now in after succesful login redirect to $this->data['refer_page'], but note that login controller must by extended by MY_Controller.
this script also takes care about what happens if user made mistake and inserted wrong password (page will reload but "old" url stays)
I encounter problems when I call the method of a controller. By the way, this controller is routed.
Routes
$route['admin/company'] ='company';
Controller
class Company extends CI_controller {
public function __construct() {
parent::__construct();
session_start();
/** Check if user is logged in */
if ($this->session->userdata('user') != "") {
$this->load->model('my_model');
if ( $this->uri->segment(1) != "admin" ) {
redirect('admin/company/'.$this->uri->segment(2));
}
} else redirect('/');
}
public function index() { Some coding here............ }
public function addnew() { Some coding here...........}
public function process() { Some coding here...... }
}
When I call "localhost/company", it works fine and redirects me to "localhost/admin/company which is great. But, when I try to call the method of it, it displays a 404 error message.
Example: When I go to link: localhost/admin/company/addnew
Did lack something in routes? or in controller? or anything else?
Thanks,
James
If appropriate for all use cases, use a simple catch-all rule in routes.php:
$route['admin/company/(.+)$'] = "company/$1";
You will have to add a route for each function in your controller.
$route['admin/company/addNew'] ='company/addNew';
$route['admin/company/process'] ='company/process';
It's very annoying. Better, create a folder "admin" inside your "controllers" folder. Put the controller on the folder. Thus you can access your controller with the URL "localhost/admin/company" and all the methods without rerouting.
If it doesn't work at first, create a controller inside "admin" folder with the same name you'll find in your routes file (default_controller).
create a admin directory and add $route["company"]="admin/company"
I am trying to build a web application with codeigniter. I have installed Ion Auth as my authentication model.
The default Auth.php controller authenticates the user and sets up the session.
<?php defined('BASEPATH') OR exit('No direct script access allowed');
class Auth extends CI_Controller {
function __construct()
{
parent::__construct();
$this->load->library('ion_auth');
$this->load->library('session');
$this->load->library('form_validation');
$this->load->helper('url');
$data['title']="Login Page";
$this->load->view("view_site_header",$data);
// Load MongoDB library instead of native db driver if required
$this->config->item('use_mongodb', 'ion_auth') ?
$this->load->library('mongo_db') :
$this->load->database();
$this->form_validation->set_error_delimiters($this->config->item('error_start_delimiter', 'ion_auth'), $this->config->item('error_end_delimiter', 'ion_auth'));
}
//redirect if needed, otherwise display the user list
function index()
{
// if not logged in - go to home page
if (!$this->ion_auth->logged_in())
{
//redirect them to the login page
redirect('auth/login', 'refresh');
}
// if user is an admin go to this page
elseif ($this->ion_auth->is_admin())
{
// if an admin, go to admin area
//set the flash data error message if there is one
$this->data['message'] = (validation_errors()) ? validation_errors() : $this->session->flashdata('message');
//list the users
$this->data['users'] = $this->ion_auth->users()->result();
foreach ($this->data['users'] as $k => $user)
{
$this->data['users'][$k]->groups = $this->ion_auth->get_users_groups($user->id)->result();
}
$this->_render_page('auth/view_users', $this->data);
} else
{
//redirect them to the default home page
$data['title']="IMS Home Page";
$this->load->view("generic/view_site_header",$data);
$this->load->view("generic/view_generic_nav");
$this->load->view("generic/view_content_generic");
$this->load->view("view_site_footer");
}
}
what I want to do is create a new controller for my application logic and leave the auth controller for authentication.
How can I make use of the auth controller to ensure my user is logged in when accessing my new controller? in addition I need the ession information to be available to the new controller.
my new controller, master_data has the following code:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Masterdata extends CI_Controller{
function index ()
{
$data['title']="Master Data Home Page";
$this->load->view("master_data/view_master_data_header",$data);
$this->load->view("master_data/view_master_data_nav");
$this->load->view("master_data/view_content_master_data_home");
$this->load->view("master_data/view_master_data_footer");
echo $this->session->userdata('username');
}
}
obviously the echo $this->session->userdata('username'); does not work as the new controller has no knowledge of the auth controller session.
any help appreciated as always.
Kind Regards,
First autoload the ion_auth library.
If u simply want to check if the user is logged-in, just check it in every controller's constructor u load
public function __construct() {
parent::__construct();
if (!$this->ion_auth->logged_in()) {
// redirect to login view
}
}
If u happen to have multiple groups , u can create a new controller inside application/core/MY_controller.This controller will check whether user is logged in.You can extend this base controller to create new controller.A very good explanation on this is given by David john.Check this link .
obviously the echo $this->session->userdata('username'); does not work as the new controller has no knowledge of the auth controller session.
Eh...if the session library is loaded, then yes...the controller calling it will be able to access the session variable $username.
The way we handle this is to create a new controller parent class like MY_Controller in the application/core directory. This class loads common libraries/packages (like session and ion_auth). You could also autoload the libraries and helpers.
Since ion_auth stores all of the user profile data in a session var, all you need (on subsequent, non-authenticated) pages is the session lib to retrieve session data about the logged in user.
You really should check for their auth status though, and fail gracefully:
if (!$this->ion_auth->logged_in()) {
// echo a login link
} else {
// echo session var for username
}
Something like that...
jcorrys approach should work. An alternative approach (which will give your entire application a great deal more flexibility is to use a modular layout - https://bitbucket.org/wiredesignz/codeigniter-modular-extensions-hmvc
You will have to do a bit of fiddling to get it to play nicely with ion auth, but following the instructions in this question worked for me: Using Ion Auth as a separate module in the HMVC structure (have a look at the forks of ion auth on git hub - I think someone may have already done it for you)
This approach will allow you to access any method in any controller from anywhere in your application (even from a view if you need to) using this kind of syntax: modules::run('module/controller/method', $params);
This will essentially allow you to develop the existing ion auth controller into a user management controller which you can access from any other controllers you create (nice and dry).
I am fairly new and just started to use Codeigniter, and have come across some confusion regarding sessions.
What I want to achieve is, like in regular php, I want to check if a user is logged in by using a header include file which checks the session data. I dont want to check/write that checking code in every controller while passing data to the view file.
Can someone please show me how it can be done?
Ex. I don't want to do the following in every controller:
//Controller:
if($this->session->userdata('loggedin'){
$data['loggedin'] = $this->session->userdata('loggedin');
}
//I dont want to check the above on every function in every controller
$this->load->view('some_view_file', $data);
//some_view_file
if(isset($loggedin)){
echo "You are logged in!";
}
else
{
echo "Please log in!";
}
Instead, I want something to like the following:
//some view file
if(isset($loggedin))
{
echo "your logged in";
}
else
{
echo "please log in";
}
And also, how can I use native php sessions instead of CI Sessions. Any help will be much appreciated. Thanks.
Firstly, theres no reason you can't just write something like this in your view:
<? echo ($this->session->userdata('loggedin')) ? "Logged In": "Not Logged In"; ?>
Then your controllers don't need any of that code.
However if the check is more complex or something, then theres a few places you can do it.
1) In the constructor of a custom controller: create a new file application/core/MY_Controller.php, and override the constructor with something like:
class MY_Controller extends CI_Controller
{
public function __construct()
{
parent::__construct();
if($this->session->userdata("loggedin")) {
// do something
}
}
}
then make all your controllers extend MY_Controller.
2) Or in a post_controller_constructor hook. http://codeigniter.com/user_guide/general/hooks.html (this is more transparent, and probably easier if you have tons of controllers already)
You can use native sessions with this:
http://codeigniter.com/wiki/Native_session/
I think using a construct on your controller would be the smartest approach.
I also recommend encrypting your session cookie.
class Blog extends CI_Controller {
public function __construct()
{
parent::__construct();
//always check if session userdata value "logged_in" is not true
if(!$this->session->userdata("logged_in"))
{
redirect('login');
}
}
}