How to refresh token using axios? - javascript

I have read a lot of articles about refreshing token , but I didn't get nothing , they seems too complicated. Could you please explain me on my sample. I'm making sign-in , on response i'm getting object with access_token, refresh_token and timestamp. After I'm saving both tokens in localStorage. Later when access_token expires I receive 403 error(Forbidden). There is no message that token expired. Could you please help me with that
signIn(event) {
event.preventDefault()
const formdata = new FormData()
formdata.append("username", this.state.userLogin.email)
formdata.append("password", this.state.userLogin.password)
axios
.post("http://dev.****************.com/auth/get-token", formdata)
.then(res => {
if (res.data) {
localStorage.setItem("token", res.data.access_token)
localStorage.setItem("updToken", res.data.update_token)
this.setState({
isLoginError: false,
})
this.props.history.push("/settings")
}
})
.catch(error => {
if (error.response.status === 403) {
this.setState({
isLoginError: true,
})
}
})
}

There are two generally accepted ways to go about this:
periodically request a refreshed token based on the provided ttl in your original token -- for example, if your original token has a ttl of, say, 3600 seconds, then have a window.setInterval which refreshes every 3530 seconds. This works, but doesn't handle if auth has changed for some other reason.
Better: install an http interceptor to handle a 401 and retry the request after re-authorising. A reasonable answer can be found here: Axios interceptors and asynchronous login

Related

How can I use refresh token in react

I have a get refresh token url like this client.com/api//auth/refresh-token. but I have a hard time using this. I think it should save a refresh token in the local storage after the login. but how can I use it?
login.tsx
export const useLogin = () => {
const LoginAuth = async (data: AuthenticationProps) => {
await axios.post(baseURL + `client/auth/login`,
{
email: data.email,
password: data.password,
},
{
headers: {
"Content-Type": "application/json",
Accept: "application/json",
}
}
)
.then((res) => {
if(res.status === 200) {
console.log("success")
}
}, (err) => {
console.log(err);
})
}
return {
LoginAuth,
}
}
You should not set the refresh token in local storage, it would cause a security vulnerability, since local storage is accessible by javascript, and since refresh token is long term token (live longer than access token), what you would do is, to store access token in local storage, since access token is short termed token, storing in local storage or cookies is totally fine, and then you should make an useEffect() call in react, that check whenever the token is expired and then make the call, a small example:
import Cookies from 'js-cookie';
axios.get("ur_url_here/",data,{withCredentials:true}).then((res)=>{
Cookies.set(res.data.access) // assuming the response has the access token
}))
// now we check the expiration of access token
useEffect(()=>{
if(!(Cookies.get("access"))){
axios.get("refresh_url_here/",{withCredentials:true}).then((res)=>{
Cookies.set(res.data.access)
})
/*what you do here, is try to have a
resource/view in your backend that has
the refresh token and make request to it
so that it gives you a new access token,
because refresh token should be in cookies tagged with `httponly',
then you can send the access token to client side
as a response and set it somewhere.
*/
}
else{
//do something else
}
},[])
this is a simplified code, but should explain well the idea of refreshing a token safely.
also note, i stored access in cookies, but you can do the same and store it in local storage.
Save it in local storage
export const storeToken = async (token: string) => {
await AsyncStorage.setItem('#token', token);
};
And fetch from storage when needed
export const getToken = async () => {
return await AsyncStorage.getItem('#token');
};
You should probably fetch the token from storage when application starts or when fetching from the API and store it in state or such while using the application.
Save in web storage
Only strings can be stored in web storage
LocalStorage
Persists even when the browser is closed and reopened.
Get
const token = localStorage.getItem('token');
Set
localStorage.setItem('token', 'value')
SessionStorage
Data removed when browser closed
Get
sessionStorage.getItem('token', 'value')
Set
sessionStorage.setItem('token', 'value')
You can use LocalStorage, or SessionStorage.
export const useLogin = () => {
const LoginAuth = async (data: AuthenticationProps) => {
await axios.post(baseURL + `client/auth/login`,
{
email: data.email,
password: data.password,
},
{
headers: {
"Content-Type": "application/json",
Accept: "application/json",
}
}
)
.then((res) => {
if(res.status === 200) {
console.log("success")
window.localstorage.setItem('authToken', res.data.token);
// Same as session storage
// window.localstorage.setItem('authToken', res.data.token);
}
}, (err) => {
console.log(err);
})
}
return {
LoginAuth,
}
}
You can check here for the difference
The best way to store the refresh token is in localstorage.
Setting token in localstorage,
localStorage.setItem("token", token);
Getting token from localstorage
let token = localStorage.getItem("token");
You can also view the stored token in browser like below,
All the measures of security of web application logic process conclude by giving you access token and refresh token, then and its your responsibility to keep them safe. As long as these tokens are valid, they are only artifacts required to make an access. In fact if you look at OIDC flows the access token is not even handed to browsers in most of them because of so many known weaknesses of browsers in terms in security.
The best way to keep or store either of these tokens is to deal with them in back channel and if not then in browsers encrypt them with custom logic and store in local storage so that only your app knows how to use these tokens.
Better even to have the backend code does this part for you as you know javascript is always exposed and retrievable.
Hope this helps.

Cypress Login with CSRF token

I would really like to expand on this topic "Logging in with CSRF token" as I have been banging my head against a wall for weeks now and I can't be the only one with this problem. All topics about logging in via POST or logging in with CSRF inevitably lead back to the above link.
Yet the recipes described in this link do not seem to work for me. They all assume that the CSRF token is created once you visit the Login page. But on our site, the CSRF token is only created once you login.
I tested with Postman and there is no CSRF token in the HTML or in the header before you are logged in.
I also tested it in Cypress with the following code:
describe('gimme dat csrf token', () => {
it('try to get the csrf token', () => {
cy.visit(Cypress.env('url'))
cy.getCookie('YII_CSRF_TOKEN')
.then(async (c) => {
cy.log(c.value)
return c.value
})
})
})
This will return an error as there is no YII_CSRF_TOKEN
Type Error
Cannot read properties of null (reading 'value')
If I add a login step before, it will return the value of the CSRF token as expected:
import {Login} from "../../pages/login/Login";
describe('gimme dat csrf token', () => {
it('try to get csrf token', () => {
cy.visit(Cypress.env('url'))
login.loginCredentials(Cypress.env('userEmail'), Cypress.env('userPass')) //added login
cy.getCookie('YII_CSRF_TOKEN')
.then(async (c) => {
cy.log(c.value)
return c.value
})
})
})
Therefore strategies #1 (parse token from HTML) and #2 (parse token from response headers) from the above link can not work.
Recipe #3 is also not feasible as we have several live systems to test and we can't expose a /csrf route
This only leaves us with the strategy #4, which we have been using so far.
Any ideas or are we stuck with adding the "manual" login step to every single spec file?
I think strategies #1 & #2 rely on the browser remembering credentials and supplying them to the login page, as happens with the Stackoverflow page - you don't have to log in every time you visit.
The main difference is you have used cy.visit() instead of cy.request() as shown in the recipes.
If you still are not able to successfully grab the token, try using your login with cy.session(). It will only call the login function once per session.
/*
Enable use of cy.session() and new behavior to handle caching
and restoring cookies, localStorage, and sessionStorage.
*/
Cypress.config('experimentalSessionSupport', true)
describe('...', () => {
beforeEach(() => {
cy.session(() => {
login.loginCredentials(Cypress.env('userEmail'), Cypress.env('userPass'))
})
})
it('try to get csrf token', () => {
cy.getCookie('YII_CSRF_TOKEN')
.then((c) => {
cy.log(c.value)
})
})
})

Handling Firebase auth authentication

I need some help understanding if I am handling the authentication/authorization correctly with Firebase Auth, JS and Python.
Once the user has signed in, I capture the idToken and create a cookie in the browser:
firebase.auth().signInWithEmailAndPassword(email, pass)
.then(({user}) => {
return user.getIdToken().then((idToken) => {
if (idToken) {
document.cookie = "token=" + idToken;
window.location.assign('/profile');
} else {
document.cookie = "token="
}
})
})
.catch((error) => {
//handle error here
});
The route /profile should be protected so I created a decorator and I retrieve the cookie and verify it:
id_token = request.cookies.get("token")
if not id_token:
return redirect(url_for('login'))
try:
decoded_token = auth.verify_id_token(id_token)
uid = decoded_token['uid']
except Exception as e:
print("Exception: {}".format(e))
return redirect(url_for('login'))
This is working so far but I want to see if this is the ideal situation from a security perspective. Also, what about the onAuthStateChanged? How should I handle it in the case above?
Firebase SDKs send the ID token with each request in the Authorization header, so sending it in a cookie is not going to more or less dangerous than that.
Instead of determining the token in signInWithEmailAndPassword though, I'd instead monitor ID token generation by listening to onIdTokenChanged events and using that moment to update your cookie.

How to get JWT cookies in our react application, how to check the user is login or not I am unable to find how to handle my react application session

How to get JWT cookies in our react application, how to check the user is login or not I am unable to find how to handle my react application session.
I really appreciate who helps me out with this problem.
Thanks in advance
The server side API is setting the HTTPOnly cookie which you wont be able to read in JS.
What you need to do it in your react App handle a 401 status error and based on that set a flag isAuthenticated or something as false. Otherwise keep it to be true. With each request to the server HTTPOnly cookie would be sent automatically so you don't need to handle the token inside a cookie.
The backend code needs to send a 401 once the cookie is expired, or the logout is requested or the JWT inside a cookie expires.
Before I say anything, you have included app.use(cookieParser()) in index.js right? Because if not, you're gonna need that once you've installed it with npm i cookie-parser
But anyway, a few things:
You can create a PrivateRoute in React, as far as I'm aware this tends to work well to protect routes from unauthorized users.
You can simply store an isAuthenticated in either the state or localStorage: however this will require that you make absolutely sure that a user shouldn't be able to just change the value in the state or add isAuthenticated in localStorage and just spoof authenticity (this is the part that took me the longest to get right).
Anyway, the way I've handled this is that when a user logs in an access token (and a refresh token if it doesn't already exists) are generated from the server and sent to the client in an httpOnly cookie, while this makes it so that you can't do anything with it on the client side with JavaScript as Pavan pointed out in his answer (which is generally a good thing), you should use res.status for validation when you make the fetch request. Here's what my fetch request kind of looks like:
const login = async (user) => {
const body = JSON.stringify(user);
return fetch(loginURL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
credentials: 'include', //this is important
body: body
}).then(function(res) {
if (res.status === 200) {
const id = Date.now();
localStorage.sid = id; //this is temporary
return res.json()
} else {
return res.json()
}
})
// you can ignore this part
.then(async resp => {
return resp
})
}
Side-note: Your browser automatically handles the httpOnly cookies you send from your server however the credentials: 'include' needs to be included in your subsequent fetch requests.
In one of my main parent components, the login function is called:
login = async (user) => {
this.setState({ error: null });
await adapter.login(user).then(data => {
if (!data.error) {
this.setState({session: "active"})
} else if (data.error && data.error.status === 401) {
// this is so I can handle displaying invalid credentials errors and stuff
this.setState({ error: data.error, loading: false });
}
});
}
I also have a middleware on the server-side that is run before any of the code in the routes to verify that the user making the request is actually authorized. This is also what handles access token expiration for me; if the access token has expired, I use the refresh token to generate a new access token and send it back in the httpOnly cookie, again with a status of 200 so the user experience isn't jarring. This does of course mean that your refresh token would have to live longer than your access token (I haven't decided how long in my case yet, but I'm thinking either 7 or 14 days) though as far as I'm aware that is okay to do.
One last thing, the parent component I referred to earlier calls a validate function which is a fetch request to the server within its componentDidMount so that the user is verified each time the component mounts, and then I've included some conditional rendering:
render() {
return (
<div className="container">
{
!localStorage.sid && <LoginForms {...yourPropsHere}/>
}
{
this.state.loading && <Loading />
}
{
localStorage.sid && this.state.session === "active" && <Route path="/" render={(props) => <Root error={this.state.error} {...props}/>}/>
}
</div>
);
}
I've gone the conditional rendering route as I couldn't get PrivateRoute to work properly in the time that I had, but either should be fine.
Hopefully this helps.
Your login API should return JWT token and how long it should be live.
Your login API response would be like :
{
jwt: your jwt token,
duration: in seconds
}
Use universal-cookies NPM to store this result into cookies.
For more details how to manipulate with cookies, visit
https://www.npmjs.com/package/universal-cookie
For setting cookies your code like:
const cookies = new Cookies();
cookies.set(name of cookies, jwt value from API call, {
maxAge: duration,
});
Above code store the jwt cookies in browser and after maxAge automatically remove it from browser.
So for identifying session is present or not, you should check after specific interval cookies has present in browser or not. If cookies has present in browser then session is on, otherwise session has expired.

How do I refresh an access token Instagram when it has expired?

I use nodejs as a framework on the server side. I have successfully retrieved access_token data and used it.
but suddenly when I tried to request API it was rejected and after replacing the access_token it work again.
so as stated by the Instagram: Access tokens may expire at any time in the future.
how long access_tokens will be expire ?
Here my API request
export const getMediaRecent = (req,res)=>{
axios.get(`https://api.instagram.com/v1/users/self/media/recent/?access_token=${keys.instagram.access_token}`)
.then(result=>{
let imageInstagram = result.data.data.filter((d,i)=> i < 10).map(r=>{
return{
images:r.images,
caption:r.caption,
link:r.link
}
});
return res.status(200).json(imageInstagram);
})
.catch(err=>{
return res.status(400).json(err);
});
}
is there a way to refresh an access token when it has expired?

Categories

Resources