I'm trying to set up JWT in my project (NodeJs Express for the backend, and Javascript in frontend).
So in my backend, I send my token like this when the user logs in :
[...]
return res.send({ token, id: userId })
[...]
And in my frontend, I set the Bearer token in my header like this :
const data = {
email,
password
}
await instance.post('/signin', data)
.then((res) => {
instance.defaults.headers.common['authorization'] = `Bearer ${res.data.token}`
window.location = './account.html'
})
.catch((err) => console.log(err))
My questions are :
Did I do it correctly ?
When I'm redirected to the account.html page, how do I retrieve the token that was set while the log in was made ?
When you assign to instance.defaults.headers.common['authorization'] then subsequent requests made via Ajax using whatever library (I'm guessing Axios) that is from that page will include the authorization header.
Assigning a new value to window.location will trigger navigation and discard the instance object (with the assigned header value).
(As a rule of thumb, if you are going to assign a new value to window.location after making an Ajax request then you should probably replace the Ajax request with a regular form submission).
You could assign the data to somewhere where you can read it in the account.html page (such as local storage).
That won't be available to the server for the request for account.html itself though.
You could assign the data to a cookie. Then it would be available for the request for account.html, but in a cookie and not an authorisation header.
Note that using JWTs in authorization headers is something generally done in Single Page Applications and not traditional multi-page websites.
Related
I'm building a web app with CodeIgniter 4 where I implemented a REST API for register and login and a profile page just a test my login.
I have a login form that sends a javascript fetch post request to my api and I receives a jwt token. This is working.
Now I am in the situation where I think I did not understand the principle.
I want that the user stays logged in and doesn't need to re-login every time. The token expires after 12h.
And I want to use php (if possible) as the entire app runs on php.
Currently, I have a little javascript function to store my token:
const store = {};
store.setJWT = (data) => {
this.JWT = data;
};
But this is not secure against page reload.
Additionally I am creating a cookie with php, when the user logs in:
helper('cookie');
set_cookie([
'name' => 'login',
'value' => $token,
'expire' => $exp,
'httponly' => true,
'secure' => true,
]);
I am able to fetch data from the API using the cookie or the store object.
const token = getCookie('login');
const res = await fetch('/profile', {
headers: {
'Authorization': `Bearer ${token}` // or store.JWT
}
});
So.... what I want is:
The user goes to a protected url e.g. https://myapp.com/profile and if he is logged in, he has access. If not, he gets redirect to the login page.
Is using the cookie to store the jwt a good idea? Or did I completely misunderstood the idea of JWT and it is not used to be used for a login?
Additionally: I still don't know if biulding the login as an API was the best idea.
First of all there is nothing wrong with building "login with API". It is common practice. And JWT is perfectly suited for auth.
You sure can store JWT token inside a cookie, but it is a little bit wrong in my opinion. Usually JWT tokens are stored in the local storage on the client side. It will persist after page reload.
Set token in the local storage:
localStorage.setItem('token', token);
Get token from the local storage:
token = localStorage.getItem('token');
To better understand the conception of the JWT you can copy some token (without Bearer) and paste it in jwt.io. Basically JWT contain all the required information about the user and server can trust this information.
And when you set the cookie like this
set_cookie([
'name' => 'login',
'value' => $token,
'expire' => $exp,
'httponly' => true,
'secure' => true,
]);
It is an overkill. JWT possible already contains all this information and you can extract it from the token.
I am calling a get api that gets an array of mail data. It works fine on postman. When I use asyncdata method to get the array. It only works once if user refreshes the page I get 401 error. I pull token from cookies just fine. Normally on non asyncData I do this to set up the header
this.$axios.setHeader('Authorization','Bearer ' + this.$store.state.token);
this.$axios.$post('upload/avatar',formData,{
headers: {'content-type': 'multipart/form-data'}
}).then(res =>{
}).catch(err => console.error(err));{
}
}
This works fine and has no issues
but my asnycData is like this
asyncData(context){
//Cookie has to be read for async to work for now if user disables cookies breaks this page
let token = Cookie.get('token');
context.app.$axios.setHeader('Authorization',`Bearer ${token}`);
return context.app.$axios.$get('get/all/mail').then(mailData =>{
console.log(context.app.$axios.defaults);
let mailMap = [];
//create array to load mail data in
for(let key in mailData){
mailMap.push({...mailData[key]});
}
return{
mailArray:mailMap
}
}).catch(e =>console.error(e));
}
I am trying to make a simple inbox page that can send , delete , and draft messages.
The problem is probably due to the fact that since asyncData is running from the server, it'll lose any browser cookies.
If you're using axios, the nuxt community has setup a middleware module that can be used to automatically inject browser cookies into server requests.
I barely started reading about JWT and I beliave I understand what a JWT token is. I am also fairly familiar with SESSIONS. And I believe I understand the pros of each as well as their cons. However, there are a couple of parts where I am confused.
When requesting a protected resource, you need to send the jwt on each request, as opposed to having a session stored on the server. But:
1) how do you store your JWT token and where. From what I read I understood that you send your request to authenticate to the server and the server sends you a JWT token if you are successfully authenticated. Then what do you do?, do you store the JWT in a cookie as I have read in some sites? If so, how do you do it (using php, using javascript). And how do you read it.
2) When using session, more or less you just check that there is a session to check the user is logged in. How do you accomplish this when using JWT.
Also I have seen this on some pages:
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
How is this related to this (if related at all)
From client side, the good practice is store JWT in cookie, with mode http_only=true, is_secure (so that only send through https), so that JWT is not accessible by javascript. Then, we don't worry about XSS attach.
We dont need to store the session on server side. A JWT contains two parts, the payload data, and signature, signed by a secret key stored on server side, and only the server could know. When we receive the token from client, we check the payload data is valid or not (user information, who assigned that token, assigned that token to whom, which roles granted with the token, expired time), and we check the signature to make sure that the token is assigned by the server, not faked. Then the user will be authenticated.
It's like a passport the government give to its citizen, the data (payload) is readable for everybody, but the signature can only created by the government, and it can verify against that.
JWT is mostly a way to authenticate a user on rest APIs as, like you said, you send it to the client, and it negates the need to store it in a session.
however, if you are making a browser application, i don't see the need to use JWT authentication, as you can use the session and cookies to do so.
JWT is mainly for those cases, when you have a, say, mobile application frontend, where maintaining a session is not suggested, and maybe impossible.
however, if you are making a hybrid application, then storing it in local storage of the "browser" is the way to go. the JWT is NEVER stored server side.
In my case I use use Illuminate\Foundation\Auth\AuthenticatesUsers;, use JWTAuth; and use Tymon\JWTAuth\Exceptions\JWTException; in my LoginController. Then I need to get my JWTToken like that
use AuthenticatesUsers;
try {
$token = JWTAuth::attempt($request->only('username', 'password'), [
'exp' => Carbon::now()->addWeek()->timestamp,
]);
} catch (JWTException $e) {
return response()->json([
'error' => 'Could not authenticate: ' . $e.message,
], 500);
}
Of cause I do somthing more, if I don't get a token. In my case I go like that:
if (!$token) {
return response()->json([
'error' => 'Could not authenticate.'
], 401);
} else {
$data = [];
$meta = [];
//all what i need from users table if auth
$data['id'] = $request->user()->id;
$data['email'] = $request->user()->email;
$meta['token'] = $token;
//now comes the part, where I set my sessions:
Session::put('auth-user', (array)$request->user());
Session::put('jwt-token', $token);
Session::save();
return response()->json([
'data' => $data,
'meta' => $meta
])->withCookie('jwt-token', $token, config('jwt.ttl'), '/', null, true, true);
}
This is actually that what I do in LoginController I also have a middleware where I have a function to handle stuff like behavior after refresh page an so on.
'my.auth' => \App\Http\Middleware\MyAuth::class
I also handle a lot in javaScript's localStorage and sessionStorage in vuex store. To define routes in web.php or api.php I'll use now the class what I defined in
middleware before Route::group(['middleware' => 'my.auth'], function(){...}
Some resources that helped me out:
LaravelCookies, LaravelSession
Hope you get a bit inspiration to create your JWT auth.
I need to save jwt-token in local-storage through nodejs after the user has logged in (has been authorized ).
After I check if the user/password is correct in my controller - I save generated token in local storage. As it stands I can't reference window as it doesn't exists.
ReferenceError: window is not defined
This is how I'm currently trying to do it.
...
payload = {
sub: user.email,
role: user.role
};
token = jwt.sign(payload, jwtSecretKey, {expiresIn: '60m'});
window.localStorage.setItem('token', token);
res.status(200).render('index' , {
user: user,
token: token
});
...
You cannot save to localStorage on Node.js, but you can do it on a browser, possibly after sending from a server, for your case from a server on Node.js.
You should send the token from the server (running on Node.js) to the client (browser) which has a window object, having the localStorage and related getItem and setItem methods, you can reference from your JavaScript code for client (browser). Node.js does not have any window to reference. So, by referencing it in Node.js code is a ReferenceError hinting program code is referring to a thing that is not defined, undefined, you have encountered.
Just put it into a cookie and send, or send it via a json response. Then on the client browser save it into window.localStorage.
following is the examples code for the latter way; sending via a response:
// SERVER-SIDE Code
// say `app` is your app server on node.js
// this is a url handler on your server-side code
// you just have sent your user credentials from a browser
// typically via a form in the body of your http request
app.post('/auth', function (req, res) {
// you may have here some jwt token creation things
// ...
// and send it as your response to the client (probably a web browser) ..
// with your prefrred name as the key, say 'my_token', ..
// where you will save it to localStorage (ie. window.localStorage)
res.json({my_token: 'asdfgh-anything-jw-token-qwerty'})
})
// CLIENT-SIDE Code (may be a web browser)
// You have just sent a request to the server..
// ..with user credentials for authentication in the request body
// in the request body may be a window.FormData object or a json etc.
http.post('auth', userCredentials)
// probably the request mechanism you make http..
// ..requests asynchronously, maybe using a library,..
// ..will return a Promise, and you will have a similar code below.
.then(response => response.json())
.then(responseJson => {
// set localStorage with your preferred name,..
// ..say 'my_token', and the value sent by server
window.localStorage.setItem('my_token', responseJson.my_token)
// you may also want to redirect after you have saved localStorage:
// window.location.assign("http://www.example.org")
// you may even want to return responseJson or something else or assign it to some variable
// return responseJson;
})
If you mean html 5 localStorage, there's no such a thing since node.js is a server-side technology. Html 5 localStorage is a client side feature supported
refer How to access localStorage in node.js?
In the client call to /login use xmlhttpresponse object, then add an event listener for 'load'. This will give the client the responseObject where you have added the token. Then in the event listener put your localStorage code
I have a javascript variable that I want to pass back to the server side, which I thereafter intend to use it as an access token to grant user access to other pages which requires this token.
I wonder how do I pass this javascript variable back to server, so I can set it to a session variable? Do I need to send it back using ajax?
this is the part of jQuery I use to retrieve the token from server
$(document).ready(function () {
$('#loginForm').submit(function(e) {
var blargh = $(this).find('input').serialize();
$.ajax({
type: 'post',
url: '/WebAPI/api/authenticate/login',
data: blargh,
success: function (data) {
$.each(data, function(index, token) {
$('#container').prepend('<input type="hidden" name="MY_HIDDEN_FIELD_NAME" id="MY_HIDDEN_FIELD_NAME" value="'+token+'">');
});
},
error: function(jqXHR, status, errorThrown) {
alert("Error " + status + "\nError Thrown" + errorThrown )
},
});
e.preventDefault();
});
});
To pass back an additional item in the AJAX POST, you could add it like this...
var blargh = $(this).find('input').serialize();
blargh.someItem = "value";
Bear in mind that this only works when the form is submitted using AJAX, so not where JavaScript isn't available or is disabled.
All the normal security disclaimers apply!
I would recommend sending you the acess token in request headers when u are sending a ajax request
xhr.setRequestHeader('custom-header', 'value');
and on the server side you can fetch the request header
Couldn't you pass it back as either a hidden form element or pass it back in the query string of a ajax postback?
Example of a hook to get the post back value in global.asmx
protected void Session_Start(object src, EventArgs e)
{
if(!string.IsNullOrEmpty(Request.Form["MY_HIDDEN_FIELD_NAME"]))
{
Session["MY_SESSION_NAME"] = Request.Form["MY_HIDDEN_FIELD_NAME"]
}
}
First - why is your client generating the token (I hope I've understood you correctly there)? The server should generate the token and the client must then be responsible for maintaining it.
If it's an API token that'll only ever be used in the browser from javascript, then I recommend using an authentication cookie - all browsers know how to handle them and you can also easily expire them server-side if you no longer want to allow a particular token to have access (that's quite an important point). Also I strongly recommend against relying on server-side session to maintain the authentication session.
Authentication tokens should ideally be stateless (just like in Forms Authentication's cookie) - the burden of proof is on the client to send you a correct token, with that token containing the information you need to re-initialise the current requests state with the correct user.
If, however, it's a general purpose API for any type of client then you should allow the client to send the token to you in the query string of all requests at a very minimum. You should also support taking it in the request header as well - clients that can easily support setting request headers often prefer to because it then hides the auth token from the URL and makes formatting requests easier (there's also the potential to max out a web server's query string limit if the token is big enough).
I then recommend you look, at a minimum, at overriding MVCs AuthorizeAttribute (there are 2 - one for the 'standard' MVC 4 pipeline and one for the new Web API pipeline, & they would both need to be done if you are using both technologies. The link is for the MVC 4 one) to crack out your cookie/header/query string value. In there you can get the value, decrypt the token, identify the user and set the roles. The core code of that attribute then contains the logic for denying a request based on whether the user is authenticated/has a certain role/is a certain user.