
WordPress AJAX Login Without a Plugin – The Right Way
Published on January 8, 2013
When doing anything AJAX-based with WordPress you need to make sure you are doing it right. In this article you will come across several techniques that are considered to be the best way of doing AJAX, as described on the Codex and many other websites.
One thing to keep in mind is that WordPress has a tone of functions that you can use to make your job a lot easier. For painless user login feature these functions will be used in this article:
- wp_enqueue_script + wp_localize_script
- wp_nonce_field + check_ajax_referer
- is_user_logged_in + wp_signon
By following this article you will be able to create a nice login box that pops out on click and checks for user credentials.
Login concepts
First off, there's a couple of ways that you can implement login on your website. You could create a special Page called "Login" and display the form on an empty page with page-login.php template (kind of what you have by default when visiting /wp-admin). Second option is to keep the login form visible all the time above the header or in the sidebar. Third option is to call a login box when you click on a "Login" button/link. This is the option that's going to be described in this article but you can easily apply this technique on any Login concept you want.
HTML markup
Form has to be placed anywhere inside the <body> tag and for this article it is preferred you put it right at the beginning. The tag is usually found in header.php file of your theme.
header.php<body> <form id="login" action="login" method="post"> <h1>Site Login</h1> <p class="status"></p> <label for="username">Username</label> <input id="username" type="text" name="username"> <label for="password">Password</label> <input id="password" type="password" name="password"> <a class="lost" href="<?php echo wp_lostpassword_url(); ?>">Lost your password?</a> <input class="submit_button" type="submit" value="Login" name="submit"> <a class="close" href="">(close)</a> <?php wp_nonce_field( 'ajax-login-nonce', 'security' ); ?> </form> ...
At the end of the form there is a wp_nonce_field function which creates a hidden field with ID "security" and value "ajax-login-nonce" but in hashed form. If you refresh your page after applying the code above you will see an ugly HTML form on top. We also need a login button, so place that anywhere you want.
header.php<?php if (is_user_logged_in()) { ?> <a class="login_button" href="<?php echo wp_logout_url( home_url() ); ?>">Logout</a> <?php } else { ?> <a class="login_button" id="show_login" href="">Login</a> <?php } ?>
That's the HTML part, let's go ahead and style it so the form only shows up when user clicks the login link.
Styling the login box
This is the minimum required CSS that you can put inside your style.css. Use this as a starting point for your styling or if you want a fully styled form just click "expand code" in the bottom right.
style.cssform#login{ display: none; background-color: #FFFFFF; position: fixed; top: 200px; padding: 40px 25px 25px 25px; width: 350px; z-index: 999; left: 50%; margin-left: -200px; } form#login p.status{ display: none; } .login_overlay{ height: 100%; width: 100%; background-color: #F6F6F6; opacity: 0.9; position: fixed; z-index: 998; }
form#login{ display: none; background-color: #FFFFFF; border-radius: 8px; font-family: Arial, Helvetica, sans-serif; box-shadow: 0 0 6px rgba(0, 0, 0, 0.2); position: fixed; top: 200px; padding: 40px 25px 25px 25px; width: 350px; z-index: 999; left: 50%; margin-left: -200px; color: #878787; font-size: 11px; } form#login h1{ color: #333333; font-family: 'Georgia', 'Times New Roman', Times, serif; font-size: 27px; font-weight: 100; text-align: center; line-height: 1; margin: 0 0 30px 0; } form#login input#username, form#login input#password{ border: 1px solid #EDEDED; border-radius: 3px 3px 3px 3px; box-shadow: 0 0 3px rgba(0, 0, 0, 0.1) inset; color: #333333; font-size: 15px; padding: 10px 10px 10px 13px; width: 325px; margin: 7px 0 30px 0; background-color: #F9F9F9; font-family: 'Georgia', 'Times New Roman', Times, serif; } form#login input#username:focus, form#login input#password:focus{ background-color: #FFF; } form#login input.submit_button{ font-size: 13px; color: #FFF; border: 1px solid #b34336; background-color: #e25c4c; border-radius: 3px; text-shadow: 0 1px 0 #ba3f31; padding: 9px 31px 9px 31px; background: -moz-linear-gradient(top, #ea6656, #df5949); border-top: 1px solid #bb483a; border-bottom: 1px solid #a63b2e; float: right; box-shadow: 0 1px 0 #E87A6E inset; } form#login a{ text-decoration: none; } form#login a.close{ color: #DCDCDC; position: absolute; right: 15px; top: 15px; } form#login a.lost{ color: #B4B2B2; float: left; margin: 10px 0 0 0; } form#login p.status{ text-align: center; margin: -25px 0 20px 0; display: none; } a.login_button{ font-family: Arial, Helvetica, sans-serif; padding: 5px 7px 5px 7px; background-color: #FFF; border-radius: 3px; border: 1px solid #DCDCDC; color: #333; text-decoration: none; font-size: 11px; } .login_overlay{ height: 100%; width: 100%; background-color: #F6F6F6; opacity: 0.9; position: fixed; z-index: 998; }
Sending user info via AJAX
When the user fills out the form with his username and password we need to send that data to the server, see if the inserted data is correct and, based on the fact , notify the user that the login was successful and perform the login or just notify him that his data is incorrect.
All of the server-side checking code will be placed inside of theme's functions.php file. We'll create a function that will include our JS file, create a new JS object called ajax_login_object and enable the users with no privileges (not logged in) to call our function.
functions.phpfunction ajax_login_init(){ wp_register_script('ajax-login-script', get_template_directory_uri() . '/ajax-login-script.js', array('jquery') ); wp_enqueue_script('ajax-login-script'); wp_localize_script( 'ajax-login-script', 'ajax_login_object', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'redirecturl' => home_url(), 'loadingmessage' => __('Sending user info, please wait...') )); // Enable the user with no privileges to run ajax_login() in AJAX add_action( 'wp_ajax_nopriv_ajaxlogin', 'ajax_login' ); } // Execute the action only if the user isn't logged in if (!is_user_logged_in()) { add_action('init', 'ajax_login_init'); }
Notice the wp_ajax_nopriv_ajaxlogin hook, it's the most important part. If you leave out "nopriv" and use wp_ajax_ajaxlogin then only users that are logged in can access the function. We continue by making a function which collects the data received from POST method, checks if the nonce is valid and then tries to perform user login with the received data.
functions.phpfunction ajax_login(){ // First check the nonce, if it fails the function will break check_ajax_referer( 'ajax-login-nonce', 'security' ); // Nonce is checked, get the POST data and sign user on $info = array(); $info['user_login'] = $_POST['username']; $info['user_password'] = $_POST['password']; $info['remember'] = true; $user_signon = wp_signon( $info, false ); if ( is_wp_error($user_signon) ){ echo json_encode(array('loggedin'=>false, 'message'=>__('Wrong username or password.'))); } else { echo json_encode(array('loggedin'=>true, 'message'=>__('Login successful, redirecting...'))); } die(); }
All that's left is making the "login" button show our form and sending the form data with AJAX to the function above. Create a file called "ajax-login-script.js" and place it inside your theme's folder.
ajax-login-script.jsjQuery(document).ready(function($) { // Show the login dialog box on click $('a#show_login').on('click', function(e){ $('body').prepend('<div class="login_overlay"></div>'); $('form#login').fadeIn(500); $('div.login_overlay, form#login a.close').on('click', function(){ $('div.login_overlay').remove(); $('form#login').hide(); }); e.preventDefault(); }); // Perform AJAX login on form submit $('form#login').on('submit', function(e){ $('form#login p.status').show().text(ajax_login_object.loadingmessage); $.ajax({ type: 'POST', dataType: 'json', url: ajax_login_object.ajaxurl, data: { 'action': 'ajaxlogin', //calls wp_ajax_nopriv_ajaxlogin 'username': $('form#login #username').val(), 'password': $('form#login #password').val(), 'security': $('form#login #security').val() }, success: function(data){ $('form#login p.status').text(data.message); if (data.loggedin == true){ document.location.href = ajax_login_object.redirecturl; } } }); e.preventDefault(); }); });
That's it! You have a nice looking AJAX login form now. Article covered the best way to do AJAX in WordPress but you should also consider adding a frontend validation to the form.
Comments? Questions? Feedback is welcome!
Comments (79)
dameer on March 12, 2013
Nice and clear article! What if user wants to register?
Natko Hasić on March 12, 2013
For registration purposes you would need to add a couple of fields to the form, do the same AJAX procedure and pass the POST data to
wp_insert_user($user_data);
.Vladimir on March 20, 2013
Hello! Big work man, thanx!
But i ise ‘redirecturl’ => $_SERVER[‘REQUEST_URI’] <– best %)
ndeijk on March 20, 2013
awsome tutorial and 100% work !
Ksenya on April 10, 2013
Thank you! I tried ajax plugins and they didn’t work as I needed, with your article I did it in only like 10 mins
Matt on April 11, 2013
Hey thanks a lot for this very clear tutotial :) I implemented that on my website … works like a charm !
John on April 15, 2013
Works a treat. A really really big thank you for taking the time to put this together. I’ve been looking for something like this for ages. Really well done. ;-))
Benoit on April 17, 2013
Hi. That worked perfectly for me. Thank you.
One question thought. shouldn’t you escape POST values before enter wp_signon ?
Natko Hasić on April 17, 2013
Hey Benoit, this article does not provide any frontend or backend validation apart from what wordpress does by default but I guess you could escape POST or do some simple frontend validation by yourself.
Gurdeep Sembi on May 4, 2013
Excellent article! Just one thing I noticed, in the ajax_login_init function, the get_template_directory_uri function is used. I was using this with a child theme so had to change this to get_stylesheet_directory_uri() and this then returned the path to the child theme where I saved the js file. Thought it might be helpful to anyone who wanted to keep all customisations inside the child theme.
Once again thanks for the article, excellent work!
gky on May 9, 2013
Excellent job, thanks a lot…
chacha on May 14, 2013
Hi, why is the second parameter (secure cookie) set to false ? wp_signon( info, false );
Natko Hasić on May 14, 2013
It’s the default wp_signon value, if you leave it empty it will also be set to false. If you want another layer of security you can set it to true.
chacha on May 14, 2013
But why don’t you set it to true in your example?
Natko Hasić on May 14, 2013
Because this article is about AJAX login, not about security issues that can occur with the login process in general (AJAXed or not). Secure cookie is such a small part of the puzzle, if you want to have a secure login first thing you should think about is getting a https, not secure cookies.
JAZZPATHS on June 4, 2013
That’s really a great and useful tutorial! After Googled for a lot searching an explanation of how WP user login works I finally came across this beautiful post! Hope to see soon a post about Ajax WP user registration.
Jon on June 14, 2013
Fantastic article, would love to see a follow up showing registration and lost password as well.
Rudd on June 21, 2013
Awesome, I know a plugin that does this but this would be great feature to add for some commercial themes.
Viefx on June 23, 2013
Many thanks for this, that what i am looking for, will try to apply this stuff on my blog. Thanks you, great job :D
kamil on June 28, 2013
wow! no words to say how much this article helps with my project! Great job!
Andrew Condon on July 8, 2013
This was unbelievably helpful – thank you so much. (also to commenter Gurdeep for the child-theme fix).
Rob on July 9, 2013
Really nice tutorial. Thank you so much Natko! Just as I’m working on a WordPress membership site for the first I stumble upon this article… you couldn’t make up it! And thanks to Gurdeep for the child theme tip.
One question though, hopefully I’m not asking too much but is there a way of changing the status message to red or green when the login fails or completes successfully? Can I add a class to the fail/success message in the functions file?
Natko Hasić on July 9, 2013
You could check if “loggedin” if true or false, no need to pass the class from functions.php. See that
if (data.loggedin == true){
in the success function? Add your success class there, like$('form#login p.status').addClass('success');
and add an else statement which contains the “fail” class.Rob on July 9, 2013
Thanks again Natko – the code you sent me via email works flawlessly :) Today must be my lucky day! Like Jon and JAZZPATHS I too would love to see a follow up post for password and user registration with ajax.
Nick on August 6, 2013
Actually I double vote on Rob’s idea. Password and registration would be awesome follow up. I used your code on our website flawlessly.
Carlos on September 1, 2013
Works like a charm! Thank you!
Joacim on September 3, 2013
Thank you!
Mike Schinkel on March 21, 2015
Thank you. I’m so glad I didn’t have to debug through core to figure this out on my own!
David on March 31, 2015
@chacha @Natko Hasić If your website is (https) then you must set the wp_signon() second parameter (secure cookie) to true. If not you will still be able to log in, but will log you out (back to default login page) when trying to access the admin. Then you will need to log in again from the default login page. By setting the second parameter (secure cookie) to true will ensure you stay logged in no matter where you navigate to within an SSL(https) setup.
David on March 31, 2015
To follow up on my last comment. This is fully tested and future proofs it for any situation. Comes in handy when you are working on a multisite setup and some sites as https and other are not, but are sharing the same ajax login code.
Change line:
$user_signon = wp_signon($info, false);
to:
$user_signon = wp_signon($info, is_ssl() ? true : false);
David on March 31, 2015
Better yet:
$user_signon = wp_signon($info);
Then wp_signon() function will decide for you.
Amit on May 30, 2015
Hey,
1. It is possible to add login from facebook/+g/twitter?
2. Add term & conditions check link.
3. Add some more fields like name, surname in registration form?
Please suggest
Bhumi on June 20, 2015
Thank you author. It is awesome. Working like a magic :)
Louis on June 25, 2015
Hi Natko, why don’t you use wp_die() instead of die() ? (as described on the Codex)
Nithin Kumar on August 12, 2015
Perfectly articulated.. I just had to adjust CSS according to my need.. Thank you Natko Hasić
Kevin on September 18, 2015
I have a problem with this, when i logged in admin account it will redirect, but if i logged in normal user account it will stuck into sending information and when i reload the page it is already logged in
Dan on September 19, 2015
Hi, thanks for the script it was very helpful. I am having one issue where when I use my admin login on the modal login screen, it logs me in but doesn’t redirect so I have to manually refresh the screen. Ever heard of this issue?
Gustavo on January 22, 2016
Awesome tutorial, easy to understand a little code!
Gregory on February 8, 2016
… well done dude. Clean, efficient, and by the book. Keep it up.
Anahit on March 28, 2016
Hi.
Thanks you for the perfect tutorial, 100% working.
I have one question please.
How i can make the Lost Password functionality also with a popup and ajax in the place of redirecting to other page?
Also i wanna create the Edit account or Profile page also with ajax. Am using woocommerse. How i can do that please?
Carlos on June 2, 2016
Hi, this works great, but I’m having an issue. The popup form doesn’t disappear unless click on (close) link, and once I click on (close) it loads the page with the user logged in already but doesn’t redirect.
Any help?
Sajjad on June 24, 2016
Great & Helpful Tutorial. So easy to understand and use, It’s helpful for me. Thanks for sharing with us.
Chris on June 27, 2016
I have been using this method with Buddypress for a while and has been working wonderfully, however on a recent Buddypress update I am now getting a 404 on admin-ajax.php. I’ve tried all sorts of fixes and nothing seems to be working and I’m at a loss of what to do. Any ideas?
Steve on June 27, 2016
Hi, does this still work with WP 4.5.3 and modern themes? I tried this with Beaver Builder (child) theme, and my everything went blank. It does sound awesome, and I’d love it to work. Thanks.
Peter on June 28, 2016
Im using your code, login works fine.
But got this error “Uncaught SyntaxError: Unexpected token <".
How can i fix that?
Peter on June 28, 2016
Was my fault. :)
AJ on July 14, 2016
Thanks! This ajax login is working great!
AJ on July 17, 2016
Thanks! Vladimir’s comment is great to redirect the login and stay on the current page, but what about if we want to stay on the same page on logout?
Ron on July 20, 2016
Thank you so much! You saved my development time!
Thabo on July 22, 2016
Hi Natko,
Just wanted to say thank you for this clear and concise tutorial.
Works like a charm.
Cheers.
Joyce on August 5, 2016
Hi, the code really works like a charm, but I want to know how can I display a welcome message with the username and the logout link. Thank you!
Haitham Athamneh on October 21, 2016
Thank you very much :D
Ani on October 31, 2016
Wow ! Working great !
Naeem on January 11, 2017
Thank you author. It is awesome. Working like a magic :)
Vahid Mohammadi on February 1, 2017
Thank you sooo much. works like a charm!
Arun Verma on February 7, 2017
It works like charms.
Thank you so much post posting nice piece of code.
Please keep it up.
Francesco Marmiroli on March 15, 2017
Nice tutorial. Thanks you
Samuel Lerch on May 20, 2017
Hey Man!
I just implemented a login-form on my development-Website for a Fashion-Shop. On my Mac as well as on any other Laptop the Login-Thing works perfect. But neither iPhone nor iPad are Logging in .. its not even loading the redirect page correctly. Any idea why this can happen?
Thanks for your help and your nice code!
Samuel Lerch on May 20, 2017
Sorry to disturb: Everything works fine now! Had some typo in js. Everything perfect! :)
Paul on June 3, 2017
This article is exactly the thing I needed to flesh out the basics of the WordPress way of using ajax. Superb. Thankyou!
Adriano Monecchi on June 26, 2017
That’s a really nice approach! Thanks for the tip! I’ve manage to reproduce the login with Ajax successfully, but I also have woocommerce social login buttons placed within my form. When a user tries to sign in with his social account, every and any messages returned by WooCommerce are thrown as raw HTML. In order to display the social login responses (success or error messages), I’ve placed wc_print_notices(); within my form, but doing it that way messages are not returned in JSON format. Would there be a way to include WooCommerce error/success messages within the $user_signon logic?
Marcio Dias on July 17, 2017
Tks Natko!!
To redirect back to referring page after login:
in function ajax_login_init() add:
Global $wp;
and Replace
‘redirecturl’ => home_url(),
to:
‘redirecturl’ => home_url(add_query_arg(array(),$wp->request)),
Matt on August 4, 2017
Such an old post but still best tutorial I was able to find in this subject.
You might want to hide the original html input submit $(‘.submit_button’) but NOT with display:none directive as it would compromise the form submission capability, use some kind of masking or put it outside the viewport instead. Then use only the replacement element $(‘.login_button’) to trigger the form submission to avoid reloading the page.
Also it is better to use the jQuery .on to attach the handlers such as .click and .submit as it will work even if the form and button doesn’t exist in the DOM after the page load.
eg:
$(document).on(‘click’, ‘.submit_button’, function() {
$(‘form#login’).trigger(‘submit’);
});
$(document).on(‘submit’, ‘form#login’, function(e) {
e.preventDefault();
….
});
(Also need to tweak the redirection part if you don’t want the page reload at all, but I haven’t worked it our yet.)
Samuel Lerch on August 10, 2017
Hello! Hopefully someone can help me with that: I only get an error-message when I leave either the username or the password field empty. When I enter a wrong username or password the loadingmessage keeps standing as status. Any ideas why the loggedin == false is not being triggered?
I only have basic knowledge in PHP and ajax, but i can code, so any solutions are appreciated. :)
Thanks!
Hosein PRD on September 9, 2017
Hi brohter, thanks for this tutorial .
it’s work , but when i set two login form in my page for example first in header and second in footer , one of them not wordking ,
how can i fix this problem ?
thank u so much :*
Kristian K on October 6, 2017
I have had similar issues as you Samuel at two occations. The first occation was when I tried to limit access to the wordpress backend for all users except admins. This caused the loading message to hang, and nothing happening. I fixed the issue by removing the code that limited access to the backend.
When I migrated my site to bluehost the issue happened again. After researching the internet I found a solution. I have no idea why it works but it did the trick for me. Might be something about how bluehost handles login or something. What I did was to enable the SSL certificate and I added the following code in the header.php just below :
I have no idea what the code actually does, but it fixed the issue, so I stopped asking questions haha
Shaun Lippitt on October 11, 2017
Hi,
This works great!
I’m wondering if it’s possible to limit the number of logins?
Ivan on November 16, 2017
I am getting ‘0’ (((
Abdul Jabber on November 27, 2017
Thanks for sharing this helpful tutorial it really awesome
Rimpy on November 29, 2017
Very helpful, after searching and trying alot of things and this helped me just in 10 minutes. great work . keep up the good work (y)
Dnyanesh Mahajan on May 11, 2018
Thanks! Very Helpful
nidhi on June 9, 2018
thanks for this code its really helpful
Shabu james on July 8, 2018
Do you have same code for register ?
Ritchie on August 6, 2018
Well, even though this post is five years old, the code still does the trick. Adjusted it a little to take people to a portal page, and used @David’s addition to check SSL. Thanks very much!
MAHDI on May 25, 2019
ty
Jordan on June 24, 2019
Fantastic article, learnt a bunch from this, would of never gotten my custom login done correctly on my own if it wasn’t for this!
TienDung on October 8, 2019
Please update code to check user if user logged in redirect to wp-admin dashboard. Thanks
Rens on July 22, 2020
Used this in 2020! I updated the code to make use of the Rest API instead of admin-ajax. Works like a charm. Thanks a lot. :)