Skip to content

Commit 3388513

Browse files
KhafraDevmcollina
andauthored
fix websocket basic auth (#4747)
* fix websocket basic auth Co-authored-by: Matteo Collina <[email protected]> * fixup * fixup --------- Co-authored-by: Matteo Collina <[email protected]>
1 parent 250efc8 commit 3388513

6 files changed

Lines changed: 964 additions & 1224 deletions

File tree

lib/web/fetch/index.js

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@ const {
4242
simpleRangeHeaderValue,
4343
buildContentRange,
4444
createInflate,
45-
extractMimeType
45+
extractMimeType,
46+
hasAuthenticationEntry,
47+
includesCredentials,
48+
isTraversableNavigable
4649
} = require('./util')
4750
const assert = require('node:assert')
4851
const { safelyExtractBody, extractBody } = require('./body')
@@ -1524,13 +1527,39 @@ async function httpNetworkOrCacheFetch (
15241527

15251528
httpRequest.headersList.delete('host', true)
15261529

1527-
// 20. If includeCredentials is true, then:
1530+
// 21. If includeCredentials is true, then:
15281531
if (includeCredentials) {
15291532
// 1. If the user agent is not configured to block cookies for httpRequest
15301533
// (see section 7 of [COOKIES]), then:
15311534
// TODO: credentials
1535+
15321536
// 2. If httpRequest’s header list does not contain `Authorization`, then:
1533-
// TODO: credentials
1537+
if (!httpRequest.headersList.contains('authorization', true)) {
1538+
// 1. Let authorizationValue be null.
1539+
let authorizationValue = null
1540+
1541+
// 2. If there’s an authentication entry for httpRequest and either
1542+
// httpRequest’s use-URL-credentials flag is unset or httpRequest’s
1543+
// current URL does not include credentials, then set
1544+
// authorizationValue to authentication entry.
1545+
if (hasAuthenticationEntry(httpRequest) && (
1546+
httpRequest.useURLCredentials === undefined || !includesCredentials(requestCurrentURL(httpRequest))
1547+
)) {
1548+
// TODO
1549+
} else if (includesCredentials(requestCurrentURL(httpRequest)) && isAuthenticationFetch) {
1550+
// 3. Otherwise, if httpRequest’s current URL does include credentials
1551+
// and isAuthenticationFetch is true, set authorizationValue to
1552+
// httpRequest’s current URL, converted to an `Authorization` value
1553+
const { username, password } = requestCurrentURL(httpRequest)
1554+
authorizationValue = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`
1555+
}
1556+
1557+
// 4. If authorizationValue is non-null, then append (`Authorization`,
1558+
// authorizationValue) to httpRequest’s header list.
1559+
if (authorizationValue !== null) {
1560+
httpRequest.headersList.append('Authorization', authorizationValue, false)
1561+
}
1562+
}
15341563
}
15351564

15361565
// 21. If there’s a proxy-authentication entry, use it as appropriate.
@@ -1612,10 +1641,48 @@ async function httpNetworkOrCacheFetch (
16121641
// 13. Set response’s request-includes-credentials to includeCredentials.
16131642
response.requestIncludesCredentials = includeCredentials
16141643

1615-
// 14. If response’s status is 401, httpRequest’s response tainting is not
1616-
// "cors", includeCredentials is true, and request’s window is an environment
1617-
// settings object, then:
1618-
// TODO
1644+
// 14. If response’s status is 401, httpRequest’s response tainting is not "cors",
1645+
// includeCredentials is true, and request’s traversable for user prompts is
1646+
// a traversable navigable:
1647+
if (response.status === 401 && httpRequest.responseTainting !== 'cors' && includeCredentials && isTraversableNavigable(request.traversableForUserPrompts)) {
1648+
// 2. If request’s body is non-null, then:
1649+
if (request.body != null) {
1650+
// 1. If request’s body’s source is null, then return a network error.
1651+
if (request.body.source == null) {
1652+
return makeNetworkError('expected non-null body source')
1653+
}
1654+
1655+
// 2. Set request’s body to the body of the result of safely extracting
1656+
// request’s body’s source.
1657+
request.body = safelyExtractBody(request.body.source)[0]
1658+
}
1659+
1660+
// 3. If request’s use-URL-credentials flag is unset or isAuthenticationFetch is
1661+
// true, then:
1662+
if (request.useURLCredentials === undefined || isAuthenticationFetch) {
1663+
// 1. If fetchParams is canceled, then return the appropriate network error
1664+
// for fetchParams.
1665+
if (isCancelled(fetchParams)) {
1666+
return makeAppropriateNetworkError(fetchParams)
1667+
}
1668+
1669+
// 2. Let username and password be the result of prompting the end user for a
1670+
// username and password, respectively, in request’s traversable for user prompts.
1671+
// TODO
1672+
1673+
// 3. Set the username given request’s current URL and username.
1674+
// requestCurrentURL(request).username = TODO
1675+
1676+
// 4. Set the password given request’s current URL and password.
1677+
// requestCurrentURL(request).password = TODO
1678+
}
1679+
1680+
// 4. Set response to the result of running HTTP-network-or-cache fetch given
1681+
// fetchParams and true.
1682+
fetchParams.controller.connection.destroy()
1683+
1684+
response = await httpNetworkOrCacheFetch(fetchParams, true)
1685+
}
16191686

16201687
// 15. If response’s status is 407, then:
16211688
if (response.status === 407) {

lib/web/fetch/request.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,8 @@ function makeRequest (init) {
918918
preventNoCacheCacheControlHeaderModification: init.preventNoCacheCacheControlHeaderModification ?? false,
919919
done: init.done ?? false,
920920
timingAllowFailed: init.timingAllowFailed ?? false,
921+
useURLCredentials: init.useURLCredentials ?? undefined,
922+
traversableForUserPrompts: init.traversableForUserPrompts ?? 'client',
921923
urlList: init.urlList,
922924
url: init.urlList[0],
923925
headersList: init.headersList

lib/web/fetch/util.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1429,6 +1429,28 @@ function getDecodeSplit (name, list) {
14291429
return gettingDecodingSplitting(value)
14301430
}
14311431

1432+
function hasAuthenticationEntry (request) {
1433+
return false
1434+
}
1435+
1436+
/**
1437+
* @see https://url.spec.whatwg.org/#include-credentials
1438+
* @param {URL} url
1439+
*/
1440+
function includesCredentials (url) {
1441+
// A URL includes credentials if its username or password is not the empty string.
1442+
return !!(url.username && url.password)
1443+
}
1444+
1445+
/**
1446+
* @see https://html.spec.whatwg.org/multipage/document-sequences.html#traversable-navigable
1447+
* @param {object|string} navigable
1448+
*/
1449+
function isTraversableNavigable (navigable) {
1450+
// TODO
1451+
return true
1452+
}
1453+
14321454
class EnvironmentSettingsObjectBase {
14331455
get baseUrl () {
14341456
return getGlobalOrigin()
@@ -1491,5 +1513,8 @@ module.exports = {
14911513
extractMimeType,
14921514
getDecodeSplit,
14931515
environmentSettingsObject,
1494-
isOriginIPPotentiallyTrustworthy
1516+
isOriginIPPotentiallyTrustworthy,
1517+
hasAuthenticationEntry,
1518+
includesCredentials,
1519+
isTraversableNavigable
14951520
}

lib/web/websocket/connection.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ function establishWebSocketConnection (url, protocols, client, handler, options)
3333
// 2. Let request be a new request, whose URL is requestURL, client is client,
3434
// service-workers mode is "none", referrer is "no-referrer", mode is
3535
// "websocket", credentials mode is "include", cache mode is "no-store" ,
36-
// and redirect mode is "error".
36+
// redirect mode is "error", and use-URL-credentials flag is set.
3737
const request = makeRequest({
3838
urlList: [requestURL],
3939
client,
@@ -42,7 +42,8 @@ function establishWebSocketConnection (url, protocols, client, handler, options)
4242
mode: 'websocket',
4343
credentials: 'include',
4444
cache: 'no-store',
45-
redirect: 'error'
45+
redirect: 'error',
46+
useURLCredentials: true
4647
})
4748

4849
// Note: undici extension, allow setting custom headers.

0 commit comments

Comments
 (0)