Sessions in token based authentication
Solution 1
What I usually do is to keep the token in the local storage, this way I can persist the token even if the user leaves the site.
localStorage.setItem('app-token', theTokenFromServer);
Every time the user loads the page, the first thing I do is to look for the existence of the token.
token = localStorage.getItem('app-token');
If using react, I'd keep the token on the global state (using redux for example):
function loadAppToken(token) {
return {
type: 'LOAD_TOKEN',
payload: { token },
};
}
With vanilla javascript I'd keep it on my connection utility. Which might look something like the following:
const token = localStorage.getItem('app-token');
export function request(config) {
const { url, ...others } = config;
return fetch(url, {
...others,
credentials: 'include',
headers: {
'Authorization': `Bearer ${token}`
},
});
}
I'd still have a fetch utility in a react app, similar to the previous code, but I'd send the token in the options, by getting it in a redux middleware for every single request.
Solution 2
Let's assume you want to build an APP with.
- ReactJS
- REST API with PHP
- Using JWT
1. Introduction
You must forget about sessions when building REST API's.
REST API's are meant to be stateless, so they must not depend on sessions, they must process requests with only the data given by the client.
2. Authentication
All the client wants to do is only to exchange some username
& password
for a token.
This is an example HTTP request
POST /api/v1/authentication HTTP/1.1
Host: localhost
Content-Type: application/json
{
"username": "foo",
"password": "bar"
}
And the response is:
{
"token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
3. let's go into more details into request/response
How our API will process the authentication request?
It will check if a user with username
foo
& passwordbar
is founded and it's active in DBIt will generate a JWT (Json Web Token)
It will return response containing the JWT
This is some super simple auth method, just for example.
public function authAction()
{
/** Get your payload somehow */
$request = $_POST;
//Validate if username & password are given/
$user = $this->model->auth($username, $password);
if(!$user) {
//throw error for not valid credentials
}
$jwt = $this->jwt->create($user);
//return response with $jwt
}
As you see they are no sessions set or anything.
How our client side will process the response?
The client could use some package like superagent for handling the requests & responses to our API this way the process will be simplified to this:
let data = {
username: email,
password: password
};
request
.post('/api/v1/authentication')
.set('Content-Type', 'application/json')
.send(data)
.end(function (error, response) {
//response.body.token
});
4. Creating JWT on server side
You could use some 3RD PT package for generating and validating JWT instead of writing it yourself.
Look at this package, you can see how it's done.
And remember to always create strong signatures.
I recommend using RSA keys
I am not advertising or supporting this project, just found it useful to share it here. I had never used it, I'm using something similar to this on my NodeJS projects.
5. Saving JWT on client side
They are two ways as you already know localStorage
& cookies
For me I am using cookies, because:
- They are a bit more secure.
- Expire date can be set without implementing some custom logic.
- Older browser support (very old browsers, so it's not that important).
But it's all up to you.
6. Using JWT
From now on every request to server you must include your JWT.
In your REST API you must write a method to validate the JWT and exchanging it for user object.
Example request:
let jwt = ...; //GET IT FROM LOCALSTORAGE OR COOKIE
request
.get('/api/v1/posts')
.set('Content-Type', 'application/json')
.set('Authorization', jwt)
.end(function (error, response) {
});
How API will process this request
public function postsAction()
{
$jwt = $this->headers->get('Authorization');
if(!$this->jwt->validate($jwt)) {
//throw unauthorized error
}
$user = $this->model->exchangeJWT($jwt);
//Your logic here
}
7. Expire date & cookie
If you are using cookie to save your JWT, be careful with setting the expire dates.
The cookie expire date must be equal to the JWT expire date.
Solution 3
Currently working on same type of application using lumen for API. Following 3 steps for Token based Authentication in Lumen with JWT:
1. Create Token and return after login success
public function login(Request $request) {
$token = $this->jwt->attempt(['user_name' => $data['user_name'], 'password' => $data['password']]); //$token = $this->jwt->attempt($data);
if (!$token) {
$response = array('success' => false, 'data' => null, 'detail' => array('message' => Messages::MSG_INVALID_USER, 'error' => array(Messages::MSG_INVALID_USER)));
return response()->json($response);
} else {
$user = \Auth::setToken($token)->user();
$data = array('token' => $token,'user_id' => $user->id);
$response = array('success' => true, 'data' => $data, 'detail' => array('message' => Messages::MSG_SUCCESS, 'error' => null));
return response()->json($response);
}
}
2. Define middleware for token verification
public function handle($request, Closure $next, $guard = null) {
try {
$token = $request->header('X-TOKEN');
$user_id = $request->header('X-USER');
$user = \Auth::setToken($token)->user();
if ($user && $user->id == $user_id) {
return $next($request);
} else {
$response = array('success' => false, 'data' => null, 'detail' => array('message' => Messages::MSG_ERR_INVALID_TOKEN, 'error' => Messages::MSG_ERR_INVALID_TOKEN));
return response()->json($response);
}
} catch (Exception $ex) {
$response = array('success' => false, 'data' => null, 'detail' => array('message' => Messages::MSG_ERROR_500, 'error' => array($ex)));
return response()->json($response);
}
}
3. Store token in localstorage or in cookies
localStorage.setItem("Token", JSON.stringify(TokenData));
TokenData = JSON.parse(localStorage.getItem("Token"));
or
$.cookie('Token', JSON.stringify(TokenData), {expires: 1, path: '/'});
TokenData = JSON.parse($.cookie("Token"));
4. Send token with every request in headers
Request with custom headers
$.ajax({
url: 'foo/bar',
headers: { 'X-TOKEN': TokenData.Token ,'X-USER': TokenData.UserId}
});
Headers to every request
$.ajaxSetup({
headers: { 'X-TOKEN': TokenData.Token ,'X-USER': TokenData.UserId}
});
Hope it'll help.
Note: Add some checks and data validations while reading data from localstorage
or cookies
.
Solution 4
You can store it in the browser's localStorage, then set it in the header for each request to the server.
Solution 5
For encryption and decryption you can use in built laravel's Crypt Model
use Illuminate\Support\Facades\Crypt;
What we do for generating APIs token is will take array of required fields.
Let's create data
$data = [
'user_id' => $user->id,
'time_stemp' => \Carbon::now() // Carbon is laravel's time model(class) for managing times
'expire_on' => \Carbon::now()->addDays(2); //here i'm setting token expires time for 2 days you can change any
];
$data = serialize($data);
then encrypt your data with Crypt
$accessToken = Crypt::encrypt($data);
Now send to front end in response and save in local storage or cookie anything no need for time here will check on server only.
Now in every request pass that token and on server side create one middle ware that will parse your data and if your token time is less then expire time then move forward else send error 403 or anything you want.
How to parse data on server side
Create middleware using command : php artisan make:middleware ApiAuth then is handle part
//Accesstoken you passed in $headers or in $request param use whatever you like
$searilizerData = Crypt::decrypt($headers['AccessToken']);
$data = unserialize($searilizerData);
//check if expire_on is less then current server time
if($data['expire_on] <= \Curbon::now()){
next(); // let them contuine and access data
} else {
throw new Exception ("Your token has expired please regenerate your token",403);
}
Hope this will help :)
Related videos on Youtube
Comments
-
Flame of udun almost 2 years
I am building an app in PHP Lumen which returns a token upon login. I am not sure how to proceed beyond this.
How am I supposed to maintain a session using these tokens?
Specifically, how do I store the tokens on the client side if I am using reactjs or vanilla HTML/CSS/jQuery and send them in every request I make for the secure part of my web app?
-
Admin over 6 yearsIf Lumen uses cookies, then they're probably automatically set for you.
https://lumen.laravel.com/docs/5.4/authentication
https://lumen.laravel.com/docs/5.4/authorization
-
Peon over 6 yearsRead about Laravel Passport
-
-
Nick over 6 years+1 for simplicity. Also, in your backend you can set the expiry date of the JWT token. If the token is expired, the API will return unauthorized, and you should redirect the user to the login page.
-
narthur157 about 4 yearsLocal storage can be read by other scripts on your page and is generally considered insecure
-
mercury about 2 yearsIt’s highly not recommended to use localStorage for any sensitive data .