Express cookie parser is not creating cookies in production - next.js

I am using graphql-yoga and I am writing cookies that access the Microsoft Graph API. On the front end I have an apollo client with NextJS set up and it is working perfectly... in development. When I deploy the server there is no recognition of the cookies from the front end at all. In my reading I think this has something to do with NextJS being server rendered (even though when I run next build it says static build...) I am certain the problem is somewhere in here (I am leaving the comments in, to show all of the places I tried to set the credentials to 'include')
export default function createApolloClient(initialState, ctx) {
// The `ctx` (NextPageContext) will only be present on the server.
// use it to extract auth headers (ctx.req) or similar.
return new ApolloClient({
ssrMode: Boolean(ctx),
link: new HttpLink({
fetch,
uri: process.env.NODE_ENV === 'development' ? endpoint : prodEndpoint,
credentials: 'include', // Additional fetch() options like `credentials` or `headers`
fetchOptions: {
credentials: 'include',
},
// request: operation => {
// operation.setContext({
// fetchOptions: {
// credentials: 'include',
// },
// });
// },
}),
connectToDevTools: true,
// credentials: 'include',
cache: new InMemoryCache().restore(initialState),
});
}
The other answers to this all involved CORS, but I have CORS set up on my my GraphQL-Server:
const opts = {
debug: process.env.NODE_ENV === 'development',
cors:
process.env.NODE_ENV === 'development'
? {
credentials: true,
origin: ['http://localhost:3000'],
}
: {
credentials: true,
origin: [
'...'
],
},
};
server.start(opts, () =>
console.log('Playground is running on http://localhost:4000'),
);
Can anyone point me in the right direction? Am I right to be looking at the ApolloClient portion of my front end? Thanks in advance.

This was staring me in the face, but they warnings were being drowned out in the console. Cookies need to be set with in Chrome.
{
...,
sameSite: false,
secure: true
}
The console had these links to provide insight:
https://www.chromestatus.com/feature/5088147346030592
https://www.chromestatus.com/feature/5633521622188032
This is a very recent change in Chrome, and I only realized that there was a difference because I randomly opened my site in Firefox, and it worked.

Related

NextJS Actioncable Proxy

So I'm trying to do two things at the same time and it's not going too well.
I have a NextJS app and a Rails API server this app connects to. For authentication I'm using a JWT token stored in an http-only encrypted cookie that the Rails API sets and the front end should not be touching. Naturally that creates a necessity for the frontend to send all the api requests though the NextJs server which proxies them to the real API.
To do that I have set up a next-http-proxy-middleware in my /pages/api/[...path] in the following way:
export const config = { api: { bodyParser: false, externalResolver: true } }
export default function handler(
req: NextApiRequest,
res: NextApiResponse
) {
httpProxyMiddleware(req, res, {
target: process.env.BACKEND_URL,
pathRewrite: [{ patternStr: "^/?api", replaceStr: "" }],
})
}
Which works great and life would be just great, but turns out I need to do the same thing with ActionCable subscriptions. Not to worry, found some handy tutorials, packed #rails/actioncable into my package list and off we go.
import {useCurrentUser} from "../../../data";
import {useEffect, useState} from "react";
const UserSocket = () => {
const { user } = useCurrentUser()
const [roomSocket, setRoomSocket] = useState<any>(null)
const loadConsumer = async () => {
// #ts-ignore
const { createConsumer } = await import("#rails/actioncable")
const newCable = createConsumer('/api/wsp')
console.log('Cable loaded')
setRoomSocket(newCable.subscriptions.create({
channel: 'RoomsChannel'
},{
connected: () => { console.log('Room Connected') },
received: (data: any) => { console.log(data) },
}))
return newCable
}
useEffect(() => {
if (typeof window !== 'undefined' && user?.id) {
console.log('Cable loading')
loadConsumer().then(() => {
console.log('Cable connected')
})
}
return () => { roomSocket?.disconnect() }
}, [typeof window, user?.id])
return <></>
}
export default UserSocket
Now when I go to load the page with that component, I get the log output all the way to Cable connected however I don't see the Room Connected part.
I tried looking at the requests made and for some reason I see 2 requests made to wsp. First is directed at the Rails backend (which means the proxy worked) but it lacks the Cookie headers and thus gets disconnected like this:
{
"type": "disconnect",
"reason": "unauthorized",
"reconnect": false
}
The second request is just shown as ws://localhost:5000/api/wsp (which is my NextJS dev server) with provisional headers and it just hangs up in pending. So neither actually connect properly to the websocket. But if I just replace the /api/wsp parameter with the actual hardcoded API address (ws://localhost:3000/wsp) it all works at once (that however would not work in production since those will be different domains).
Can anyone help me here? I might be missing something dead obvious but can't figure it out.

