FirebaseAuth with SvelteKit on +page.ts load - firebase-authentication

I have a SvelteKit app and am using Firebase and Node to do simple Google SSO auth. I am using an API that requires the IDToken of the currently signed in user to authenticate requests. Ideally I'd like to use the +page.ts load function to load in the data, something like this:
export const load = (async () => {
// Get user, token
const auth = getAuth();
const user = auth.currentUser;
const token = await user?.getIDToken();
if (!token) throw error(401, "Could not authenticate");
// Use token to get data needed to load page
const data = api.requestData(token);
return { data };
}) satisfies PageLoad;
export const ssr = false;
The issue is that user is always null when this function executes. I imagine this is because this is called before the page loads and Firebase hasn't had a way to access the session and get the current user.
My question is, what approach do I take to solve this without simply requesting the data after the page is rendered? Is there a way to authenticate the user server side? Thanks so much.

Related

res locals being wiped from _app.tsx in NextJS

I am working on a NextJS app that integrates with passport for authentication. I have it configured so that on login it links to a 3rd party auth service (keycloak - shouldn't be important) and then on success back to a callback url.
The session exists on req.session and I have a root level middleware ('/') to append the req.session.user to res.locals.user
In the custom _app.tsx file I have a getInitialProps that I can extract out ctx.res.locals.user and logging it to console all data is as expected.
The problem is when I pass it as part of pageProps returned from getInitialProps, the same user data is undefined when console logged within JSX.
Here's how the code looks:
function MyApp({Component, pageProps}: AppProps<{user: any}>) {
const { user } = pageProps;
return (
<div>
{console.log(user)} // this is 'undefined' in browser console
{/* ... */}
</div>
);
}
MyApp.getInitialProps = async (appContext: AppContext) => {
const pageProps = await App.getInitialProps(appContext);
const user = appContext.ctx.res.locals.user;
console.log(user) // logs the correct user information in the terminal
return {
pageProps: {
...pageProps,
user,
}
}
};
I am checking the logs on the home-page of app (pages/index.tsx), which has its own getServerSideProps call so I am wondering if this is the point where the res.locals is being cleared?
Questions:
Should I be able to pass res.locals from server to client like this?
If yes, can you see what I'm doing wrong
If not, how should user data returned in req.session be made available from server to client in NextJS?

React hooks and getStaticProps

I want to access hooks from getStaticProps to get the accessToken of the logged user which is saved in AuthContext. but this is impossible
I am using getStaticProps of Nextjs to pre-render pages, this is an example :
export async function getStaticProps() {
const result = await axios.get("http://127.0.0.1:5000/track/landing-tracks-for-admin");
return {
props: {
tracks: result.data,
},
revalidate: 5,
};
}
the problem is that this request needs the user accesToken which I can get from useAuth
import useAuth from "../../hooks/useAuth";
but when I try to call it in getStaticProps like this : const auth = useAuth();
I get this error
React Hook "useAuth" is called in function "getStaticProps" that is neither a React function component nor a custom React Hook function
any other alternatives ?
I guess you cannot authenticate the user like this. Please follow the documentation on Nextjs for authentication.
There is also an example repository for static page authentication using with-iron-session.
I suggest that you implement the user Authentication the nextjs way since you are using nextjs

React Query uses outdated headers (old JWT token) after Keycloak refresh token

I'm pretty new in React-Native programming, but here is the context.
We are using React Query and Axios libraries in our project. As AuthManager we are using Keycloak and for the library managing auth status we have React Native Keycloak. We encounter a tedious problem with our server responding randomly 401 at our requests after a certain amount of time, bringing also to the app crash sometimes.
We reproduced the error making the Bearer Token of Keycloak expire after only 1 minute. This caused almost immediatly the 401 error and we wondered why this is happening.
Let's say we have a screen with some "Activities" and this screen is the first thing the user will see. For handling requests, in our code we use some custom hooks that reference useQuery, for example:
export function useActivities(): UseQueryResult<ActivityList> {
const { headers } = useHeaders();
return useQuery(
['activities', today.start],
() => getActivitiesList(headers), // Note 1
{
enabled: !!today.start,
}
);
}
The important point of it is that we useHeaders to get our updated headers with the Keycloak token and our realm settings. useHeaders is almost everywhere in our app.
export function useHeaders(): UseHeaders {
const { keycloak } = useKeycloak();
const KEYCLOAK_REALM = remoteConfig().getString('KEYCLOAK_REALM');
const headers = {
Authorization: `Bearer ${keycloak?.token}`,
Realm: KEYCLOAK_REALM,
};
return { headers };
}
Now, the getActivitiesList is simple as five:
async function getActivitiesList(headers: UseHeaders['headers']): Promise<ActivityList> {
const url = `${BASE_URL}${VERSION}/activities/grouped?end=${end}&start=${start}`;
// Note 2
return axios
.get(url, { headers })
.then((res) => res.data)
.catch((e) => console.error('Error fetching grouped activities:', e));
}
The problem with all of that is that whenever Keycloak will trigger the refresh token, the token inside keycloak object is changed, the headers inside useActivities are changed BUT if I print the headers inside my getActivitiesList (// Note 2), or even inside the query function (// Note 1), headers will not be updated. Sometimes it just causes to make two requests (one that fails and show error, the other one actually working), some other times the app crashes without any explain. This makes me wonder why the query function will not update its headers and passed the "old" headers inside getActivitiesList.
For now we are mitigating this problem in two different points.
After keycloak init, we pass immediatly to axios a global header with axios.defaults.headers.common.Realm = KEYCLOAK_REALM;
After receiving a valid token from Keycloak, we overwrite the Authorization header with a new one: axios.defaults.headers.common.Authorization = 'Bearer ${keycloak?.token}';
This is not a perfect solution and we are here to search some info about this problem.
Someone get something similar? How to manage the token refresh in useQuery function?

how do i authenticate my vuejs app using azure active directory and get the security groups to which the user belong to

i saw something similar here: How do you authenticate a VueJS app with Azure AD?
but it did not work for me...
my problem is that after authenticating the user at login - i still needed to get the users security groups and that information was not received using the graph-api described in the above mentioned post
thank you for any help
it was something that took me a long time to figure out so im posting my findings here, hopfully this will help someone:
this was a hard one for me so im posting here - hopfully this will save some time to someone:
my problem was that i need not only to authenticate my vuejs app with azure-ad, but i need also to get the security groups to which the user is belonging to.
to achive this, this is what i done:
i used the vue-adal sample app mentioned above( you can find it in: https://github.com/survirtual/vue-adal ) - under sample folder.
but i still had to make some changes to make it behave the way i need. the problam was that after logging in with my user the sample app used windows.net graph api for retrieving user info with the token from the user authentication, so i had to change in main.js this:
const graphApiBase = `https://graph.windows.net`
const graphApiResource = '00000002-0000-0000-c000-000000000000'
to this:
const graphApiBase = `https://graph.microsoft.com/v1.0`
const graphApiResource = '00000003-0000-0000-c000-000000000000'
in addition, inside the return url component i had to change the axios query to get the security groups to which the user belongs to..so i changed this (in the home.vue file):
async getUserInfo () {
let res = await this.$graphApi.get(`me`, {
params: {
'api-version': 1.6
}
})
to this:
async getUserInfo () {
let res = await this.$graphApi.post(`/me/getMemberGroups`, {
securityEnabledOnly: true
})
console.log(res)
return res.data
}
and then the data that i received back from the api contained the security groups to which the user belongs to...

How to define a Role without login page in TestCafe?

I'm having trouble to make my tests stable with this code.
Can someone give me some directions
My app doesn't have a login page, and users might get logged in different ways, what determines if they're clogged or not is an auth cookie.
So I decided to define a role by a direct API call to the authentication API then save a cookie with the user token:
export const adminUser = Role('any-page', async t => {
const loginRequest = await fetch(
`https://my-auhtentication-api/oauth/token?grant_type=password&username=${userName}&password=${password}`,
{
method: 'POST',
}
)
const loginToken = await loginRequest.json()
await ClientFunction(() => {
document.cookie = `myAuthCookie=${loginToken.access_token}; Domain=.my-app-domain.com; Path=/`
})
})
And then I use in my code test like this:
fixture('[Admin User] Menu navigation')
.beforeEach(async t => {
await t
.useRole(adminUser)
.navigateTo(strictPage)
})
The problem is that it works fine when I run only this test, but when I run it with the rest of my stack it becomes very unstable, sometimes works, sometimes not.
For end-to-end tests, it's better if your test environment is the same as your production environment. Your comment indicates that you use a parent web app to authenticate your users in the production environment, so the best way to test authentication is using the same parent app in your test environment.