[JSONWEBTOKEN] PHP JWT & REST API Authentication Tutorial: Login and Signup full (ok)
Ví dụ đã hoàn thành :)
Inside your MySQL Database create a new Database called php_auth_api. And after that, use the following SQL code to create users table and the structure of this table –
SQL code for `users` Table
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`email` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`password` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
<?php
class Database {
// CHANGE THE DB INFO ACCORDING TO YOUR DATABASE
private $db_host = 'localhost';
private $db_name = 'php_auth_api';
private $db_username = 'root';
private $db_password = '';
public function dbConnection() {
try {
$conn = new PDO('mysql:host=' . $this->db_host . ';dbname=' . $this->db_name, $this->db_username, $this->db_password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $conn;
} catch (PDOException $e) {
echo "Connection error " . $e->getMessage();
exit;
}
}
}
C:\xampp\htdocs\abc\classes\JwtHandler.php
Chú ý: Những file sau đây được lấy từ jwt được lấy bằng "composer require firebase/php-jwt" JWT.php ExpiredException.php SignatureInvalidException.php BeforeValidException.php
Learn how to create a simple Login and Registration System using the React JS, PHP, and MySQL Database. And in this tutorial, instead of redux, we will use the context API.
The purpose of this tutorial is to give an idea of creating a Login and Registration system using React JS & PHP.
Screenshots of this React JS Login & Registration System
Login PageSign Up PageAfter Login (Home) Page
Follow the below to create this React JS & PHP Login System
React JS is used to building user interfaces, so we can’t handle the backend with this. To interact with the backend, we will use the PHP RESTful API.
After making the PHP RESTful API, set up your React CLI Environment & Install Axios package (to perform the ajax requests we will use the Axios JS).
👉 API URLs
👉 Creating files & folders
Now we will create our application files and folders. But, before going further let’s take a look at the 📁src folder structure. See the following image –Application src folder structure
MyContext.js
First, we will set up our app context. So inside the src folder create a new folder called 📁context, and here we have to create the MyContext.js file.
👉 Creating Components
After creating the context file, Now we will create our components. So, again inside the src folder create a new folder called 📁components. And here we will create our components.
In the components folder we have to create three components –
Login.js – Where a user can log in
Register.js – Where a user can Sign up
Home.js – A user can access this page after login.
App.js
After creating the components, now we have to edit the App.js file –
index.js
We don’t have to edit this file. Leave it as it is.
index.css
Here is the stylesheet of this application –
Now time to test it. To test this application, execute the npm start command in your terminal.
In this tutorial, you will learn how to create a simple Login and Registration RESTful API using PHP and MySQL Database.
Before going further, if you wish you can read my already made tutorial on how to create CRUD RESTful API. Maybe this tutorial is a bit useful for you here.
And in this tutorial, we will use the JWT to authenticate a user. So, I suggest you read this tutorial first – How to implement JWT with PHP.
MySQL Database Setup
Database Name – php_auth_api
Table Name – users
Inside your MySQL Database create a new Database called php_auth_api. And after that, use the following SQL code to create users table and the structure of this table –
Creating files & folders
First, Open your Xampp htdocs dir or you server www dir and create a new folder called php-login-registration-api. This is our application folder.
Inside the application folder, we will create the following files and folders –PHP Login Registration API Folder structure
Copy the src folder inside the php-login-registration-api folder.
Rename the src folder to jwt.
📁 classes Folder Setup
Now inside the application folder, you have to create a new folder called classes. After that, inside the classes folder, we will create two files or classes –
Database.php – For making the database connection
JwtHandler.php – For handling the JWT actions like encoding and decoding token
📁 middlewares Folder Setup
After creating the classes now again inside the application folder create a new folder called middlewares. And inside the middlewares folder, you just have to create one class –
Auth.php – For validating the token (checking if the token is valid or not)
Valid Token – return the User
Invalid Token – return null
Creating other files
Now time to create the base files –
register.php – For user registration.
login.php – For user login.
user-info.php – After login, this page is accessible with a valid token.
<?php
require __DIR__ . '/../jwt/JWT.php';
require __DIR__ . '/../jwt/ExpiredException.php';
require __DIR__ . '/../jwt/SignatureInvalidException.php';
require __DIR__ . '/../jwt/BeforeValidException.php';
use \Firebase\JWT\JWT;
class JwtHandler {
protected $jwt_secrect;
protected $token;
protected $issuedAt;
protected $expire;
protected $jwt;
public function __construct() {
// set your default time-zone
date_default_timezone_set('Asia/Kolkata');
$this->issuedAt = time();
// Token Validity (3600 second = 1hr)
$this->expire = $this->issuedAt + 3600;
// Set your secret or signature
$this->jwt_secrect = "this_is_my_secrect";
}
// ENCODING THE TOKEN
public function _jwt_encode_data($iss, $data) {
$this->token = array(
//Adding the identifier to the token (who issue the token)
"iss" => $iss,
"aud" => $iss,
// Adding the current timestamp to the token, for identifying that when the token was issued.
"iat" => $this->issuedAt,
// Token expiration
"exp" => $this->expire,
// Payload
"data" => $data,
);
$this->jwt = JWT::encode($this->token, $this->jwt_secrect);
return $this->jwt;
}
protected function _errMsg($msg) {
return [
"auth" => 0,
"message" => $msg,
];
}
//DECODING THE TOKEN
public function _jwt_decode_data($jwt_token) {
try {
$decode = JWT::decode($jwt_token, $this->jwt_secrect, array('HS256'));
return [
"auth" => 1,
"data" => $decode->data,
];
} catch (\Firebase\JWT\ExpiredException $e) {
return $this->_errMsg($e->getMessage());
} catch (\Firebase\JWT\SignatureInvalidException $e) {
return $this->_errMsg($e->getMessage());
} catch (\Firebase\JWT\BeforeValidException $e) {
return $this->_errMsg($e->getMessage());
} catch (\DomainException $e) {
return $this->_errMsg($e->getMessage());
} catch (\InvalidArgumentException $e) {
return $this->_errMsg($e->getMessage());
} catch (\UnexpectedValueException $e) {
return $this->_errMsg($e->getMessage());
}
}
}
<?php
require __DIR__ . '/../classes/JwtHandler.php';
class Auth extends JwtHandler {
protected $db;
protected $headers;
protected $token;
public function __construct($db, $headers) {
parent::__construct();
$this->db = $db;
$this->headers = $headers;
}
public function isAuth() {
if (array_key_exists('Authorization', $this->headers) && !empty(trim($this->headers['Authorization']))):
$this->token = explode(" ", trim($this->headers['Authorization']));
if (isset($this->token[1]) && !empty(trim($this->token[1]))):
$data = $this->_jwt_decode_data($this->token[1]);
if (isset($data['auth']) && isset($data['data']->user_id) && $data['auth']):
$user = $this->fetchUser($data['data']->user_id);
return $user;
else:
return null;
endif; // End of isset($this->token[1]) && !empty(trim($this->token[1]))
else:
return null;
endif; // End of isset($this->token[1]) && !empty(trim($this->token[1]))
else:
return null;
endif;
}
protected function fetchUser($user_id) {
try {
$fetch_user_by_id = "SELECT `name`,`email` FROM `users` WHERE `id`=:id";
$query_stmt = $this->db->prepare($fetch_user_by_id);
$query_stmt->bindValue(':id', $user_id, PDO::PARAM_INT);
$query_stmt->execute();
if ($query_stmt->rowCount()):
$row = $query_stmt->fetch(PDO::FETCH_ASSOC);
return [
'success' => 1,
'status' => 200,
'user' => $row,
];
else:
return null;
endif;
} catch (PDOException $e) {
return null;
}
}
}
<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: access");
header("Access-Control-Allow-Methods: POST");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
function msg($success, $status, $message, $extra = []) {
return array_merge([
'success' => $success,
'status' => $status,
'message' => $message,
], $extra);
}
require __DIR__ . '/classes/Database.php';
require __DIR__ . '/classes/JwtHandler.php';
$db_connection = new Database();
$conn = $db_connection->dbConnection();
$data = json_decode(file_get_contents("php://input"));
$returnData = [];
// IF REQUEST METHOD IS NOT EQUAL TO POST
if ($_SERVER["REQUEST_METHOD"] != "POST"):
$returnData = msg(0, 404, 'Page Not Found!');
// CHECKING EMPTY FIELDS
elseif (!isset($data->email) || !isset($data->password) || empty(trim($data->email)) || empty(trim($data->password))
):
$fields = ['fields' => ['email', 'password']];
$returnData = msg(0, 422, 'Please Fill in all Required Fields!', $fields);
// IF THERE ARE NO EMPTY FIELDS THEN-
else:
$email = trim($data->email);
$password = trim($data->password);
// CHECKING THE EMAIL FORMAT (IF INVALID FORMAT)
if (!filter_var($email, FILTER_VALIDATE_EMAIL)):
$returnData = msg(0, 422, 'Invalid Email Address!');
// IF PASSWORD IS LESS THAN 8 THE SHOW THE ERROR
elseif (strlen($password) < 8):
$returnData = msg(0, 422, 'Your password must be at least 8 characters long!');
// THE USER IS ABLE TO PERFORM THE LOGIN ACTION
else:
try {
$fetch_user_by_email = "SELECT * FROM `users` WHERE `email`=:email";
$query_stmt = $conn->prepare($fetch_user_by_email);
$query_stmt->bindValue(':email', $email, PDO::PARAM_STR);
$query_stmt->execute();
// IF THE USER IS FOUNDED BY EMAIL
if ($query_stmt->rowCount()):
$row = $query_stmt->fetch(PDO::FETCH_ASSOC);
$check_password = password_verify($password, $row['password']);
// VERIFYING THE PASSWORD (IS CORRECT OR NOT?)
// IF PASSWORD IS CORRECT THEN SEND THE LOGIN TOKEN
if ($check_password):
$jwt = new JwtHandler();
$token = $jwt->_jwt_encode_data(
'http://blog.com/',
array("user_id" => $row['id'])
);
$returnData = [
'success' => 1,
'message' => 'You have successfully logged in.',
'token' => $token,
];
// IF INVALID PASSWORD
else:
$returnData = msg(0, 422, 'Invalid Password!');
endif;
// IF THE USER IS NOT FOUNDED BY EMAIL THEN SHOW THE FOLLOWING ERROR
else:
$returnData = msg(0, 422, 'Invalid Email Address!');
endif;
} catch (PDOException $e) {
$returnData = msg(0, 500, $e->getMessage());
}
endif;
endif;
echo json_encode($returnData);
<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: access");
header("Access-Control-Allow-Methods: POST");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
function msg($success, $status, $message, $extra = []) {
return array_merge([
'success' => $success,
'status' => $status,
'message' => $message,
], $extra);
}
// INCLUDING DATABASE AND MAKING OBJECT
require __DIR__ . '/classes/Database.php';
$db_connection = new Database();
$conn = $db_connection->dbConnection();
// GET DATA FORM REQUEST
$data = json_decode(file_get_contents("php://input"));
$returnData = [];
// IF REQUEST METHOD IS NOT POST
if ($_SERVER["REQUEST_METHOD"] != "POST"):
$returnData = msg(0, 404, 'Page Not Found!');
// CHECKING EMPTY FIELDS
elseif (!isset($data->name) || !isset($data->email) || !isset($data->password) || empty(trim($data->name)) || empty(trim($data->email)) || empty(trim($data->password))
):
$fields = ['fields' => ['name', 'email', 'password']];
$returnData = msg(0, 422, 'Please Fill in all Required Fields!', $fields);
// IF THERE ARE NO EMPTY FIELDS THEN-
else:
$name = trim($data->name);
$email = trim($data->email);
$password = trim($data->password);
if (!filter_var($email, FILTER_VALIDATE_EMAIL)):
$returnData = msg(0, 422, 'Invalid Email Address!');
elseif (strlen($password) < 8):
$returnData = msg(0, 422, 'Your password must be at least 8 characters long!');
elseif (strlen($name) < 3):
$returnData = msg(0, 422, 'Your name must be at least 3 characters long!');
else:
try {
$check_email = "SELECT `email` FROM `users` WHERE `email`=:email";
$check_email_stmt = $conn->prepare($check_email);
$check_email_stmt->bindValue(':email', $email, PDO::PARAM_STR);
$check_email_stmt->execute();
if ($check_email_stmt->rowCount()):
$returnData = msg(0, 422, 'This E-mail already in use!');
else:
$insert_query = "INSERT INTO `users`(`name`,`email`,`password`) VALUES(:name,:email,:password)";
$insert_stmt = $conn->prepare($insert_query);
// DATA BINDING
$insert_stmt->bindValue(':name', htmlspecialchars(strip_tags($name)), PDO::PARAM_STR);
$insert_stmt->bindValue(':email', $email, PDO::PARAM_STR);
$insert_stmt->bindValue(':password', password_hash($password, PASSWORD_DEFAULT), PDO::PARAM_STR);
$insert_stmt->execute();
$returnData = msg(1, 201, 'You have successfully registered.');
endif;
} catch (PDOException $e) {
$returnData = msg(0, 500, $e->getMessage());
}
endif;
endif;
echo json_encode($returnData);
// To register a user
http://localhost/php-login-registration-api/register.php
// To log in a user
http://localhost/php-login-registration-api/login.php
// Get User Info by giving token
http://localhost/php-login-registration-api/user-info.php
MyContext.jsimport React, { createContext,Component } from "react";
import axios from 'axios'
export const MyContext = createContext();
// Define the base URL
const Axios = axios.create({
baseURL: 'http://localhost/php-login-registration-api/',
});
class MyContextProvider extends Component{
constructor(){
super();
this.isLoggedIn();
}
// Root State
state = {
showLogin:true,
isAuth:false,
theUser:null,
}
// Toggle between Login & Signup page
toggleNav = () => {
const showLogin = !this.state.showLogin;
this.setState({
...this.state,
showLogin
})
}
// On Click the Log out button
logoutUser = () => {
localStorage.removeItem('loginToken');
this.setState({
...this.state,
isAuth:false
})
}
registerUser = async (user) => {
// Sending the user registration request
const register = await Axios.post('register.php',{
name:user.name,
email:user.email,
password:user.password
});
return register.data;
}
loginUser = async (user) => {
// Sending the user Login request
const login = await Axios.post('login.php',{
email:user.email,
password:user.password
});
return login.data;
}
// Checking user logged in or not
isLoggedIn = async () => {
const loginToken = localStorage.getItem('loginToken');
// If inside the local-storage has the JWT token
if(loginToken){
//Adding JWT token to axios default header
Axios.defaults.headers.common['Authorization'] = 'bearer '+loginToken;
// Fetching the user information
const {data} = await Axios.get('user-info.php');
// If user information is successfully received
if(data.success && data.user){
this.setState({
...this.state,
isAuth:true,
theUser:data.user
});
}
}
}
render(){
const contextValue = {
rootState:this.state,
toggleNav:this.toggleNav,
isLoggedIn:this.isLoggedIn,
registerUser:this.registerUser,
loginUser:this.loginUser,
logoutUser:this.logoutUser
}
return(
<MyContext.Provider value={contextValue}>
{this.props.children}
</MyContext.Provider>
)
}
}
export default MyContextProvider;
Home.jsimport React, {useContext} from 'react'
import {MyContext} from '../contexts/MyContext'
// Importing the Login & Register Componet
import Login from './Login'
import Register from './Register'
function Home(){
const {rootState,logoutUser} = useContext(MyContext);
const {isAuth,theUser,showLogin} = rootState;
// If user Logged in
if(isAuth)
{
return(
<div className="userInfo">
<div className="_img"><span role="img" aria-label="User Image">👦</span></div>
<h1>{theUser.name}</h1>
<div className="_email"><span>{theUser.email}</span></div>
<button onClick={logoutUser}>Logout</button>
</div>
)
}
// Showing Login Or Register Page According to the condition
else if(showLogin){
return <Login/>;
}
else{
return <Register/>;
}
}
export default Home;
App.jsimport React from 'react';
// Importing the Context Provider & Home Component
import MyContextProvider from './contexts/MyContext';
import Home from './components/Home'
function App() {
return (
<MyContextProvider>
<Home/>
</MyContextProvider>
);
}
export default App;
index.jsimport React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
register.php<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: access");
header("Access-Control-Allow-Methods: POST");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
function msg($success,$status,$message,$extra = []){
return array_merge([
'success' => $success,
'status' => $status,
'message' => $message
],$extra);
}
// INCLUDING DATABASE AND MAKING OBJECT
require __DIR__.'/classes/Database.php';
$db_connection = new Database();
$conn = $db_connection->dbConnection();
// GET DATA FORM REQUEST
$data = json_decode(file_get_contents("php://input"));
$returnData = [];
// IF REQUEST METHOD IS NOT POST
if($_SERVER["REQUEST_METHOD"] != "POST"):
$returnData = msg(0,404,'Page Not Found!');
// CHECKING EMPTY FIELDS
elseif(!isset($data->name)
|| !isset($data->email)
|| !isset($data->password)
|| empty(trim($data->name))
|| empty(trim($data->email))
|| empty(trim($data->password))
):
$fields = ['fields' => ['name','email','password']];
$returnData = msg(0,422,'Please Fill in all Required Fields!',$fields);
// IF THERE ARE NO EMPTY FIELDS THEN-
else:
$name = trim($data->name);
$email = trim($data->email);
$password = trim($data->password);
if(!filter_var($email, FILTER_VALIDATE_EMAIL)):
$returnData = msg(0,422,'Invalid Email Address!');
elseif(strlen($password) < 8):
$returnData = msg(0,422,'Your password must be at least 8 characters long!');
elseif(strlen($name) < 3):
$returnData = msg(0,422,'Your name must be at least 3 characters long!');
else:
try{
$check_email = "SELECT `email` FROM `users` WHERE `email`=:email";
$check_email_stmt = $conn->prepare($check_email);
$check_email_stmt->bindValue(':email', $email,PDO::PARAM_STR);
$check_email_stmt->execute();
if($check_email_stmt->rowCount()):
$returnData = msg(0,422, 'This E-mail already in use!');
else:
$insert_query = "INSERT INTO `users`(`name`,`email`,`password`) VALUES(:name,:email,:password)";
$insert_stmt = $conn->prepare($insert_query);
// DATA BINDING
$insert_stmt->bindValue(':name', htmlspecialchars(strip_tags($name)),PDO::PARAM_STR);
$insert_stmt->bindValue(':email', $email,PDO::PARAM_STR);
$insert_stmt->bindValue(':password', password_hash($password, PASSWORD_DEFAULT),PDO::PARAM_STR);
$insert_stmt->execute();
$returnData = msg(1,201,'You have successfully registered.');
endif;
}
catch(PDOException $e){
$returnData = msg(0,500,$e->getMessage());
}
endif;
endif;
echo json_encode($returnData);
login.php<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: access");
header("Access-Control-Allow-Methods: POST");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
function msg($success,$status,$message,$extra = []){
return array_merge([
'success' => $success,
'status' => $status,
'message' => $message
],$extra);
}
require __DIR__.'/classes/Database.php';
require __DIR__.'/classes/JwtHandler.php';
$db_connection = new Database();
$conn = $db_connection->dbConnection();
$data = json_decode(file_get_contents("php://input"));
$returnData = [];
// IF REQUEST METHOD IS NOT EQUAL TO POST
if($_SERVER["REQUEST_METHOD"] != "POST"):
$returnData = msg(0,404,'Page Not Found!');
// CHECKING EMPTY FIELDS
elseif(!isset($data->email)
|| !isset($data->password)
|| empty(trim($data->email))
|| empty(trim($data->password))
):
$fields = ['fields' => ['email','password']];
$returnData = msg(0,422,'Please Fill in all Required Fields!',$fields);
// IF THERE ARE NO EMPTY FIELDS THEN-
else:
$email = trim($data->email);
$password = trim($data->password);
// CHECKING THE EMAIL FORMAT (IF INVALID FORMAT)
if(!filter_var($email, FILTER_VALIDATE_EMAIL)):
$returnData = msg(0,422,'Invalid Email Address!');
// IF PASSWORD IS LESS THAN 8 THE SHOW THE ERROR
elseif(strlen($password) < 8):
$returnData = msg(0,422,'Your password must be at least 8 characters long!');
// THE USER IS ABLE TO PERFORM THE LOGIN ACTION
else:
try{
$fetch_user_by_email = "SELECT * FROM `users` WHERE `email`=:email";
$query_stmt = $conn->prepare($fetch_user_by_email);
$query_stmt->bindValue(':email', $email,PDO::PARAM_STR);
$query_stmt->execute();
// IF THE USER IS FOUNDED BY EMAIL
if($query_stmt->rowCount()):
$row = $query_stmt->fetch(PDO::FETCH_ASSOC);
$check_password = password_verify($password, $row['password']);
// VERIFYING THE PASSWORD (IS CORRECT OR NOT?)
// IF PASSWORD IS CORRECT THEN SEND THE LOGIN TOKEN
if($check_password):
$jwt = new JwtHandler();
$token = $jwt->_jwt_encode_data(
'http://localhost/php_auth_api/',
array("user_id"=> $row['id'])
);
$returnData = [
'success' => 1,
'message' => 'You have successfully logged in.',
'token' => $token
];
// IF INVALID PASSWORD
else:
$returnData = msg(0,422,'Invalid Password!');
endif;
// IF THE USER IS NOT FOUNDED BY EMAIL THEN SHOW THE FOLLOWING ERROR
else:
$returnData = msg(0,422,'Invalid Email Address!');
endif;
}
catch(PDOException $e){
$returnData = msg(0,500,$e->getMessage());
}
endif;
endif;
echo json_encode($returnData);
// Register
http://localhost/php-login-registration-api/register.php
// Login
http://localhost/php-login-registration-api/login.php
// Get User Info by giving token
http://localhost/php-login-registration-api/user-info.php