S
SolidJS•3mo ago
NitonFx

Keeping data and code secure and on the server (restrict code to initial render)

Hi, I have questions regarding SSR & "use server"; What already works: 1. OAuth authentication where the client only has a uuid session but not any of the OAuth keys/tokens 2. When the client fetches a page, server action ... the server resolves the OAuth tokens based on the session UUID What i want to archive: 3. I can use the OAuth tokens to fetch data for components only on initial server render without exposing tokens to the client or code that deals with theese tokens
16 Replies
NitonFx
NitonFxOP•3mo ago
What i currently have is this inside a component
const authentication = useAuthentication();
const authentication = useAuthentication();
and use Authentication is
export function useAuthentication(): Accessor<UserData> {
const fallbackUserRoles: UserData = { roles: [], user: null };
const fetchUser = async () => {
const idToken = await getUserIdentity();
if (idToken === null) {
return fallbackUserRoles;
}
return {
user: idToken.name,
roles: [],
} satisfies UserData;
};
const res = createAsync<UserData>(fetchUser, {
deferStream: true,
});
return () => res() ?? fallbackUserRoles;
}
export function useAuthentication(): Accessor<UserData> {
const fallbackUserRoles: UserData = { roles: [], user: null };
const fetchUser = async () => {
const idToken = await getUserIdentity();
if (idToken === null) {
return fallbackUserRoles;
}
return {
user: idToken.name,
roles: [],
} satisfies UserData;
};
const res = createAsync<UserData>(fetchUser, {
deferStream: true,
});
return () => res() ?? fallbackUserRoles;
}
Where getUserIdentity() is a use server my problem is that 1. this code exposes the identity token to the client 2. Authentication related code is in client alto it wont and should never be used Making useAuthentication "use server" doesn't work since the function cannot be serialized While i can add the mapping from idToken to UserData to the server (getUserIdentity) i cannot remove the dead code from the frontend I would search for a solution where the server just returns the HTML with all the values already inlined (this already works!) but without the code that makes it dynamic since its not the clients concern So i suppose i am searching for something "use init"; or something along the lines
Brendonovich
Brendonovich•3mo ago
what about this?
const fallbackUserRoles: UserData = { roles: [], user: null };
const fetchUser = async () => {
"use server";
const idToken = await getUserIdentity();
if (idToken === null) {
return fallbackUserRoles;
}
return {
user: idToken.name,
roles: [],
} satisfies UserData;
};

export function useAuthentication(): Accessor<UserData> {
const res = createAsync<UserData>(fetchUser, {
deferStream: true,
});
return () => res() ?? fallbackUserRoles;
}
const fallbackUserRoles: UserData = { roles: [], user: null };
const fetchUser = async () => {
"use server";
const idToken = await getUserIdentity();
if (idToken === null) {
return fallbackUserRoles;
}
return {
user: idToken.name,
roles: [],
} satisfies UserData;
};

export function useAuthentication(): Accessor<UserData> {
const res = createAsync<UserData>(fetchUser, {
deferStream: true,
});
return () => res() ?? fallbackUserRoles;
}
NitonFx
NitonFxOP•3mo ago
I mean thats what i meant with
While i can add the mapping from idToken to UserData to the server (getUserIdentity) i cannot remove the dead code from the frontend
But yes that hides the token from the client (altho the code is still in the sourcemap but thats ok for dev) While it leaves the dead code in the frontend this would be ok if no other solution exists
Brendonovich
Brendonovich•3mo ago
which dead code?
NitonFx
NitonFxOP•3mo ago
everything inside useAuthentication() should never be called on the client the user can/will never change besides on page reload
Brendonovich
Brendonovich•3mo ago
could use middleware but i wouldn't recommend it
NitonFx
NitonFxOP•3mo ago
Or do i understand it wrong and useAuthentication will also be called by the client?
Brendonovich
Brendonovich•3mo ago
yeah useAuthentication will be called on the client Start only does SSR for the initial page load, in the browser it becomes an SPA
NitonFx
NitonFxOP•3mo ago
but it doesn't cause a RPC call because of solid magic?
Brendonovich
Brendonovich•3mo ago
on initial load yeah it reuses the server data instead of doing another fetch
NitonFx
NitonFxOP•3mo ago
Hmmm ok because the problem i have is i have an api client this api client object contains the api key (oath access token) i wanted to do something like
const myClient = useMyClient();
<div>{myClient.getFooBar("lorem ipsum")}</div>
const myClient = useMyClient();
<div>{myClient.getFooBar("lorem ipsum")}</div>
the problem i dont know how to do this without exposing the apiKey from the myClient to the frontend ofc i could write a own use server function for every method in MyClient but i am looking at an autogenerated API client with ~100 API endpoints & growing of which i use quite a few
Brendonovich
Brendonovich•3mo ago
are you using cookies or something to authenticate the client to the backend instead of the api key?
NitonFx
NitonFxOP•3mo ago
Yes As I said I have a session cookie and only the backend knows the access token Backend being solid start server
Brendonovich
Brendonovich•3mo ago
i think i'd try use one server function that wraps the api client and use some typescript magic to do something like apiClientServerFunction('getFooBar', ['lorem ipsum']) or is it possible to use the api client in the browser and provide a custom transport that resolves the api key on the backend
NitonFx
NitonFxOP•3mo ago
Oh like a path /api/myapi/[...path] With like a generic handler that replaces the session with API key and relays it to the real API? Altho I probably would have to be quite careful since well requests have quite a lot to them🤔 Or is the mechanism you mean something different? But there is no way in general to render something initially on the server and leave it static once on the client I am not saying that it is unreasonable to not have such a feature given the already high complexity of meta frameworks
Brendonovich
Brendonovich•3mo ago
you could use NoHydration but it's a bit extreme
Want results from more Discord servers?
Add your server