Apollo Client can't query in getServerSideProps but works on client [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed last month.
Improve this question
In our Nextjs app we are connecting to a different domain for Apollo Server, queries work fine on the client with cors options and everything. However same queries fails with network fetch error inside getServerSideProps.
I have tried changing headers on the createApolloClient and using fetch with isomorphic-unfetch however there is no response from server side calls to the graphql server.
Our apollo client code is same as the nextjs example on here except the httpLink part we use it like the following
const httpLink = new HttpLink({
uri: 'http://localhost:3000/graphql',
fetchOptions: {
mode: 'cors',
},
credentials: 'include',
fetch: enhancedFetch,
});
and this is the server configutaion
const server = new ApolloServer({
schema,
validationRules: Meteor.isDevelopment ? [depthLimit(10)] : [NoIntrospection, depthLimit(10)],
context: async ({ req }) => ({
user: await getUser(req.headers.authorization),
}),
cache: 'bounded',
uploads: false,
plugins: Meteor.isDevelopment ? [ApolloServerPluginLandingPageGraphQLPlayground({})] : [],
csrfPrevention: true, // Enable CSRF prevention
});
async function startApolloServer() {
await server.start();
server.applyMiddleware({
app: WebApp.connectHandlers,
path: 'http://localhost:3000/graphql',
cors: {
origin: 'http://localhost:4000',
credentials: true,
},
});
}
When NextJS sends a query form getServerSideProps it receives an error as the following
{
"name": "ApolloError",
"graphQLErrors": [],
"clientErrors": [],
"networkError": {
"cause": {
"errno": -61,
"code": "ECONNREFUSED",
"syscall": "connect",
"address": "::1",
"port": 3000
}
},
"message": "fetch failed"
}

How to generate billing portal link for Stripe NextJS with Firebase extension?

I'm using the Stripe extension in Firebase to create subscriptions in a NextJS web app.
My goal is to create a link for a returning user to edit their payments in Stripe without authenticating again (they are already auth in my web app and Firebase recognizes the auth).
I'm using the test mode of Stripe and I have a test customer and test products.
I've tried
The Firebase Stripe extension library does not have any function which can just return a billing portal link: https://github.com/stripe/stripe-firebase-extensions/blob/next/firestore-stripe-web-sdk/markdown/firestore-stripe-payments.md
Use the NextJS recommended import of Stripe foudn in this Vercel blog
First I setup the import for Stripe-JS: https://github.com/vercel/next.js/blob/758990dc06da4c2913f42fdfdacfe53e29e56593/examples/with-stripe-typescript/utils/get-stripejs.ts
export default function Settings() {
import stripe from "../../stripe_utils/get_stripejs"
async function editDashboard() {
const dashboardLink = await stripe.billingPortal.sessions.create({
customer: "cus_XXX",
})
}
console.log(dashboardLink.url)
return (
<Button
onClick={() => editDashboard()}>
DEBUG: See payments
</Button>
)
}
This would result in an error:
TypeError: Cannot read properties of undefined (reading 'sessions')
Use the stripe library. This seemed like the most promising solution but from what I read this is a backend library though I tried to use on the front end. There were no errors with this approach but I figure it hangs on the await
import Stripe from "stripe"
const stripe = new Stripe(process.env.STRIPE_SECRET)
...
const session = await stripe.billingPortal.sessions.create({
customer: 'cus_XXX',
return_url: 'https://example.com/account',
})
console.log(session.url) // Does not reach here
Use a pre-made Stripe link to redirect but the user will have to authenticate on Stripe using their email (this works but I would rather have a short-lived link from Stripe)
<Button component={Link} to={"https://billing.stripe.com/p/login/XXX"}>
Edit payment info on Stripe
</Button>
Using POST HTTPS API call found at https://stripe.com/docs/api/authentication. Unlike the previous options, this optional will register a Stripe Dashboard Log event.
const response = await fetch("https://api.stripe.com/v1/billing_portal/sessions", {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json',
'Authorization': 'bearer sk_test_XXX',
'Content-Type': 'application/x-www-form-urlencoded',
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer', // no-referrer, *client
body: JSON.stringify(data), // body data type must match "Content-Type" header
})
The error is I'm missing some parameter parameter_missing -customer. So I'm closer to a resolution but I feel as if I should still be able to make the solution above work.
You should use Stripe library to create a billing portal session (your 2nd approach), and you might want to check your Dashboard logs and set the endpoint to /v1/billing_portal/sessions so that you can see if there are any errors during portal session creation.
Given my case, I chose to call the API itself instead of the libraries provided:
export default async function Stripe(payload, stripeEndpoint) {
const _ENDPOINTS = [
"/v1/billing_portal/sessions",
"/v1/customers",
]
let contentTypeHeader = "application/json"
let body = JSON.stringify(payload)
if _ENDPOINTS.includes(stripeEndpoint)) {
contentTypeHeader = "application/x-www-form-urlencoded"
body = Object.keys(payload).map(
entry => entry + "=" + payload[entry]).join("&")
}
try {
// Default options are marked with *
const stripeResponse = await fetch("https://api.stripe.com" + stripeEndpoint, {
method: "POST", // *GET, POST, PUT, DELETE, etc.
headers: {
"Authorization": "bearer " + STRIPE_PRIVATE_KEY,
"Content-Type": contentTypeHeader,
},
redirect: "follow", // manual, *follow, error
referrerPolicy: "no-referrer", // no-referrer, *client
body: body, // body data type must match "Content-Type" header
})
return await stripeResponse.json()
} catch (err) {
console.error(err)
}
}

