2
0
mirror of https://github.com/tenrok/axios.git synced 2026-06-20 20:00:40 +03:00

feat(withXSRFToken): added withXSRFToken option as a workaround to achieve the old withCredentials behavior; (#6046)

This commit is contained in:
Dmitriy Mozgovoy
2023-11-14 15:38:25 +02:00
committed by GitHub
parent 7009715369
commit cff996779b
8 changed files with 110 additions and 47 deletions
+3
View File
@@ -452,6 +452,9 @@ These are the available config options for making requests. Only the `url` is re
// `xsrfHeaderName` is the name of the http header that carries the xsrf token value // `xsrfHeaderName` is the name of the http header that carries the xsrf token value
xsrfHeaderName: 'X-XSRF-TOKEN', // default xsrfHeaderName: 'X-XSRF-TOKEN', // default
// `undefined` (default) - set XSRF header only for the same origin requests
withXSRFToken: boolean | undefined | ((config: InternalAxiosRequestConfig) => boolean | undefined),
// `onUploadProgress` allows handling of progress events for uploads // `onUploadProgress` allows handling of progress events for uploads
// browser & node.js // browser & node.js
+1
View File
@@ -414,6 +414,7 @@ declare namespace axios {
family?: AddressFamily; family?: AddressFamily;
lookup?: ((hostname: string, options: object, cb: (err: Error | null, address: LookupAddress | LookupAddress[], family?: AddressFamily) => void) => void) | lookup?: ((hostname: string, options: object, cb: (err: Error | null, address: LookupAddress | LookupAddress[], family?: AddressFamily) => void) => void) |
((hostname: string, options: object) => Promise<[address: LookupAddressEntry | LookupAddressEntry[], family?: AddressFamily] | LookupAddress>); ((hostname: string, options: object) => Promise<[address: LookupAddressEntry | LookupAddressEntry[], family?: AddressFamily] | LookupAddress>);
withXSRFToken?: boolean | ((config: InternalAxiosRequestConfig) => boolean | undefined);
} }
// Alias // Alias
Vendored
+1
View File
@@ -355,6 +355,7 @@ export interface AxiosRequestConfig<D = any> {
family?: AddressFamily; family?: AddressFamily;
lookup?: ((hostname: string, options: object, cb: (err: Error | null, address: LookupAddress | LookupAddress[], family?: AddressFamily) => void) => void) | lookup?: ((hostname: string, options: object, cb: (err: Error | null, address: LookupAddress | LookupAddress[], family?: AddressFamily) => void) => void) |
((hostname: string, options: object) => Promise<[address: LookupAddressEntry | LookupAddressEntry[], family?: AddressFamily] | LookupAddress>); ((hostname: string, options: object) => Promise<[address: LookupAddressEntry | LookupAddressEntry[], family?: AddressFamily] | LookupAddress>);
withXSRFToken?: boolean | ((config: InternalAxiosRequestConfig) => boolean | undefined);
} }
// Alias // Alias
+10 -7
View File
@@ -49,7 +49,7 @@ export default isXHRAdapterSupported && function (config) {
return new Promise(function dispatchXhrRequest(resolve, reject) { return new Promise(function dispatchXhrRequest(resolve, reject) {
let requestData = config.data; let requestData = config.data;
const requestHeaders = AxiosHeaders.from(config.headers).normalize(); const requestHeaders = AxiosHeaders.from(config.headers).normalize();
const responseType = config.responseType; let {responseType, withXSRFToken} = config;
let onCanceled; let onCanceled;
function done() { function done() {
if (config.cancelToken) { if (config.cancelToken) {
@@ -185,13 +185,16 @@ export default isXHRAdapterSupported && function (config) {
// Add xsrf header // Add xsrf header
// This is only done if running in a standard browser environment. // This is only done if running in a standard browser environment.
// Specifically not if we're in a web worker, or react-native. // Specifically not if we're in a web worker, or react-native.
if (platform.hasStandardBrowserEnv) { if(platform.hasStandardBrowserEnv) {
// Add xsrf header withXSRFToken && utils.isFunction(withXSRFToken) && (withXSRFToken = withXSRFToken(config));
// regarding CVE-2023-45857 config.withCredentials condition was removed temporarily
const xsrfValue = isURLSameOrigin(fullPath) && config.xsrfCookieName && cookies.read(config.xsrfCookieName);
if (xsrfValue) { if (withXSRFToken || (withXSRFToken !== false && isURLSameOrigin(fullPath))) {
requestHeaders.set(config.xsrfHeaderName, xsrfValue); // Add xsrf header
const xsrfValue = config.xsrfHeaderName && config.xsrfCookieName && cookies.read(config.xsrfCookieName);
if (xsrfValue) {
requestHeaders.set(config.xsrfHeaderName, xsrfValue);
}
} }
} }
+1
View File
@@ -75,6 +75,7 @@ export default function mergeConfig(config1, config2) {
timeout: defaultToConfig2, timeout: defaultToConfig2,
timeoutMessage: defaultToConfig2, timeoutMessage: defaultToConfig2,
withCredentials: defaultToConfig2, withCredentials: defaultToConfig2,
withXSRFToken: defaultToConfig2,
adapter: defaultToConfig2, adapter: defaultToConfig2,
responseType: defaultToConfig2, responseType: defaultToConfig2,
xsrfCookieName: defaultToConfig2, xsrfCookieName: defaultToConfig2,
+29 -39
View File
@@ -1,52 +1,42 @@
'use strict';
import utils from './../utils.js'; import utils from './../utils.js';
import platform from '../platform/index.js'; import platform from '../platform/index.js';
export default platform.hasStandardBrowserEnv ? export default platform.hasStandardBrowserEnv ?
// Standard browser envs support document.cookie // Standard browser envs support document.cookie
(function standardBrowserEnv() { {
return { write(name, value, expires, path, domain, secure) {
write: function write(name, value, expires, path, domain, secure) { const cookie = [name + '=' + encodeURIComponent(value)];
const cookie = [];
cookie.push(name + '=' + encodeURIComponent(value));
if (utils.isNumber(expires)) { utils.isNumber(expires) && cookie.push('expires=' + new Date(expires).toGMTString());
cookie.push('expires=' + new Date(expires).toGMTString());
}
if (utils.isString(path)) { utils.isString(path) && cookie.push('path=' + path);
cookie.push('path=' + path);
}
if (utils.isString(domain)) { utils.isString(domain) && cookie.push('domain=' + domain);
cookie.push('domain=' + domain);
}
if (secure === true) { secure === true && cookie.push('secure');
cookie.push('secure');
}
document.cookie = cookie.join('; '); document.cookie = cookie.join('; ');
}, },
read: function read(name) { read(name) {
const match = document.cookie.match(new RegExp('(^|;\\s*)(' + name + ')=([^;]*)')); const match = document.cookie.match(new RegExp('(^|;\\s*)(' + name + ')=([^;]*)'));
return (match ? decodeURIComponent(match[3]) : null); return (match ? decodeURIComponent(match[3]) : null);
}, },
remove: function remove(name) { remove(name) {
this.write(name, '', Date.now() - 86400000); this.write(name, '', Date.now() - 86400000);
} }
}; }
})() :
:
// Non-standard browser env (web workers, react-native) lack needed support.
{
write() {},
read() {
return null;
},
remove() {}
};
// Non standard browser env (web workers, react-native) lack needed support.
(function nonStandardBrowserEnv() {
return {
write: function write() {},
read: function read() { return null; },
remove: function remove() {}
};
})();
+1 -1
View File
@@ -13,7 +13,7 @@ export default platform.hasStandardBrowserEnv ?
let originURL; let originURL;
/** /**
* Parse a URL to discover it's components * Parse a URL to discover its components
* *
* @param {String} url The URL to be parsed * @param {String} url The URL to be parsed
* @returns {Object} * @returns {Object}
+64
View File
@@ -79,4 +79,68 @@ describe('xsrf', function () {
done(); done();
}); });
}); });
describe('withXSRFToken option', function(){
it('should set xsrf header for cross origin when withXSRFToken = true', function (done) {
const token = '12345';
document.cookie = axios.defaults.xsrfCookieName + '=' + token;
axios('http://example.com/', {
withXSRFToken: true
});
getAjaxRequest().then(function (request) {
expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toEqual(token);
done();
});
});
it('should not set xsrf header for the same origin when withXSRFToken = false', function (done) {
const token = '12345';
document.cookie = axios.defaults.xsrfCookieName + '=' + token;
axios('/foo', {
withXSRFToken: false
});
getAjaxRequest().then(function (request) {
expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toEqual(undefined);
done();
});
});
it('should not set xsrf header for the same origin when withXSRFToken = false', function (done) {
const token = '12345';
document.cookie = axios.defaults.xsrfCookieName + '=' + token;
axios('/foo', {
withXSRFToken: false
});
getAjaxRequest().then(function (request) {
expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toEqual(undefined);
done();
});
});
it('should support function resolver', (done) => {
const token = '12345';
document.cookie = axios.defaults.xsrfCookieName + '=' + token;
axios('/foo', {
withXSRFToken: (config) => config.userFlag === 'yes',
userFlag: 'yes'
});
getAjaxRequest().then(function (request) {
expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toEqual(token);
done();
});
});
});
}); });