I'm searching for a clean way to store my access token in React.
- First thing: I don't want to use local storage. I don't need to make my access token persistent since I can always refresh it.
- I also excluded a cookie since I want to prevent CSRF attacks. By storing the access token only in memory, in fact, the page needs to be loaded to get the token and authenticate requests (refresh token can be used only to refresh)
- I thought of using redux/context, however, the function calling the API is not a child of a ponent so I can't access the token from that. Furthermore, I don't want to pass the token as a parameter, since I want to keep decoupled the HTTP logic. Maybe there is a clean way to use it?
After a bit of research, I found that using a global variable is a working "cheat" to obtain what I want. However, I was guessing if there was a clearer way to get the same result.
//token.js
const access_token= "";
export const setAccessToken(token){
access_token=token;
}
export const getAccessToken(){
return access_token;
}
//NOTICE:
// -ON LOGIN/REFRESH: I set the access token
// -ON API CALLS: I get the access token and I add it to the header
api.js
const baseURL= "http://my_base_url";
const generateApiInterface = ()=>{
let headers : any= {
};
token=getAccessToken(); //HERE I NEED TO RETRIEVE MY ACCESS TOKEN
if(token){
headers={
'Authorization': 'Token '+ token,
...headers //append other basic proprieties
}
}
return axios.create({
baseURL: baseURL,
headers: headers
});
}
const api = generateApiInterface();
I'm searching for a clean way to store my access token in React.
- First thing: I don't want to use local storage. I don't need to make my access token persistent since I can always refresh it.
- I also excluded a cookie since I want to prevent CSRF attacks. By storing the access token only in memory, in fact, the page needs to be loaded to get the token and authenticate requests (refresh token can be used only to refresh)
- I thought of using redux/context, however, the function calling the API is not a child of a ponent so I can't access the token from that. Furthermore, I don't want to pass the token as a parameter, since I want to keep decoupled the HTTP logic. Maybe there is a clean way to use it?
After a bit of research, I found that using a global variable is a working "cheat" to obtain what I want. However, I was guessing if there was a clearer way to get the same result.
//token.js
const access_token= "";
export const setAccessToken(token){
access_token=token;
}
export const getAccessToken(){
return access_token;
}
//NOTICE:
// -ON LOGIN/REFRESH: I set the access token
// -ON API CALLS: I get the access token and I add it to the header
api.js
const baseURL= "http://my_base_url";
const generateApiInterface = ()=>{
let headers : any= {
};
token=getAccessToken(); //HERE I NEED TO RETRIEVE MY ACCESS TOKEN
if(token){
headers={
'Authorization': 'Token '+ token,
...headers //append other basic proprieties
}
}
return axios.create({
baseURL: baseURL,
headers: headers
});
}
const api = generateApiInterface();
Share
Improve this question
edited Feb 2, 2021 at 8:58
Andrea Costanzo
asked Feb 1, 2021 at 23:34
Andrea CostanzoAndrea Costanzo
2,2354 gold badges17 silver badges40 bronze badges
4
- What's wrong with storing it in a cookie and just retrieving it from there every time you want to make an HTTP call? – codemonkey Commented Feb 1, 2021 at 23:45
- I already have the refresh token in an HTTP only cookie. I don't want to put the access token in a cookie to prevent CSRF attacks – Andrea Costanzo Commented Feb 1, 2021 at 23:49
- Since you've mentioned login, you must have some sort of a session. I'd store the access token on session level, there's no need to always get a fresh one, even if you can do it. – Zoli Szabó Commented Feb 2, 2021 at 8:25
- Actually I'm using JWTs to store "session" on client-side. To avoid security issues I need to refresh my tokens. To better understand my approach I link you to the post in which I explain my authentication process (asking if it's a good approach): stackoverflow./questions/65946041/… – Andrea Costanzo Commented Feb 2, 2021 at 8:52
1 Answer
Reset to default 6The way you're doing it is preferable to keep anything in memory. If this is server side, then you'd want to make sure you delete the token once you're done, but if not then the current approach is desirable. Note, you're using a const
, you'd want to change that to a let
. Another idea would be to use session storage, but then that brings the idea of XSS.
However, React provide a way to have "global" state - this would be to use a provider and context. Here's an example:
// provider.tsx
import React, { useContext, createContext, FC, useState } from 'react'
type AccessTokenContext = [string, React.Dispatch<React.SetStateAction<string>>]
const AccessTokenProvider: FC = (props) => {
const [accessToken, setAccessToken] = useState<string>(null)
return <AccessToken.Provider value={[accessToken, setAccessToken]} {...props} />
}
const AccessToken = createContext<AccessTokenContext>(null)
const useAccessToken = (): AccessTokenContext => useContext<AccessTokenContext>(AccessToken)
export { AccessTokenProvider, useAccessToken }
You'd have to wrap your app container in AccessTokenProvider
:
// index.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import { AccessTokenProvider } from './providers/AccessTokenProvider'
ReactDOM.render(
<AccessTokenProvider>
<App />
</AccessTokenProvider>,
document.getElementById('app')
)
Then you can then use the hook useAccessToken
in App
and any children of App
. Of course, this provider doesn't have to be root level, but it's easiest to include it here.
// app.tsx
import React, { FC } from 'react'
const App: FC = props => {
const [accessToken, setAccessToken] = useAccessToken()
return <div>{/* content */}</div>
}
export default App