Skip to content

Commit

Permalink
fix: parsing multiple www-authenticate response headers (#421)
Browse files Browse the repository at this point in the history
closes #420
  • Loading branch information
capsice committed Nov 18, 2022
1 parent 5c0b4e9 commit 46d0515
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 6 deletions.
11 changes: 7 additions & 4 deletions src/HttpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,11 +418,14 @@ export class HttpClient extends EventEmitter {
}

let response = await undiciRequest(requestUrl, requestOptions);
// handle digest auth
if (response.statusCode === 401 && response.headers['www-authenticate'] &&
!requestOptions.headers.authorization && args.digestAuth) {
const authenticate = response.headers['www-authenticate'];
if (authenticate.startsWith('Digest ')) {
!requestOptions.headers.authorization && args.digestAuth) {
// handle digest auth
const authenticateHeaders = response.headers['www-authenticate'];
const authenticate = Array.isArray(authenticateHeaders)
? authenticateHeaders.find(authHeader => authHeader.startsWith('Digest '))
: authenticateHeaders;
if (authenticate && authenticate.startsWith('Digest ')) {
debug('Request#%d %s: got digest auth header WWW-Authenticate: %s', requestId, requestUrl.href, authenticate);
requestOptions.headers.authorization = digestAuthHeader(requestOptions.method!,
`${requestUrl.pathname}${requestUrl.search}`, authenticate, args.digestAuth);
Expand Down
28 changes: 26 additions & 2 deletions test/fixtures/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,30 @@ export async function startServer(options?: {
}));
}

if (pathname === '/digestAuth/multi') {
const authorization = req.headers.authorization;
if (!authorization) {
res.setHeader('www-authenticate', [
'Digest realm="testrealm@urllib.com", algorithm=MD5-sess, qop="auth", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"',
'Digest realm="testrealm@urllib.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"',
]);
res.statusCode = 401;
return res.end(JSON.stringify({
error: 'authorization required',
}));
}
if (!authorization.includes('Digest username="user"')) {
res.setHeader('www-authenticate', 'Digest realm="testrealm@urllib.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"');
res.statusCode = 401;
return res.end(JSON.stringify({
error: 'authorization invaild',
}));
}
return res.end(JSON.stringify({
authorization,
}));
}

if (pathname === '/wrongjson') {
res.setHeader('content-type', 'application/json');
return res.end(Buffer.from('{"foo":""'));
Expand Down Expand Up @@ -154,7 +178,7 @@ export async function startServer(options?: {
res.socket!.end('balabala');
return;
}

if (pathname === '/wrongjson-gbk') {
res.setHeader('content-type', 'application/json');
createReadStream(__filename).pipe(res);
Expand Down Expand Up @@ -259,7 +283,7 @@ export async function startServer(options?: {
if (req.headers['content-type']?.startsWith('application/x-www-form-urlencoded')) {
const searchParams = new URLSearchParams(requestBytes.toString());
requestBody = {};
for (const [ field, value ] of searchParams.entries()) {
for (const [field, value] of searchParams.entries()) {
requestBody[field] = value;
}
} else if (req.headers['content-type']?.startsWith('application/json')) {
Expand Down
12 changes: 12 additions & 0 deletions test/options.digestAuth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ describe('options.digestAuth.test.ts', () => {
assert.match(response.data.authorization, /, uri="\/digestAuth",/);
});

it('should choose one of multiple challenges split in different headers', async () => {
const response = await urllib.request(`${_url}digestAuth/multi`, {
digestAuth: 'user:pwd',
dataType: 'json',
});
assert.equal(response.status, 200);
assert(response.data.authorization);
// console.log(response.data);
assert.match(response.data.authorization, /Digest username="user", realm="testrealm@urllib.com", nonce="/);
assert.match(response.data.authorization, /, uri="\/digestAuth\/multi",/);
});

it('should auth fail', async () => {
const response = await urllib.request(`${_url}digestAuth`, {
digestAuth: 'invailduser:pwd',
Expand Down

0 comments on commit 46d0515

Please sign in to comment.