access Nextjs httponly cookie - reactjs

I'm working on a Nextjs app and I'm using Laravel api for auth and other things.
So I was searching about the best way to store the token that i will get when I sign a user in or sign him up from that Laravel external api and as I found storing it via httponly cookie is the best way and that's what I did using nextjs api routes to store it there.
I create 3 api routes in /api directory for loging the user in and up and out.
but now I'm facing an issue which is how to send this token on each request that i'm sending on client side to that api.
for now I'm using getServerSideProps to get the token and then send it by props to the needed component but this is solution is redundant and not handy at all.
I'm using getServerSideProps just to get it on any page that need to communicate with backend.
so is there any way to forward the token on each request without the need to get the token from server side and send it to client side?
or do u think there is a better way to store the token in somewhere else?

If you have a httpOnly cookie and you don't want to expose it to the client-side, I don't suggest to pass it in getServerSideProps as prop to the component. This will expose your token and will have the risk of scripts accessing the cookie. If the cookie is HttpOnly, it means it cannot be accessed through the client-side script.
Instead, you can create a NextJS api and access your token there. Then call that api in your app.
This is an example of how to access the cookie in nextjs api:
// src > pages > api > endpoint.js
export default async function (req, res) {
// assuming there is a token in your cookie
const token = req.headers.cookie.token;
// Attach the token to the headers of each request
const fetchWithToken = (url, options = {}) => {
options.headers = options.headers || {};
options.headers.Authorization = `Bearer ${token}`;
return fetch(url, options);
};
// Your API logic here
// ...
}
Now in your component in client-side, you can easily the new endpoint in /api/endpoint/ instead of calling the API with the token. In this method, you will never expose the token to the browser:
import { useState, useEffect } from 'react';
const MyComponent = () => {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const res = await fetch('/api/endpoint');
const data = await res.json();
setData(data);
};
fetchData();
}, []);
// Your component logic here
// ...
};
export default MyComponent;

Related

Should I store a Axios instance configured with interceptors that use a state value in a state or ref or useMemo?

Similiar to Should we use axios inside both components and store?
I have a state called authorization which contains the Bearer token value that would be used in Axios calls. The state is available in a context and accessible using the useContext hook.
I create the AxiosInstance where I add a interceptors.request.use to add the Authorization header.
What I've done so far was useMemo with the authorization value a a dependency. But since Axios operation is asynchronous it seems that I may get the wrong axios instance.
I did a bit of refactoring using useRef and I still had a bit of an issue.
What I then did was implement the Observer pattern and send a notification to the component that provides the Axios client that the authorization header was changed and update the ref. However, again there's still some cases where the old client is being invoked.
What I am wondering is should I store it in useState or is there a fundamental problem with the approach of storing the Axios client and instead should I just bite the bullet and create a new axios client per request which takes the authorization header that's presently in the state?
The way I typically do it is to save the authentication information in a React context or to redux, and create axios instances as needed to access the authorization token.
Maybe something like:
const getBearerToken() => { ... [your implementation to retrieve from context here] ... };
const webserviceRequest = (url) => () => axios.create({
baseURL: url,
... [AxiosRequestConfig options here] ...
headers: {
Authorization: `Bearer ${getBearerToken()}`,
...
},
});
Then, you can define your webservice requests by invoking this function, e.g.:
const sampleGetRequest = () => webserviceRequest(SAMPLE_URL)().get('');
const samplePostRequest = (postData) => webserviceRequest(SAMPLE_URL)().post('', postData);
These return a Promise<AxiosResponse> which you can call as normal, e.g.
sampleGetRequest().then((response) => { ... }).catch((error) => { ... })
and so on.
The key point is that the webserviceRequest returns a function which creates an asynchronous webservice request with the current authorization token. You don't ever save the webserviceRequest function because that authorization token can become stale.
With the dependency on React context, I'd avoid using interceptors for this (I'd avoid using Axios all together but that's just my opinion).
Instead, try creating a custom hook
import axios from "axios";
import { useContext } from "react";
const api = axios.create({ /* baseURL, etc */ });
const useApi = () => {
const authorization = useContext(AuthContext); // guessing
if (authorization) {
api.defaults.headers.common.Authorization = `Bearer ${authorization}`;
} else {
delete api.defaults.headers.common.Authorization;
}
return api;
};

React provide default authentication for all request

