...
const userIcon = document.querySelector('.user-icon');
userIcon.addEventListener("click", () => {
if (token) {
fetch('/privatearea', {
headers: {
'authorization': `Bearer ${token}`
}
}).catch(err => console.log(err));
} else {
window.location.assign('/login');
}
...
What i'm trying to do is:
When the element "userIcon" is clicked if "token" exists, i want to fetch the url "http://localhost:3000/privatearea".
If token doesn't exist the url "http://localhost:3000/login" is reached.
...
router.get('/privatearea', authenticateToken, (req, res) => {
res.render("private-area");
});
...
The backend has been done with node js and express.
So, if i click the element the url "http://localhost:3000/privatearea" should be reached and the page "private-area" should be renderized.
But it doesn't happen. I don't need a response, but i would reach the url by setting some headers.
fetch() by itself does not render anything in the browser. It just returns content back to your Javascript. So, if you had a .then() handler on the fetch() call, you would just get back the rendered data from res.render("private-area"); on your server.
userIcon.addEventListener("click", () => {
if (token) {
fetch('/privatearea', {
headers: {
'authorization': `Bearer ${token}`
}
}).then(data => {
// here you will see the result of res.render("private-area");
// on your server
console.log(data);
}).catch(err => console.log(err));
} else {
window.location.assign('/login');
}
...
}
So, the way you have it isn't the correct design for using fetch(). Since you don't show the larger context of this feature here, it's not entirely clear what the best architecture/solution would be here. Here are some options:
You could take the data from the .then() handler I show above and insert it into the current page using your own Javascript.
You could do window.location = '/privatearea'; and find some other way to communicate the Bearer token to your server (perhaps a previous call to your server sets a session cookie) so your server can use the session cookie to verify that the browser is allowed to enter /privatearea.
You could switch your auth model to something that is more friendly to browser URLs (like a session/cookie) since you can't use a Bearer Token from a URL entered into the browser bar as they can only be used from Javascript. The user logs in, gets assigned a session cookie and URL handlers on your server for URLs from the browser check that.
Related
I'm developing an SSR app with Vue. The API is made using Laravel framework and i'm using axios to send requests to the API.
I have to make a POST request on server-side to fetch it's response and render it on initial page load. To fetch the CSRF token for the POST request, the Laravel API provides an endpoint that sends the CSRF response as JSON (instead of a cookie because cookies cannot be used on server-side).
I'm attaching the csrf token in the following way:
this._vm.$api.post('/api/ssr-csrf-token').then(response => {
console.log("Token: ", response.data.token); // This shows that csrf is fetched correctly
let config = {
headers: {
'X-XSRF-TOKEN': response.data.token
}
};
this._vm.$api.post('/api/product/categories', { some: "thing"}, config).then(response => {
console.log("Categories: ", response.data.data);
}).catch(err => {
console.log(err);
})
}).catch(err => {
console.log(err);
})
But sending the request this way still gives the "CSRF Token Mismatch" error. Even though the token is attached to the request.
When I use Postman to fetch the results on categories endpoint, everything works fine and I get the response as should. But making a request from my SSR project I'm not receiving the response.
What do I need to do to get the response? Any help will be appreciated.
Thanks
You can attach the token to axios.defaults.headers.common.
next time, when you make a call, it auto sets your token to the header.
this._vm.$api.post('/api/ssr-csrf-token').then(response => {
console.log("Token: ", response.data.token); // This shows that csrf is fetched correctly
// set token to your header
axios.defaults.headers.common['Authorization'] =
'Bearer ' + response.data.response.access_token;
this._vm.$api.post('/api/product/categories', { some: "thing"}).then(response => {
console.log("Categories: ", response.data.data);
}).catch(err => {
console.log(err);
})
}).catch(err => {
console.log(err);
})
Now you don't have to include the config variable in your request. it auto sets to the header on your all calls.
I am trying to build a web application using Firebase and NodeJS with ejs templates at the client. I want to make sure the user is authenticated before accessing a particular route through a middleware. In this middleware, I verify ID tokens sent through the client and then render the page if they are verified. But when the tokens are verified the app doesn't show the new page i.e. "user-profile.ejs".
<button onclick="sendRequest('/profile')">Profile</button>
//The client function which sends ID token
function sendRequest(url){
if(firebase.auth().currentUser){
firebase.auth().currentUser.getIdToken(true)
.then(idToken => {
client({
method: 'get',
url: url,
headers: {
'authtoken': idToken
}
})
.then(res => {
console.log("Auth token sent.")
})
.catch(err => {
console.log(err)
})
})
.catch(err => {
console.log(err)
})
}else{
client({
method: 'get',
url: url,
}).then(res => {
console.log("Request sent without header.")
})
.catch(err => {
console.log(err)
})
}
}
//The NodeJS server which contains the routes and checkAuth middleware
function checkAuth(req,res,next){
if(req.headers.authtoken){
console.log('Auth token with headers received. User has logged in.')
admin.auth().verifyIdToken(req.headers.authtoken)
.then(verifiedUser => {
console.log('Auth token verified')
return next()
})
.catch(err => {
console.log(err)
res.redirect('back')
})
}else{
console.log('Without header received.')
res.redirect('back')
}
}
app.get('/',(req,res)=>{
res.render('home')
})
app.get('/profile',checkAuth,(req,res)=>{
console.log("Reached user profile")
res.send("user-profile")
})
Remember that when you redirect, the client get the redirected URL and issues an entirely new http request. That starts a completely new request cycle on your server that will go through all your middleware again that matches that new URL.
Automatic client redirects do NOT include custom headers from the original response. If that's what you were expecting, that will not work. If you're following a redirect manually from an Ajax call, you can manually get the token from the redirect response header and manually add it to a new Ajax call to the redirected location.
If you're expecting the browser or some automatic redirect to handle the redirect, they won't take the token with them from the headers. So, your server will have to either send the response to the original URL without redirecting or you will have to put the token in something that will survive the redirect such as a query parameter on the redirect (usually a bad idea for a credential) or in a cookie or in a server-side session object that is tied to a cookie.
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
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);
I am trying to build a token system to allow authentication via an email link. The flow I am thinking might work is...
click email link (of the form site.com/login?token=hf74hf64&email=m#email.com) -> server checks the token is valid and the email is registered -> server redirects to '/' with a session cookie -> client acknowledges session cookie and authenticates the user
The last step is where I'm having trouble. How do I detect from within my component that a session cookie is present?
I was thinking of something like this in my React auth component:
class AuthenticatedComponent extends Component {
componentWillMount() {
if (cookie) {
this.props.dispatch(authenticateUser())//.....
}
}
}
Might this work, or do I need to make a separate fetch to the server and trigger the dispatch depending on the response?
We've implemented a very similar approach for our app. For this to work, we handle all the login in Node and not in the actual components.
Check if token is provided in query string
Pass token to server to validate
If token is valid, create the cookie as you would for a normal user/pass login
Redirect call to original url, sans the token
server.js
// I abstracted the login functionality into one call since it's the same for us
var handleAuthRequest = function handleAuthRequest(auth_body, req, res, next) {
request ({
method: 'POST',
uri: Constants.API_LOGIN_URL,
body: auth_body,
json: true
}, (error, response, body) => {
if (response.statusCode === 200) {
// this makes a cookie with the response of the body (auth token)
ssoUtils.generateCookies(body, res)
// this redirects to the initial url, with the provided cookie.
// Assuming your router already doesn't allow certain components to be accessed
// without a cookie, it would work the same for the login_token auth too.
res.redirect(req.url)
}
else {
next();
}
})
}
// this needs to come before any other routes
app.use((req, res, next) => {
// check if login_token query string was provided
if (req.query.hasOwnProperty('login_token')) {
var {login_token} = req.query
// API call to server to validate token
var jwtToken = jwt.sign({
sub: login_token
}, Constants.API_JWT_SECRET)
// modify the redirect url to remove the token
let parsed = url.parse(req.url)
delete req.query['login_token']
let newUrl = parsed.pathname + '?' + qs.stringify(req.query)
req.url = newUrl
// call the generic login handler
return handleAuthRequest({link_token: jwtToken}, req, res, next)
}
Assuming your server will return the same response from logging in or a valid link token, this would just redirect the call back to whatever your existing process is so no separate functionality client side is needed. As you can see, we also sign the token in a JWT to ensure it's only accepted by the server if sent from our app.
We use React Router to handle our client side routing. Your onEnter check for the initial route would look like this.
routes.js
// token is passed in from our cookie by both the client and server
module.exports = function (token, userAgent, originalUrl) {
function isToken() {
return token !== undefined && token !== null;
}
function ifNoTokenRedirect(nextState, replaceState) {
// check if token is set from cookie
if (!isToken()) {
replaceState({ nextPathname: nextState.location.pathname}, '/signup? redirect=' + originalUrl.pathname);
}
}
return (
// the actual routes
)
}