Sapper.js - Preload with cookies/headers? - javascript

I'm using Sapper.js to power my application but only using the static content created by running sapper export. So there is no server rendering the pages.
I'm using AWS CloudFront with Lambda#Edge to perform authentication on the user's HttpOnly cookies whenever they request a page. If the user is authenticated, Lambda will then fetch user data such as the user's profile picture, username, etc and set these values in custom headers/cookies (non HttpOnly) on the pages returned by CloudFront.
These values can be set in either headers or cookies, there are no requirements for either.
But I need to have this dynamic content available to the client before the page is rendered in order to avoid an ugly flash of empty content. So it should be retrieved inside of sapper's preload function instead of onMount in order to stall any other html from being rendered until the data is returned.
I know how to fetch inside of the preload function like so:
<script context="module">
export async function preload(page, session) {
const res = await this.fetch("SOME_ENDPOINT");
const data = await res.json();
return {data};
}
</script>
but I'm not sure on how to get access to headers or cookies from within this function.
EDIT: NEW APPROACH?
So I've been thinking and it seems like the best way to go at this point is to try and transform Sapper's sapper.middleware function so that it accepts a custom req object and returns the res object instead of trying to serve it to the client.
Then we can run npm run build and use the entire build directory inside of Lambda. We're free to pass any user data into the middleware session obbject afterwards as it explains in the docs:
sapper.middleware({session: (CUSTOM_REQ, CUSTOM_RES) => ({user: CUSTOME_REQ.user})})
No need to fetch any data as it should now be available in the store.
Any thoughts?

You can pass { credentials: true } as the second option to this.fetch (same as regular fetch):
export async function preload(page, session) {
const res = await this.fetch("SOME_ENDPOINT", {
credentials: true
});
const data = await res.json();
return {data};
}
This will cause cookies to be sent with the request. By definition though, this won't work with exported apps — the response must be constructed per-user.

Related

Skip authentication on a specific GraphQL query

I have a GraphQL API (TypeScript, Express, apollo-server), which is being consumed by a client app. All requests require authentication by validating the JWT token like this:
return new ApolloServer({
schema,
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
context: async ({ req }) => {
const user = await validateJWT(ctx, req)
return { ...ctx, user }
},
})
(Ignore ctx, it's an implementation specific detail.)
My problem here is that I need to allow a specific query to be unauthenticated. During the onboarding, the client is fetching data before the user is even created.
type Query {
onboardingData(profile: ProfileInput!): OnboardingData!
...
}
What is the appropriate way of bypassing authentication for a particular query?
I've looked into using
import { parse, print } from 'graphql'
to get the query from req.body.query and then do string-matching, but that feels janky, to say the least. My Spidey-senses are tingling that it's prone to errors, confusion and potential vulnerabilities.
In a REST world, I would just specify a particular path to be excluded.
You can get you context to return the function that gets your user, instead of getting the user in the context level and returning it to the resolvers. Wrap your context body in a function and return the function. Then on your resolvers that require authentication and / or the current user, you simply call it, similar to the way you call it in the context body.
Example:
const user = await validateJWT()
Or better named:
const user = await getCurrentUser()
This approach gives you flexibility to only call it on resolvers that require authentication.

Route that is executed within another route in Node.js is not being executed

Good Evening,
I have a function that contains a route that is a call to the Auth0 API and contains the updated data that was sent from the client. The function runs, but the app.patch() does not seem to run and I am not sure what I am missing.
function updateUser(val) {
app.patch(`https://${process.env.AUTH0_BASE_URL}/api/v2/users/${val.id}`,(res) => {
console.log(val);
res.header('Authorization: Bearer <insert token>)
res.json(val);
})
app.post('/updateuser', (req, ) => {
const val = req.body;
updateUser(val);
})
app.patch() does NOT send an outgoing request to another server. Instead, it registers a listener for incoming PATCH requests. It does not appear from your comments that that is what you want to do.
To send a PATCH request to another server, you need to use a library that is designed for sending http requests. There's a low level library built into the nodejs http module which you could use an http.request() to construct a PATCH request with, but it's generally a lot easier to use a higher level library such as any of them listed here.
My favorite in that list is the got() library, but many in that list are popular and used widely.
Using the got() library, you would send a PATCH request like this:
const got = require('got');
const options = {
headers: {Authorization: `Bearer ${someToken}`},
body: someData
};
const url = `https://${process.env.AUTH0_BASE_URL}/api/v2/users/${val.id}`;
got.patch(url, options).then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
Note: The PATCH request needs body data (the same that a POST needs body data)

React: Store JWT

I have a backend where the user gets a JSON Web Token if he is logged in successfully. This token is needed for other API calls and I want to store it application-wide to access it from every point in the application. What is the best point to store it and access it from anywhwere?
You can store it on local storage on login like
export async function login(username, password) {
const { data: jwt } = await http.post(apiEndpoint, { username, password });
localStorage.setItem("token", jwt.token);
}
and than you can access it every time like
export function getCurrentUser() {
try {
const jwt = localStorage.getItem("token");
return jwtDecode(jwt);
} catch (error) {
return null;
}
}
I have used widely this approach and works like charm.
I'd highly suggest not to store in localStorage. I'd highly recommend cookies.
See this link.
In addition, also See another link.
Storing JWT token in localstorage or session storage of a browser is not preferable, as it can be accessed easily by anyone who has even a little knowledge of browser developer options (especially developers like us).
I suggest you use http only cookie to store them, that way it can be accessed whenever you send a HTTP request.
You can read about it more - https://blog.logrocket.com/jwt-authentication-best-practices/

How to get response header from useQuery of Apollo Client

I haven't been able to find a way to do this at all. Does anyone know if this is supported? Thanks.
ApolloClient's methods for making requests, and the React Hooks that use them, serve as an abstraction over how the data is actually fetched. It could come from a remote server over HTTP, from the cache, from directly executing the request against a schema, etc. As a result, they don't expose any information regarding how the data was fetched in the first place, including transport-specific information like HTTP headers.
If you need to access this information, the appropriate place to do so would be inside a Link that you'd prepend to your HttpLink -- either an existing one like a ContextLink or ErrorLink, or some custom Link you roll yourself. If you're doing this in an error-handling context, then ErrorLink would be your best bet, as suggested in the comments.
HttpLink injects the raw response from the server into the context object used by all Links (see here). Assuming you're using the default fetch API as the fetcher, this response will be a Response object.
So you can do something like this:
const link = onError(({ graphQLErrors, networkError, operation }) => {
const { response } = operation.getContext();
const { headers, status } = response;
// do something with the headers
});

How do I intercept an API call and display data from it using a UserScript?

There's a webapp that makes a request (let's call it /api/item). This request returns a json body with a field called itemData which is normally hidden from the user, but I want to make that shown.
So how do I make a userscript that listens for the request at /api/item and displays the itemData field?
For reference the way the webapp is making the request is:
return Promise.resolve(new Request(e,r)).then(sendCookies).then(addLangParam).then(addCacheParam).then(addXsrfKey).then(checkZeroRating).then(function(e) {
return fetch(e)
}).then(checkStatus).then(checkApiVersionMismatch).then(checkApiResponse)
Most of that is irrelevant, but the important part is Request (I think).
This webapp is not using XMLHttpRequest, but the Fetch API.
You can use the fetch-intercept npm module to intercept fetch requests. Example code:
import fetchIntercept from 'fetch-intercept'
fetchIntercept.register({
response(response) {
console.log(response)
return response
}
})
Do you have access to the promise returned ?
If so, then you may add another "then".
Otherwise, you may overwrite "checkApiResponse"

Categories