I'm using React with axios mainly. There I have an interceptor for API calls to refresh my JWT token when it expires.
<img src="/media/cache/img.jpg" alt={row.id} width={45} height={45}>
These are also loaded from the server and authentication is needed. But when the token expires and no API query is needed, these images won't load because the token is invalid and authentication is required for these images.
Can I somehow achieve that even in these scenarios the tokens are refreshed correctly before loading the image?
You can use axios to fetch images as well. It looks something like this:
const url = "/media/cache/img.jpg";
const [objectURL, setObjectURL] = useState("");
useEffect(() => {
axios
.get(url, {
responseType: "blob",
})
.then((res) => {
const new_blob = new Blob([res.data], { type: "image/jpg" });
setObjectURL(URL.createObjectURL(new_blob));
});
}, []);
<img src={objectURL} alt={row.id} width={45} height={45}>
Now you can modify this to use your "interceptor for API calls" to refresh your token.

NextAuth: How can I attach my JWT token to every axios call?

I am building a Next.js application.
For authentication, I am using NextAuth.
For making HTTP calls, I am using Axios.
The problem I am facing is that I want to attach the JWT with every axios call I make.
I have an axios instance created as:
axios-client.js
const ApiClient = () => {
const defaultOptions = {
baseURL,
};
const instance = axios.create(defaultOptions);
instance.interceptors.response.use(
(response) => {
return response;
},
(error) => {
console.log(`error`, error);
throw new Error(error.response.data.message);
},
);
return instance;
};
export default ApiClient();
I can get the jwt from getSession() function provided by next-auth.
But the problem is that function is asynchronous. If I try to get jwt from this from the getSession() function, I always get a "Promise" instead of value.
PS: I am using Strapi which sends the JWT after successful login.
Why can't you await the response from getSession() and add it as a header to your request. Note that you don't have to reinitialize axios client each time, you can reuse it.

Is it safe to temporarily store JWT in react native variable?

I'm currently using react-native-secure-storage to store JWTs for my user. The app retrieves data from an Express API so when making an HTTP request I retrieve the token from secure storage and when the async function completes the token is sent it to the API. I've run into a problem now which is that I want to make a component with an image in it served from the API but I can't get the JWT in an async function since the component needs to return immediately. My question is can I just temporarily store the JWT in a variable or in component state?
Yes, you can store it in a variable, in a context, or in a state. I would recommend using React.context() to propapate your JWT token.
Example:
const JwtStuffContext = React.createContext({
jwt: null,
});
const YourApp = () => {
const theToken = 'abc';
return <JwtStuffContext.Provider value={{ jwt: theToken }}>
{/** The rest of your app, or {children} */}
</JwtStuffContext.Provider>
}
const SomewhereDownTheReactTree = () => {
const { jwt } = useContext(JwtStuffContext);
}
Note that if you wanted to avoid sharing the JWT directly with your entire application, you could a function in your state that would proxy your API requests.
However, as you mentioned displaying an image, you could create a special Picture component that fetches the token from your store, and then requests the image from the API. Like so:
const Picture = ({ placeholderSrc, getSrcWithToken }) => {
const [token, setToken] = useState(null);
useEffect(() => {
const token = await yourFunctionToFetchTokenFromstore();
setToken(token);
}, [finalSrc]);
return <img src={token !== null ? getSrcWithToken(token) : placeholderSrc } />;
}
The above would display the placeholderSrc for as long as the token is unknown, when the token is known, it would call the prop function getSrcWithToken passing the token, allowing you to construct the URL if you need to.

Auth Nextjs website from getInitialProps with localstorage or cookie and Apollo

I have some problems with the auth process on Nextjs project. From the example, it stores the token in cookies, but in checkLoggedIn.js it queries DB instead of get the token from cookie.
I would like to get the token from cookie or localstorage in getInitialProps, but in getInitialProps, it can NOT see localstorage because it's still in server side. Is there any better way that I could auth user before component render?
Not sure is it possible to get the token from getToken in apollo client.
My current code is
class DashBoard extends React.Component {
constructor(props) {
super(props)
}
componentDidMount () {
const decodeToken = verifyToken(localStorage.getItem('KEY'));
if (!decodeToken.mail) {
Router.push('/login');
} else {
this.props.loginSuccess(decodeToken.name, decodeToken.mail);
}
}
render () {
return (<div></div>)
}
}
Thank you
I solved this by using nookies. It works both server side (for getInitialProps) and client side in Next.js
Here is what I did to solve it after few days of research (honestly, not the best documentation around)
In the getInitialProps, I took the headers from the requests and copied them over in my fetch request.
Profile.getInitialProps = async ({ req }) => {
const headers = { ...req.headers }
const url = `http://localhost:3000/api/profile`
const data = await fetch(url, { headers }).then(res => res.json())
return { data }
}
Keep in mind that this only works on the server. For the client, you can just pass {credentials: 'include'} to fetch

Resources