Apollo client cache with SSR

I am using #apollo/client with Next.js Server Side Rendering. The data I am fetching is from wp-graphql, and therefore it is crucial to have them rendered on server side. There is just one slight issue, the cache is forever. I either have to reset the cache every x minutes:
setInterval(async () => {
await client.resetStore();
}, 60_000);
or specify the no-cache policy
const client = new ApolloClient({
uri: "https://the.url",
cache: new InMemoryCache({}),
// For no cache, which is slow
defaultOptions: {
watchQuery: {
fetchPolicy: "no-cache",
errorPolicy: "ignore",
},
query: {
fetchPolicy: "no-cache",
errorPolicy: "all",
},
},
});
Is there any better way to do this, while still using SSR?

Nextjs and workbox integration

Requirement: I am trying to use service worker and cache static files so as to have a benefit to reduce HTTP requests and make the site performance better. 
Down the lane I would switch to offline, caching images, api's etc.
I have seen the plugins:
https://github.com/hanford/next-offline and
https://www.npmjs.com/package/next-pwa
It seems to work. Although I was trying to find out if there were examples of (nextjs + workbox).
Next js do have an example for https://github.com/vercel/next.js/tree/canary/examples/with-next-offline. But I would like just using workbox for this.
Anyone got any working examples? Even a basic one would do.
Currently am not using a custom server. Just using the inbuilt builder of nextjs (https://nextjs.org/docs/getting-started#manual-setup)
I figured out an answer on my own:
Reference: https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-build#.generateSW
I have done runtime caching for my app here and added the workbox file into the base file:
// Use the window load event to keep the page load performant
useEffect(() => {
window.addEventListener("load", () => {
const serviceWorkerScope = `/${country}/workbox-worker.js`
navigator.serviceWorker
.register(serviceWorkerScope)
.then(() => {
logger.info(`Service worker registered at ${serviceWorkerScope}`)
})
.catch(error => {
logger.error("Error in serviceWorker registration: ", error)
})
})
})
I have added comments,
// File to generate the service worker.
require("dotenv").config()
const workboxBuild = require("workbox-build")
const { COUNTRY: country, NODE_ENV } = process.env
const urlPattern = new RegExp(`/${country}\/static|_next\/.*/`)
// https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-build#.generateSW
const buildSW = () => {
return workboxBuild.generateSW({
swDest: "public/workbox-worker.js",
clientsClaim: true,
mode: NODE_ENV,
skipWaiting: true,
sourcemap: false,
runtimeCaching: [
{
urlPattern: urlPattern,
// Apply a cache-first strategy.
handler: "CacheFirst",
options: {
cacheName: "Static files caching",
expiration: {
maxEntries: 50,
maxAgeSeconds: 15 * 60, // 15minutes
},
},
},
],
})
}
buildSW()

Resources