I'm currently developing a node express postgresql application, and I'm trying to implement Jsonwebtokens as authentication. I've seen multiple tutorials on how to implement it and I get how to do it on the backend part, but the frontend is usually skipped and apparently everyone just tests their code with Postman.
I have also read online that the recommended way to implement jwt authentication is to store the generated token in localstorage, and, when needed, to send it on the header. But I wasn't able to find how this is done...
Thus, my questions are:
How do you store the token on the front-end once it's generated by the backend? (an example would help a lot, because I don't really get how am I supposed to get the token on a front-end javascript program)
How do you send the token on the headers when making an http request that needs it once you have it stored?
On the server side, once you have created the token and logged the user in, you send the token via res.send(), example below, note that you may have different approach to functions findByCredentials ad genereateAuthToken, they are custom:
app.post("/users/login", async (req, res) => {
try {
const user = await User.findByCredentials(
req.body.email,
req.body.password
);
const token = await user.generateAuthToken();
res.send({ token: user.tasks });
} catch (e) {
res.status(400).send();
}
});
On the frontend you can use html5's fetch() to send the token in the header. For example, if you would like to access '/users/me' that needs authentication you follow the steps below (make sure you however you save the token to localstorage first so you can access that via getItem:
localStorage.setItem('userInfo', JSON.stringify(userInfo));
document.getElementById("my-profile").addEventListener("click", getMe);
then:
function getMe(e) {
e.preventDefault();
var token = JSON.parse(localStorage.getItem('token'));
console.log(`Authorization=Bearer ${token}`)
fetch('/users/me', {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + token
}
})
.then(res => res.json())
.then(data => {
console.log(data)
// window.location.href = 'http://localhost:3000/dashboard';
})
.catch(err => { console.log(err) })
}
As you said, usually the token is store in localStorage.
localStorage is similar to sessionStorage, except that while data
stored in localStorage has no expiration time, data stored in
sessionStorage gets cleared when the page session ends — that is, when
the page is closed.
https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
For getting the token in front-end you send to a URL the email & password of the user in order to exchange it with a token (you have to be in https). After that you store it with localStorage.setItem('key', value)
Short example:
$.post("/authenticate", {email: userEmail, password: userPassword}, function(data) {
localStorage.setItem('token', data.token)
});
For get back the token, after a refresh for example, you have to use : localStorage.getItem('key').
And finally, in order to be authenticate with this token, you can send it in bearer headers in Authorization headers property.
Why bearer ? => https://security.stackexchange.com/questions/108662/why-is-bearer-required-before-the-token-in-authorization-header-in-a-http-re
Example:
$.ajax({
type: 'GET',
url: '/account,
headers: {
"Authorization": "Bearer " + token
}
}, function(data) {
// Authenticated data
});
May this can help : https://github.com/auth0-blog/angularjs-jwt-authentication-tutorial/blob/master/frontend/login/login.js
Related
I've built a JWT API for user authentication. I want to use it with react.
my app has these routes:
Node js API Routes:
http://localhost:8080/api/auth/signin -> this route accepts username and password from react and sends back an access token to react which it will save it inside localstorage(I'm planning to use memory instead) and cookie containing refresh token with httpOnly enabled.
http://localhost:8080/api/auth/signup -> this route will add a new user to database based on user input(doesn't relate to authentication process)
http://localhost:8080/api/auth/refreshtoken -> this route will return a new access token based on the sent refresh token from client which was saved inside cookies. and then client will replace it with the expired access token.
http://localhost:8080/api/test/user -> this will check if user is signed in with the access token from client and will send the user data back
React Client Routes:
http://localhost:3000/login -> a route for sending user information for logging in
http://localhost:3000/register -> a route for creating a new user
http://localhost:3000/user -> a protected route which needs user to be logged in to view data.
when I log in to my website I can see the data inside the user route for 20 seconds before the access token expiry. when the access token is expired and I do a new request the server will response with 401 unauthorized code. so this way I can make sure that the access token is expired and I need to use refresh token. I've done that like this:
const API_URL = "http://localhost:8080/api/test/";
const getUserBoard = () => {
return axios.get(API_URL + "user", { headers: authHeader() })
.catch((error) => {
if(error.response.status === 401) {
// if error response status was 401 then request a new token
authService.refreshToken();
window.location.reload();
}else if(error.response.status === 403) {
authService.logout();
window.location.href = "/login";
}
});
};
code to get new access token with refresh token:
const refreshToken = () => {
axios({
url: API_URL + "refreshtoken",
method: "POST",
withCredentials: true
})
.then((response) => {
if(response.data.accessToken) {
const user = JSON.parse(localStorage.getItem("user"));
user.accessToken = response.data.accessToken;
localStorage.setItem("user", JSON.stringify(user));
}
})
.catch((error) => {
console.log("server: " + JSON.stringify(error.response));
})
}
code to set header for receiving data from server using access token:
export default function authHeader() {
const user = JSON.parse(localStorage.getItem('user'));
if (user && user.accessToken) {
// for Node.js Express back-end
return { 'x-access-token': user.accessToken };
} else {
return {};
}
}
the first part for getting new access token on 401 error works good but my refresh token also expires after 40 second. if I send the expired refresh token it won't return a new access token. and because of that again I will receive a 401 error which again will cause a 403 error and here I'll get stuck in an infinite reload loop.
any idea? how can I control tokens expiry?
You're issuing tokens in your node.js app, right? So that is where you should adjust the expiration time of the token. The code which issue tokens should have an option to set the expiration time.
Remember that once the refresh token is expired you should log in again. You can implement something which is called a rolling refresh token. So whenever you call the /api/auth/refreshtoken endpoint you can also issue a new refresh token, with a new expiration time and return it in a cookie.
I am during create a chat-app. I want to find a good way to safely connect front and backend using a TokenId from firebase auth.
Do I have to in every request from my front to backend send a TokenId?
It is possible to do it in session or something like that I can send token once and verified it once time too?
My current road.
My code front end where I send a token:
fetch('http://localhost:5500/check', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ token: token }),
})
.then(data => data.json())
.then(e => console.log(e))
.catch(err => console.log(err));
});
I get token on front end like that:
firebase.auth().currentUser.getIdToken(true)
My code backend verify token:
router.post('/check', urlencodedParser, (req, res) => {
const {token} = req.body;
admin
.auth()
.verifyIdToken(token)
.then(function (decodedToken) {
// let uid = decodedToken.uid;
res.setHeader('Content-Type', 'application/json');
res.send({status: 'Ok'});
})
.catch(function (error) {
console.log(error);
res.setHeader('Content-Type', 'application/json');
res.send({status: 'err'});
});
});
Do I have to in every request from my front to backend send a TokenId?
That is what the Firebase clients and back-end services do: they send (and reverify) the ID token with every request. The server caches is the public key that is needed to decode the token, but aside from that are pretty much stateless with regards to the user/ID token.
It is possible to do it in session or something like that I can send token once and verified it once time too?
You can definitely keep the token in a session, but that means that you'll need to send the session ID instead of the token itself, and have to implement state management for your sessions.
On my express server I make queries to an external API using its own token. When I log in to my server I request a token to the external API based on the user who logged in, and I keep the token of the external API in the token of my express server.
Each user gets different data according to their token from the external api, for queries that require external API information, I read the received token and get the external API token to send it through headers with axios, for example:
const LoginUser = (request, response) {
axios.post('/ExternalApi/auth',request.body)
.then( data =>{
const payload = {
...
tokenExternalApi: data.token
}
const token = jwt.sign(payload, ...)
return response.status(200).json(token)
})
}
const getData = (req, response){
const tokenFromClient = req.headers.authorization
//Function extract tokenExternalApi from payload Token
const tokenExternalApi = getTokenExternl(tokenFromClient )
axios.get(`/urlExternalApi`, { headers:
{ Authorization: tokenExternalApi }}
).then(res => {
return response.status(200).json(res.data)
})
}
Is this the correct approach to managing external apis tokens or is there a cleaner way to do it?
Here is my sample code that I use for hit an external API within function in node js using axios
first time you should install axios npm install axois
const axios = require('axios');
async yourFunction(){
axios({
method: 'POST',
url: "http://yoururl.com",
data: {
name: '+62'+phoneNumber,
number: '+62'+phoneNumber,
message: 'success',
}
});
}
In my personal opinion, this seems to be a clean approach.
But keep in mind that tokens are visible to users, so the fact is your users can decode the token, view tokenExternalApi, know that you are using an external API in the backend and directly make calls to ExternalApi using that token, provided they have the know-how of it. If you understand this fact and are fine with it, then this works.
Otherwise, you can consider encoding the token before sending it to the user or store it on the server-side session.
I have done a tutorial trying to get my head around JWT tokens. I seem to have got my head around the token creation as well as using the token to allow or disallow access to a route.
This all works great using postman, but in postman I enter the token under authorization. My question is:
1. how do I send the token to the client so it is saved on that client side.
2. How does the client return the token when they try to access a route?
I need to understand how this happens when NOT using postman. I am sure its pretty simple.
Do I just send
`res.header('Authorization', 'Bearer', + token);`
`res.header('Authorization', 'Bearer' + token);`
But can I send this with other stuff like a message / data etc?
Then when the user tries to access a protected route later, How do I access this header. IOW how is it stored client-side?
This is what I have thus far:
`//login route`
`app.post('/login', async function(req, res, next) {
const { name, password } = req.body;
if (name && password) {
let user = await getUser({ name: name });
if (!user) {
res.status(401).json({ message: 'No such user found' });
}
if (user.password === password) {
// from now on we'll identify the user by the id and the id is the
// only personalized value that goes into our token
let payload = { id: user.id };
let token = jwt.sign(payload, jwtOptions.secretOrKey);
res.json({ msg: 'ok', token: token });
} else {
res.status(401).json({ msg: 'Password is incorrect' });
}
}
});`
`// protected route
app.get('/protected', passport.authenticate('jwt', { session: false }), function(req, res) {
console.log('REQUEST HEADERS ON PROTECTED::',req.headers.authorization)
res.json('Success! You can now see this without a token.');
});`
The console.log under protected route gives me:
"REQUEST HEADERS ON PROTECTED:: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNTU2NjI3NTczfQ.gAU2VzpUpXHpcgM6_n8gf7D-xLCS59tK6K2RIlIk-L4" but I gather this is because I used the authorization in postman.
I recently worked with jwt auth using react as my front end and hapi.js as backend. To save the token on the client side, you can use localstorage like this:
You have to save this on the user login component.
localStorage.setItem('token', res.data.token);
And then, to access this token on the protected router, use this :
let token = localStorage.getItem('token');
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
I hope this may help you to solve your problem on the client side.
So far I have only dealt with server-rendered apps, where after a user logs in via username/password or using an OAuth provider (Facebook etc.), the server just sets a session cookie while redirecting to the relevant page.
However now I'm attempting to build an app using a more 'modern' approach, with React on the frontend and a JSON API backend. Apparently the standard choice for this is to use a JSON web token for authentication, however I'm having trouble working out how I'm meant to provide the JWT to the client so it can be stored in session/local storage or wherever.
Example to illustrate better:
User clicks link (/auth/facebook) to log in via Facebook
User is redirected and shown Facebook login form and/or permission dialog (if necessary)
Facebook redirects user back to /auth/facebook/callback with an authorization code in tow, the server exchanges this for an access token and some information about the user
Server finds or creates the user in the DB using the info, then creates a JWT containing a relevant subset of the user data (e.g. ID)
???
At this point I just want the user to be redirected to the main page for the React app (let's say /app) with the JWT in tow, so the frontend can take over. But I can't think of an (elegant) way to do that without losing the JWT along the way, other than to put it in the query string for the redirect (/app?authtoken=...) - but that will display in the address bar until I remove it manually using replaceState() or whatever, and seems a little weird to me.
Really I'm just wondering how this is typically done, and I'm almost sure I'm missing something here. The server is Node (Koa with Passport), if that helps.
Edit: To be clear, I'm asking what the best way is to provide a token to the client (so it can be saved) after an OAuth redirect flow using Passport.
I recently ran across this same issue, and, not finding a solution here or elsewhere, wrote this blog post with my in-depth thoughts.
TL;DR: I came up with 3 possible approaches to send the JWT to the client after OAuth logins/redirects:
Save the JWT in a cookie, then extract it on the front-end or server in a future step (eg. extract it on the client with JS, or send a request to the server, server uses the cookie to get the JWT, returns the JWT).
Send the JWT back as part of the query string (which you suggest in your question).
Send back a server-rendered HTML page with a <script> tag that:
Automatically saves the embedded JWT to localStorage
Automatically redirects the client to whatever page you like after that.
(Since logging in with JWTs is essentially equivalent to "saving the JWT to localStorage, my favorite option was #3, but it's possible there are downsides I haven't considered. I'm interested in hearing what others think here.)
Hope that helps!
Client: Open a popup window via $auth.authenticate('provider name').
Client: Sign in with that provider, if necessary, then authorize the application.
Client: After successful authorization, the popup is redirected back to your app, e.g. http://localhost:3000, with the code (authorization code) query string parameter.
Client: The code parameter is sent back to the parent window that opened the popup.
Client: Parent window closes the popup and sends a POST request to /auth/provider withcode parameter.
Server: Authorization code is exchanged for access token.
Server: User information is retrived using the access token from Step 6.
Server: Look up the user by their unique Provider ID. If user already exists, grab the existing user, otherwise create a new user account.
Server: In both cases of Step 8, create a JSON Web Token and send it back to the client.
Client: Parse the token and save it to Local Storage for subsequent use after page reload.
Log out
Client: Remove token from Local Storage
here is a login request from the server side. it's storing the token in the header:
router.post('/api/users/login', function (req, res) {
var body = _.pick(req.body, 'username', 'password');
var userInfo;
models.User.authenticate(body).then(function (user) {
var token = user.generateToken('authentication');
userInfo = user;
return models.Token.create({
token: token
});
}).then(function (tokenInstance) {
res.header('Auth', tokenInstance.get('token')).json(userInfo.toPublicJSON());
}).catch(function () {
res.status(401).send();
});
});
here is the login request on the react side, where I am grabbing the token from the header and setting the token in local storage once the username and password pass authentication:
handleNewData (creds) {
const { authenticated } = this.state;
const loginUser = {
username: creds.username,
password: creds.password
}
fetch('/api/users/login', {
method: 'post',
body: JSON.stringify(loginUser),
headers: {
'Authorization': 'Basic'+btoa('username:password'),
'content-type': 'application/json',
'accept': 'application/json'
},
credentials: 'include'
}).then((response) => {
if (response.statusText === "OK"){
localStorage.setItem('token', response.headers.get('Auth'));
browserHistory.push('route');
response.json();
} else {
alert ('Incorrect Login Credentials');
}
})
}
When you get a token from any passport authentication sites you have to save the token in your browser's localStorage. The Dispatch is Redux's Middleware. Ignore dispatch if you don't use redux in your app. you can just use setState here (A bit weird without redux).
Client-side:
Here's something similar API of mine, which returns token.
saving tokens
axios.post(`${ROOT_URL}/api/signin`, { email, password })
.then(response => {
dispatch({ type: AUTH_USER }); //setting state (Redux's Style)
localStorage.setItem('token', response.data.token); //saving token
browserHistory.push('/home'); //pushes back the user after storing token
})
.catch(error => {
var ERROR_DATA;
try{
ERROR_DATA = JSON.parse(error.response.request.response).error;
}
catch(error) {
ERROR_DATA = 'SOMETHING WENT WRONG';
}
dispatch(authError(ERROR_DATA)); //throw error (Redux's Style)
});
So When you make some authenticated requests,you have to attach the token with the request in this form.
authenticated requests
axios.get(`${ROOT_URL}/api/blog/${blogId}`, {
headers: { authorization: localStorage.getItem('token') }
//take the token from localStorage and put it on headers ('authorization is my own header')
})
.then(response => {
dispatch({
type: FETCH_BLOG,
payload: response.data
});
})
.catch(error => {
console.log(error);
});
Here's my index.js:
The token is checked each and everytime, so even if the browser got refreshed, you can still set the state.
checks if the user is authenticated
const token = localStorage.getItem('token');
if (token) {
store.dispatch({ type: AUTH_USER })
}
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/" component={App}>
..
..
..
<Route path="/blog/:blogid" component={RequireAuth(Blog)} />
//ignore this requireAuth - that's another component, checks if a user is authenticated. if not pushes to the index route
</Route>
</Router>
</Provider>
, document.querySelector('.container'));
All that dispach actions does is it sets the state.
my reducer file(Redux only) else you can just use setState() in your index route file to provide the state to the whole application. Every time the dispatch is called, it runs a similar reducer file like this which sets the state.
setting the state
import { AUTH_USER, UNAUTH_USER, AUTH_ERROR } from '../actions/types';
export default function(state = {}, action) {
switch(action.type) {
case AUTH_USER:
return { ...state, error: '', authenticated: true };
case UNAUTH_USER:
return { ...state, error: '', authenticated: false };
case AUTH_ERROR:
return { ...state, error: action.payload };
}
return state;
} //you can skip this and use setState() in your index route instead
Delete the token from your localStorage to logout.
caution: Use any different name rather than token to save the token in your browser's localStorage
Server-Side:
considering your passport services file. You must set the header search.
Here's passport.js
const passport = require('passport');
const ExtractJwt = require('passport-jwt').ExtractJwt;
const JwtStrategy = require('passport-jwt').Strategy;
..
..
..
..
const jwtOptions = {
jwtFromRequest: ExtractJwt.fromHeader('authorization'), //client's side must specify this header
secretOrKey: config.secret
};
const JWTVerify = new JwtStrategy(jwtOptions, (payload, done) => {
User.findById(payload._id, (err, user) => {
if (err) { done(err, null); }
if (user) {
done(null, user);
} else {
done(null, false);
}
});
});
passport.use(JWTVerify);
In my router.js
const passportService = require('./services/passport');
const requireAuthentication = passport.authenticate('jwt', { session: false });
..
..
..
//for example the api router the above react action used
app.get('/api/blog/:blogId', requireAuthentication, BlogController.getBlog);