Skip to content

Instantly share code, notes, and snippets.

@jbruni
Created May 22, 2018 01:17
Show Gist options
  • Save jbruni/8239af17fbc9e541900b2ca55759547d to your computer and use it in GitHub Desktop.
Save jbruni/8239af17fbc9e541900b2ca55759547d to your computer and use it in GitHub Desktop.
Digest Authentication for perry-mitchell/webdav-client
/**
* Provide digestFetch function
*/
const crypto = require('crypto');
const nodeFetch = require('node-fetch');
const mergeOptions = require('merge-options');
const cnonceSize = 32;
const nonceRaw = 'abcdef0123456789';
function makeNonce () {
let uid = '';
for (let i = 0; i < cnonceSize; ++i) {
uid += nonceRaw[Math.floor(Math.random() * nonceRaw.length)];
}
return uid;
}
function md5(data) {
return crypto.createHash('md5').update(data).digest('hex');
}
function ha1Compute(algorithm, user, realm, pass, nonce, cnonce) {
const ha1 = md5(`${user}:${realm}:${pass}`);
if (algorithm && algorithm.toLowerCase() === 'md5-sess') {
return md5(`${ha1}:${nonce}:${cnonce}`);
} else {
return ha1;
}
}
function digestFetch(username, password) {
const digest = { nc: 0, algorithm: 'md5' };
let hasAuth = false;
function parseAuth (response) {
const authHeader = response.headers.get('www-authenticate');
if (authHeader.split(/\s/)[0].toLowerCase() !== 'digest') {
return false;
}
const re = /([a-z0-9_-]+)=(?:"([^"]+)"|([a-z0-9_-]+))/gi;
for (;;) {
var match = re.exec(authHeader);
if (!match) {
break;
}
digest[match[1]] = match[2] || match[3];
}
digest.nc++;
digest.cnonce = makeNonce();
return true;
}
function addAuth (url, options) {
if (!hasAuth) {
return options;
}
const _url = url.replace('//', '');
const uri = _url.indexOf('/') == -1 ? '/' : _url.slice(_url.indexOf('/'));
const method = options.method ? options.method.toUpperCase() : 'GET';
const qop = /(^|,)\s*auth\s*($|,)/.test(digest.qop) && 'auth';
const ncString = (`00000000${digest.nc}`).slice(-8);
const cnonce = digest.cnonce;
const ha1 = ha1Compute(digest.algorithm, username, digest.realm, password, digest.nonce, digest.cnonce);
const ha2 = md5(`${method}:${uri}`);
const digestResponse = qop
? md5(`${ha1}:${digest.nonce}:${ncString}:${digest.cnonce}:${qop}:${ha2}`)
: md5(`${ha1}:${digest.nonce}:${ha2}`);
const authValues = {
username,
realm: digest.realm,
nonce: digest.nonce,
uri,
qop,
response: digestResponse,
nc: ncString,
cnonce: digest.cnonce,
algorithm: digest.algorithm,
opaque: digest.opaque
};
const authHeader = [];
for (var k in authValues) {
if (authValues[k]) {
if (k === 'qop' || k === 'nc' || k === 'algorithm') {
authHeader.push(`${k}=${authValues[k]}`);
} else {
authHeader.push(`${k}="${authValues[k]}"`);
}
}
}
return mergeOptions(options, { headers: { Authorization: 'Digest ' + authHeader.join(', ') } });
}
return async function fetch (url, options = {}) {
const response = await nodeFetch(url, addAuth(url, options));
if (response.status == 401) {
hasAuth = parseAuth(response);
if (hasAuth) {
const response2 = await nodeFetch(url, addAuth(url, options));
if (response2.status == 401) {
hasAuth = false;
} else {
digest.nc++;
}
return response2;
}
} else {
digest.nc++;
}
return response;
}
}
module.exports = digestFetch;
const createClient = require('webdav');
const digestFetch = require('./digestFetch');
createClient.setFetchMethod(digestFetch('username', 'password'));
const client = createClient('path');
@abhay25vyas
Copy link

I am getting error "createClient.setFetchMethod" is not a function

@jbruni
Copy link
Author

jbruni commented Sep 3, 2023

@abhay25vyas - This gist was written five years ago, for the WebDAV client release version from that time. Digest authentication is now built-in the client. This gist is not necessary anymore.

@abhay25vyas
Copy link

abhay25vyas commented Sep 3, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment