Is there a way to fetch data only when token Is available? - javascript

Good day all,
I have a little issue in my react code and I hope someone here would be able to help me fix it.
Basically I am fetching data from an endpoint (multiple)
Am using axios and react query.
In my get request, I have security headers and one is Authorization.
User is authorized by a token sent to session storage when he / she login.
I get the token from session storage and place it in my authorization header.
My problem now is before the user login, the token === null
and my get request runs before the user logins leaving it with an empty token.
When the user finally logins, no data is fetched because what is in the authorization header is null but when I refresh the page, the data is fetched because at that time the token was already delivered and not = null.
I don't know if you fully understand cause am pretty bad at explaining but if you do, is there anyway to fix the issue.
thanks...

If you're using RTK, you may use skip:
const [skip, setSkip] = useState(true);
const { isUninitialized, isSuccess, isLoading } = useSomeQuery(token, {
skip
});
and set skip to false when the token is available

since the token is a dependency to your fetch, it should go to the queryKey. Then you can disable the query via the enabled option for as long as the token is null.
const token = useTokenFromLocalStorage()
useQuery(
["user", token],
() => fetchUser(token),
{
enabled: !!token
}
)
if the token updates (changes from null to a value), and that re-renders your component, the query will run automatically because the query key changes. The query will not run if the token is null.

Related

Axios sending double CSRF-Token [CSRF-Token mismatch]

I have a basic laravel/vue with sanctum implementation. The problem is straight forward:
I'm sending a token-request and logging-in the user. The server sends back a new token. Axios adds this token, but adds another token that is always the same and expired.
Code:
await APIClient.get("/sanctum/csrf-cookie")
return APIClient.post("/api/user/login", payload);
DevTools/Network tab:
csrf-cookie request => response-headers has the valid XSRF-TOKEN
login request => request-headers, SET-COOKIE property has XSRF-TOKEN (old expired value) ; laravel_session ; XSRF-TOKEN (new valid value)
My problem is with the old value. There's no place in my code for this to be added at all.
This's my axios client
const APIClient = axios.create({
baseURL: constants.PATHS.url,
withCredentials: true, // required to handle the CSRF token
});
Your help is much appreciated.
Sorry I don't quite understand your question, do you not know how to add (old expired value) or (new valid value) to the header of axios?
I recommend using axios as a property of window. It is easier to change its properties:
window.axios.defaults.headers.common['token'] = "aaa"; //add a header
I have found an actual solution. You should clear the cookies of the document whenever you logout, in this way, the token will always be the new one and the old one cannot be duplicated. I've seen this bug happens in some cases.

SvelteKit Hook Prevents Endpoint Request

Trying out SvelteKit and I'm having a hard time with hooks. The docs don't really seem to explain it all too well. My current understanding of hooks is that they basically allow you to interact with requests made to your server before they get to their destination? (I'm open to a better explanation - specifically the handle hook).
My current issue is I made an endpoint called login. As the name suggests, it allows users to sign into my application by generating a token and storing it as a cookie on their client. This works until I add hooks. After reading the hooks description, I figured the handle hook is perfect for what I want to do - validate the token on each request - if invalid, reroute the user to the login screen, if valid, allow the request to continue uninterrupted.
export const handle: Handle = async ({ event, resolve }) => {
const isLogin = event.url.pathname.startsWith('/login')
const cookies = cookie.parse(event.request.headers.get('cookie') || '');
const token = cookies['token']
if (!token) {
if (!isLogin) {
return Response.redirect(`${event.url.origin}/login`)
}
return await resolve(event)
} else {
try {
await verifyToken(token)
if (isLogin) {
return Response.redirect(`${event.url.origin}/about`)
}
} catch (err) {
return Response.redirect(`${event.url.origin}/login`)
}
}
return await resolve(event)
};
This does not work as expected. When I initiate the request to the api/login endpoint, the request does not seem to make it there. I have console.logs all over the endpoint but no messages were outputted to the terminal & when I check the application storage, no new cookie was added.
What am I missing about hooks?
Why is it not passing the request off to the endpoint?
Any idea how I can fix this?
The handle hook runs for every request—including endpoints.
When you fetch /api/login without a token, your hook will redirect the request to /login since isLogin === false. You need to allow through every route that should be accessible without a login, for example:
const isLogin = /^\/(api\/)?login$/.test(event.url.pathname)

php jwt login storage

I'm building a web app with CodeIgniter 4 where I implemented a REST API for register and login and a profile page just a test my login.
I have a login form that sends a javascript fetch post request to my api and I receives a jwt token. This is working.
Now I am in the situation where I think I did not understand the principle.
I want that the user stays logged in and doesn't need to re-login every time. The token expires after 12h.
And I want to use php (if possible) as the entire app runs on php.
Currently, I have a little javascript function to store my token:
const store = {};
store.setJWT = (data) => {
this.JWT = data;
};
But this is not secure against page reload.
Additionally I am creating a cookie with php, when the user logs in:
helper('cookie');
set_cookie([
'name' => 'login',
'value' => $token,
'expire' => $exp,
'httponly' => true,
'secure' => true,
]);
I am able to fetch data from the API using the cookie or the store object.
const token = getCookie('login');
const res = await fetch('/profile', {
headers: {
'Authorization': `Bearer ${token}` // or store.JWT
}
});
So.... what I want is:
The user goes to a protected url e.g. https://myapp.com/profile and if he is logged in, he has access. If not, he gets redirect to the login page.
Is using the cookie to store the jwt a good idea? Or did I completely misunderstood the idea of JWT and it is not used to be used for a login?
Additionally: I still don't know if biulding the login as an API was the best idea.
First of all there is nothing wrong with building "login with API". It is common practice. And JWT is perfectly suited for auth.
You sure can store JWT token inside a cookie, but it is a little bit wrong in my opinion. Usually JWT tokens are stored in the local storage on the client side. It will persist after page reload.
Set token in the local storage:
localStorage.setItem('token', token);
Get token from the local storage:
token = localStorage.getItem('token');
To better understand the conception of the JWT you can copy some token (without Bearer) and paste it in jwt.io. Basically JWT contain all the required information about the user and server can trust this information.
And when you set the cookie like this
set_cookie([
'name' => 'login',
'value' => $token,
'expire' => $exp,
'httponly' => true,
'secure' => true,
]);
It is an overkill. JWT possible already contains all this information and you can extract it from the token.

firebase provider authentication refresh flow

I am trying to use firebase authentication to get a google access token in order to call the youtube API.
I am able to initially get the access token like this:
const provider = firebase.auth.GoogleAuthProvider();
cosnt scopes = []; // ...scopes
scopes.forEach(scope => provider.addScope(scope));
firebase.auth().signInWithPopUp(provider)
.then(userCredentials=> {
const oauthCredentials = userCredentials.credentials;
// using credentials for API calls
axios.get("some-google-api-path", { params: { access_token: oauthCredentials.accessToken } }); // and so on...
});
This works fine until the access token expires.
As far as I can tell, it seems like firebase automatically refreshes the session, but I can find a way to get the new access token.
I tried:
firebase.auth().onAuthStateChange(user => {
// could not find the access token on the user object
});
And since that failed, I tried to do it manually using:
const token = firebase.auth.GoogleAuthProvider.credential(
oauthCredentials.idToken,
oauthCredentials.accessToken
);
const authResult = await firebase.auth().signInWithCredential(token);
The issue is that authResult will only contain the idToken or the accessToken, but not both, depends on what I give the firebase.auth.GoogleAuthProvider.credential function.
Am I missing something?
Is there another/better way to do it?
There are two tokens in play here:
The refresh token, which is only available after the user actively signs in. Since it doesn't change, you can cache this token - which is what the Firebase SDKs actually also do.
The ID token, which is available at any moment from the current user. This token expires in an hour, and is auto-refreshed by the Firebase SDKs.
So if you want to retain access to the refresh token, you will have to store (typically in local storage) yourself when the user first actively signs in.
So apparently firebase doesn't refresh tokens of other providers for you (not event google) according to this (thank you Frank van Puffelen!)
So what I did instead, is authenticate manually to google (since I use react, I used react-google-login), and got all tokens from there.
Then, once the session is initiated/refreshed, I create a firebase session using:
const token = firebase.auth.GoogleAuthProvider.credential(
oauthCredentials.idToken,
oauthCredentials.accessToken
);
const authResult = await firebase.auth().signInWithCredential(token);
I hope that this helps anyone, and I will accept another answer if firebase ever changes this.

Best way to set timer in React/Redux for sending refresh auth token

I'm using the access refresh jwt authentication flow and want the client to send a refresh token to get a new access token every 10 minutes after it received the access token. I also want to make sure that if the user closes their laptop for an hour and comes back to it that a new access token request is also sent. What is the best way to implement this behavior in React/redux, where the user will always have a valid access token and seamlessly keeps their "session" going?
To achieve that you can conditionnally check the expiration of the token before each request instead of setting a timer.
This will depend on the way you communicate with the server but the idea is to store client side the jwt token, his expiration and the refresh token (and his expiration too if needed) then use some sort of middleware before each request that need the auth :
const authClientMiddleware = (done) => {
if (new Date(localStorage.getItem('expiration')) <= new Date()) {
getNewToken(localStorage.getItem('refreshToken')).then(() => done()).catch(() => logout());
} else {
done();
}
}

Categories