get access token from back and cache it to browser - node.js

hey guys. i'm working with axios,nodeJS and vue . i want
to generate an accessToken in node (it's done) and get it
with axios (it's done too) and cache it to browser cache with
vue(don't know how), so whenever i want to send a request to
node,i could send it with headers of axios , i dont know if it's
possible or not ? need some help around here.

Yes, that's possible and is pretty common.
To do it, you just have to store the token in the browser memory (usually in the localStorage) and then use axios interceptors on the requests to automatically add the token.
How you can add items to local storage?
localStorage.setItem('my-key',my-value)
you can find the complete docs here.
axios.interceptors.request.use(
config => {
//here retrieve the token and add it (if present)
//most probably you would use VUEX for storing it
//in you app
const token = yourLocalStorageAccessor.getAccessToken();
if (token) {
config.headers['Authorization'] = 'Bearer ' + token;
}
// config.headers['Content-Type'] = 'application/json';
return config;
},
error => {
Promise.reject(error)
});
You can read about axios interceptors here.

Related

How do I access Express.js Cookie in React app?

I'm creating E-shop with MERN Stack
This is my response from the server on Login where you can see, the cookie is SET and it's sent from the Backend.
That means no there shoudln't be problem with BE, but the FE I will need to handle the Cookie on the FE.
How do I access this sent Cookie from Express in the React ?
I have tried something like this:
const handleLogin = async (event) => {
event.preventDefault();
try {
const url = "http://localhost:5000/api/auth/login";
const data = await axios.post(url, formFields);
const { user } = data.data;
// Here I have tried to access it from Headers where I can see it in the attached
// picture under Set-Cooki. But inside headers I can only see
// Content-Length and Content-Type
console.log(data.headers);
// Aswell I have tried react-cookie but docs are not clear enough for me.
// and this just sets cookie from react, I would like to use cookie from express
// and set it like this
// await setCookie("user", "INSERT_HERE_COOKIE_FROM_EXPRESS.JS", {
// path: "/",
// });
setCurrentUser(user);
await resetFormFields();
} catch (error) {
const data = error.response.data;
!data.error
? alert(`${data.message}`)
: alert(`${data.message}: ${data.error}`);
}
};
Thank you for any answers, I'm sure it's not that hard as I think and it's few lines of code.
As I see on your screenshot - you use express with httpOnly cookies:
https://developer.mozilla.org/ru/docs/Web/HTTP/Cookies -
A cookie with the HttpOnly attribute is inaccessible to the JavaScript Document.cookie API; it's only sent to the server. For example, cookies that persist in server-side sessions don't need to be available to JavaScript and should have the HttpOnly attribute. This precaution helps mitigate cross-site scripting (XSS) attacks.
And I think you don't want to use nonsecure cookies in your E-shop - so you can't access it, but you can use custom headers, so on your frontend it will be like:
fetch('/myapi').then(response => console.log(response.headers.get('myCustomHeader')));

Is it possible to have protected routes in Remix.run, so the browser doesn't get the protected source code?

Is it possible to have protected routes in the Remix.run React framework, so that only admin users get the protected components, while regular users don't get the protected components at all as part of the JS bundle sent to the browser?
Also, this may require a form of code splitting on the front end side. Is code splitting supported in Remix.run?
this is a code snippet from a sample app I wrote, this is the home page and can only be accessed if the user is authenticated.
the redirect(`/login?${searchParams}`) will redirect if the user isn't authenticated
// Loaders provide data to components and are only ever called on the server, so
// you can connect to a database or run any server side code you want right next
// to the component that renders it.
// https://remix.run/api/conventions#loader
export let loader = async ({ request }) => {
const redirectTo = new URL(request.url).pathname;
let session = await getSession(request.headers.get("Cookie"));
// if there is no access token in the header then
// the user is not authenticated, go to login
if (!session.has("access_token")) {
let searchParams = new URLSearchParams([["redirectTo", redirectTo]]);
throw redirect(`/login?${searchParams}`);
} else {
// otherwise execute the query for the page, but first get token
const { user, error: sessionErr } = await supabaseClient.auth.api.getUser(
session.get("access_token")
);
// if no error then get then set authenticated session
// to match the user associated with the access_token
if (!sessionErr) {
// activate the session with the auth_token
supabaseClient.auth.setAuth(session.get("access_token"));
// now query the data you want from supabase
const { data: chargers, error } = await supabaseClient
.from("chargers")
.select("*");
// return data and any potential errors alont with user
return { chargers, error, user };
} else {
return { error: sessionErr };
}
}
};
You can protect routes by authorizing the user inside the loader of the Route, there you could decide to redirect it somewhere else or send a flag as part of the loader data so the UI can hide/show components based on it.
For the code splitting, Remix does it at the route level, but it doesn't support server-side code-splitting out of the box, you may be able to support it with react-loadable.
I hope it has, but not. Below is the official answer.
https://remix.run/docs/en/v1/pages/faq#how-can-i-have-a-parent-route-loader-validate-the-user-and-protect-all-child-routes
You can't 😅. During a client side transition, to make your app as speedy as possible, Remix will call all of your loaders in parallel, in separate fetch requests. Each one of them needs to have its own authentication check.
This is probably not different than what you were doing before Remix, it might just be more obvious now. Outside of Remix, when you make multiple fetches to your "API Routes", each of those endpoints needs to validate the user session. In other words, Remix route loaders are their own "API Route" and must be treated as such.
We recommend you create a function that validates the user session that can be added to any routes that require it.

How to implement auto jwt token refresh before every graphql request with Apollo and React Native?

I would like to implement auto refresh jwt token before every request to GraphQL with Apollo middleware in React Native app. After every user login he gets two tokens: access and refresh. Access token it is the short one for 30-60 min for using in authorization header. And refresh token it is the long one for 60 days for confirm of refresh token graphql mutation.
My flow:
User login and gets 2 tokens -> put access token to authorization header with Appollo setContext.
User make request to GraphQL -> check expireTime of accessToken on a client side:
-> if it is not expired -> confirm request
-> if it is has expired -> call GraphQL refreshToken mutation -> get new tokens -> confirm request.
For keeping tokens on the client side i use KeyChain storage. Can you tell me please should i use Apollo cache for keeping tokens too? Should i write Apollo state for tokens? And how i can implement my flow?
GraphQL mutation
mutation UpdateTokens($refreshToken: String!, $refreshTokenId: String!)
{
updateTokens(refreshToken: $refreshToken, refreshTokenId: $refreshTokenId) {
user {
name
phone
}
accessToken
refreshToken
}
}
App.js
import React from 'react'
import { ApolloClient } from 'apollo-client'
import { ApolloLink } from 'apollo-link'
import { ApolloProvider } from 'react-apollo'
import { ApolloProvider as ApolloHooksProvider } from 'react-apollo-hooks'
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { setContext } from 'apollo-link-context'
import * as Keychain from 'react-native-keychain'
import AppNavigator from './AppNavigator'
const httpLink = createHttpLink({
uri: 'http://localhost:4000'
})
const cache = new InMemoryCache()
const authLink = setContext(async (req, { headers, ...context }) => {
const tokens = await Keychain.getGenericPassword()
const accessToken = tokens.username
return {
headers: {
...headers,
authorization: accessToken ? `Bearer ${accessToken}` : ''
},
...context
}
})
const client = new ApolloClient({
link: ApolloLink.from([authLink, httpLink]),
cache,
connectToDevTools: true
})
const App = () => {
return (
<ApolloProvider client={client}>
<ApolloHooksProvider client={client}>
<AppNavigator />
</ApolloHooksProvider>
</ApolloProvider>
)
}
export default App
I honestly think you are on the right path -- as I read your required functionality I thought of apollo-link-context and was happy to see you were taking that approach. We recently had to implement similar functionality in a react-native app, which entailed attaching custom headers with authentication-related data. To retrieve this data we were required to make an async request, although ours was over the network to a third-party service. We did this all in setContext from the client just as you are. This worked well.
I do not think you need to concern yourself with updating your Apollo cache with your tokens, manually or otherwise, for at least a few reasons. First, given the quasi-sensitive nature of what you are storing, it would be best practice to defer to the more secure storage solution, which in this case is likely the Keychain. In addition, having a single source of truth for the tokens in your application can keep things clean.
Assuming you do not want to write this data to cache, I would double-check Apollo client is not automatically doing so. For example, if you for some reason previously queried your token data, Apollo might automatically update your cache upon receiving the mutation payload. Just something to be mindful of.

Using cookies with axios and Vue

I have created a Node.js express server that connects to Salesforce.com using the SOAP interface provided by 'jsforce'. It uses session cookies for authorization via the 'express-session' package. So far, it has a POST method for login and a GET to perform a simple query. Testing with Postman has proven that this server is working as expected.
As the browser interface to this server, I have wrttien a Vue application that uses axios to perform the GET and POST. I need to save the session cookie created during login POST then attach attach the cookie to subsequent CRUD operations.
I have tried various methods to handle the cookies. One method I have tried is using axios response interceptors on the POST
axios.interceptors.response.use(response => {
update.update_from_cookies();
return response;
});
The function 'update_from_cookies' attempts to get the cookie named 'js-force' but it does not find it although I know it is being sent
import Cookie from 'js-cookie';
import store from './store';
export function update_from_cookies() {
let logged_in = Cookie.get('js-force');
console.log('cookie ' + logged_in);
if (logged_in && JSON.parse(logged_in)) {
store.commit('logged_in', true);
} else {
store.commit('logged_in', false);
}
}
I have also seen various recommendations to add parameters to the axios calls but these also do not work.
I would appreciate some advice about how to handle cookies using axios or some similar package that works with Vue
Thanks
The problem has been resolved. I was using the wrong syntax for the axios call
The correct syntax has the {withCredentials: true} as the last parameter
this.axios.post(uri, this.sfdata, {withCredentials: true})
.then( () => {
this.$router.push( {name : 'home' });
})
.catch( () => {
});

How to include access-token in the HTTP header when requesting a new page from browser

The similar question was asked by someone else (here) but got no proper answer. Since this is basic and important for me (and maybe for someone else as well), I'm trying to ask here. I'm using Node.js+Express+EJS on the server side. I struggled to make the token authentication succeeded by using jsonwebtoken at the server and jQuery's ajax-jsonp at the web browser. Now after the token is granted and stored in the sessionStorage at the browser side, I can initiate another ajax request with the token included in the request header, to get the user's profile and display it somewhere in the 'current' page. But what I want is to display a new web page to show the user's profile instead of showing it in the 'current' page (the main/index page of the website). The question is:
How to initiate such an HTTP GET request, including the token in the HTTP header; and display the response as a new web page?
How the Node.js handle this? if I use res.render then where to put the js logic to verify the token and access the DB and generate the page contents?
Or, should we say the token mechanism is more suitable for API authentication than for normal web page authentication (where the web browser provides limited API)?
I think the answer to this question is important if we want to use the token mechanism as a general authentication since in the website scenario the contents are mostly organized as web pages at the server and the APIs at the client are provided by the browser.
By pure guess, there might be an alternative way, which the ajax success callback to create a new page from the current page with the response from the server, but I have no idea of how to realize that as well.
By calling bellow code successfully returned the HTML contents in customer_profile.ejs, but the client side ajax (obviously) rejected it.
exports.customer_profile = function (req, res) {
var token = req.headers.token;
var public_key = fs.readFileSync(path.resolve() + '/cert/public_key.pem');
var decoded = jwt.verify(token, public_key);
var sql = 'SELECT * FROM customer WHERE username = "' + decoded.sub + '"';
util.conn.query(sql, function (err, rows) {
if (!err) {
for (var i = 0; i < rows.length; i++) {
res.render('customer_profile', {customer_profile: rows[i]});
break;
}
}
});
};
I am trying to find a solution to this as well. Please note, I am using Firebase for some functionality, but I will try to document the logic as best as I can.
So far what I was able to figure out is the following:
Attach a custom header to the HTTP request client-side
// landing.js - main page script snippet
function loadPage(path) {
// Get current user's ID Token
firebase.auth().currentUser.getIdToken()
.then(token => {
// Make a fetch request to 'path'
return fetch(`${window.location.origin}/${document.documentElement.lang}/${path}`, {
method: 'GET',
headers: {'X-Firebase-ID-Token': token} // Adds unverified token to a custom header
});
})
.then(response => {
// As noted below, this part I haven't solved yet.
// TODO: Open response as new webpage instead of displaying as data in existing one
return response.text();
})
.then(text => {
console.log(text);
})
.catch(error => {
console.log(error);
});
}
Verify the token according to your logic by retrieving the corresponding header value server-side
// app.js - main Express application server-side file
// First of all, I set up middleware on my application (and all other setup).
// getLocale - language negotiation.
// getContext - auth token verification if it is available and appends it to Request object for convenience
app.use('/:lang([a-z]{2})?', middleware.getLocale, middleware.getContext, routes);
// Receives all requests on optional 2 character route, runs middleware then passes to router "routes"
// middleware/index.js - list of all custom middleware functions (only getContext shown for clarity)
getContext: function(req, res, next) {
const idToken = req.header('X-Firebase-ID-Token'); // Retrieves token from header
if(!idToken) {
return next(); // Passes to next middleware if no token, terminates further execution
}
admin.auth().verifyIdToken(idToken, true) // If token provided, verify authenticity (Firebase is kind enough to do it for you)
.then(token => {
req.decoded_token = token; // Append token to Request object for convenience in further middleware
return next(); // Pass on further
})
.catch(error => {
console.log('Request not authorized', 401, error)
return next(); // Log error to server console, pass to next middleware (not interested in failing the request here as app can still work without token)
});
}
Render and send back the data
// routes/index.js - main router for my application mounted on top of /:lang([a-z]{2})? - therefore routes are now relative to it
// here is the logic for displaying or not displaying the page to the user
router.get('/console', middleware.getTranslation('console'), (req, res) => {
if(req.decoded_token) { // if token was verified successfully and is appended to req
res.render('console', responseObject); // render the console.ejs with responseObject as the data source (assume for now that it contains desired DB data)
} else {
res.status(401).send('Not authorized'); // else send 401 to user
}
});
As you can see I was able to modularize the code and make it neat and clear bu use of custom middleware. It is right now a working API returning data from the server with the use of authentication and restricted access
What I have not solved yet:
As mentioned above, the solution uses fetch API and result of the request is data from server (html) and not a new page (i.e when following an anchor link). Meaning the only way with this code now is to use DOM manipulation and setting response as innerHTML to the page. MDN suggests that you can set 'Location' header which would display a new URL in the browser (the one you desire to indicate). This means that you practically achieved what both, you and I wanted, but I still can't wrap my head around how to show it the same way browser does when you follow a link if you know what I mean.
Anyways, please let me know what you think of this and whether or not you were able to solve it from the part that I haven't yet

Resources