-
Notifications
You must be signed in to change notification settings - Fork 245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add proxyCookies option #358
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,6 +1,7 @@ | ||||||
import Axios from 'axios' | ||||||
import defu from 'defu' | ||||||
<% if (options.retry) { %>import axiosRetry from 'axios-retry'<% } %> | ||||||
<% if (options.proxyCookies) { %>import { parse as parseCookies } from 'cookie'<% } %> | ||||||
|
||||||
const $nuxt = typeof window !== 'undefined' && window['$<%= options.globalName %>'] | ||||||
|
||||||
|
@@ -179,6 +180,68 @@ const setupProgress = (axios) => { | |||||
axios.defaults.onDownloadProgress = onProgress | ||||||
}<% } %> | ||||||
|
||||||
<% if (options.proxyCookies) { %> | ||||||
const proxyCookies = (ctx, axios, response) => { | ||||||
const parseSetCookies = cookies => { | ||||||
return cookies | ||||||
.map(cookie => cookie.split(';')[0]) | ||||||
.reduce((obj, cookie) => ({ | ||||||
...obj, | ||||||
...parseCookies(cookie) | ||||||
}), {}) | ||||||
} | ||||||
|
||||||
const serializeCookies = cookies => { | ||||||
return Object | ||||||
.entries(cookies) | ||||||
.map(([name, value]) => `${name}=${encodeURIComponent(value)}`) | ||||||
.join('; ') | ||||||
} | ||||||
|
||||||
const mergeSetCookies = (oldCookies, newCookies) => { | ||||||
const cookies = new Map() | ||||||
|
||||||
const add = setCookie => { | ||||||
const cookie = setCookie.split(';')[0] | ||||||
const name = Object.keys(parseCookies(cookie))[0] | ||||||
|
||||||
cookies.set(name, cookie) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
} | ||||||
|
||||||
oldCookies.forEach(add) | ||||||
newCookies.forEach(add) | ||||||
|
||||||
return [...cookies.values()] | ||||||
} | ||||||
|
||||||
const arrayify = obj => Array.isArray(obj) ? obj : [obj] | ||||||
|
||||||
if (response.headers['set-cookie']) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
We must check
|
||||||
const setCookies = arrayify(response.headers['set-cookie']) | ||||||
|
||||||
// Combine the cookies set on axios with the new cookies and serialize them | ||||||
const cookie = serializeCookies({ | ||||||
...parseCookies(axios.defaults.headers.common.cookie), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line can fail with |
||||||
...parseSetCookies(setCookies) | ||||||
}) | ||||||
|
||||||
axios.defaults.headers.common.cookie = cookie | ||||||
|
||||||
// If the res already has a Set-Cookie header it should be merged | ||||||
if (ctx.res.getHeader('Set-Cookie')) { | ||||||
const newCookies = mergeSetCookies( | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is fine by spec to send multiple
Yes it would but backend shouldn't send multiple If you are getting this behavior by your implementation, probably reason is during single SSR lifecycle, after we get first There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The issue isn't with a single request to the backend sending duplicate Consider you have the route async asyncData({ $axios }) {
await $axios.$get('/cookie'); // Set-Cookie: count=0
await $axios.$get('/cookie'); // Set-Cookie: count=1
await $axios.$get('/cookie'); // Set-Cookie: count=2
} When you're in a browser, you'll end up with When you're on the server and the
If the
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know what you mean :) but it comes with the cost of much more parse/seralize logic and a library import and Something like a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you're fine with edge cases not being covered that's fine by me. I think in most cases there won't be any issues if The cookie library is also used to parse the cookies and store them on the Axios instance. This is necessary when dealing, for example, with access tokens that get refreshed after a request, and the next request requires the new token. Ideally, all this logic is only included in the server-side bundle, but I don't know whether that is possible here. |
||||||
arrayify(ctx.res.getHeader('Set-Cookie')), | ||||||
setCookies | ||||||
) | ||||||
|
||||||
ctx.res.setHeader('Set-Cookie', newCookies) | ||||||
} else { | ||||||
ctx.res.setHeader('Set-Cookie', setCookies) | ||||||
} | ||||||
} | ||||||
} | ||||||
<% } %> | ||||||
|
||||||
export default (ctx, inject) => { | ||||||
// baseURL | ||||||
const baseURL = process.browser | ||||||
|
@@ -213,6 +276,15 @@ export default (ctx, inject) => { | |||||
|
||||||
const axios = createAxiosInstance(axiosOptions) | ||||||
|
||||||
<% if (options.proxyCookies) { %> | ||||||
// Proxy cookies | ||||||
if (process.server) { | ||||||
axios.onResponse(response => { | ||||||
proxyCookies(ctx, axios, response) | ||||||
}) | ||||||
} | ||||||
<% } %> | ||||||
|
||||||
// Inject axios to the context as $axios | ||||||
ctx.$axios = axios | ||||||
inject('axios', axios) | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest turning off by default because of security implications it has (private SSR API calls may leak headers)