Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 37 additions & 10 deletions digest-fetch-src.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ const base64 = require('base-64')

const supported_algorithms = ['MD5', 'MD5-sess']

// challenge = auth-scheme 1*SP 1#auth-param

const tokenRegexp = '[!#-\'*+-.0-9A-Z^_`a-z~]+'
const schemeRegexp = tokenRegexp
const paramRegexp = tokenRegexp+'='+'('+tokenRegexp+'|"([^"\\\\]|\\\\.)*")'

const challengeRegexp = new RegExp('^(('+schemeRegexp+') +'+paramRegexp+'( *, *'+paramRegexp+')*) *(, *'+schemeRegexp+' .*)?$')

const parse = (raw, field, trim=true) => {
const regex = new RegExp(`${field}=("[^"]*"|[^,]*)`, "i")
const match = regex.exec(raw)
Expand Down Expand Up @@ -117,7 +125,7 @@ class DigestClient {

const opaqueString = this.digest.opaque !== null ? `opaque="${this.digest.opaque}",` : ''
const qopString = this.digest.qop ? `qop="${this.digest.qop}",` : ''
const digest = `${this.digest.scheme} username="${this.user}",realm="${this.digest.realm}",\
const digest = `Digest username="${this.user}",realm="${this.digest.realm}",\
nonce="${this.digest.nonce}",uri="${uri}",${opaqueString}${qopString}\
algorithm="${this.digest.algorithm}",response="${response}",nc=${ncString},cnonce="${this.digest.cnonce}"`
options.headers = options.headers || {}
Expand All @@ -140,19 +148,38 @@ algorithm="${this.digest.algorithm}",response="${response}",nc=${ncString},cnonc
}

this.hasAuth = true

this.digest.scheme = h.split(/\s/)[0]
while (true) {
const challengeMatch = h.match(challengeRegexp)
if (!challengeMatch) {
break
}
const challenge = challengeMatch[1]
const scheme = challengeMatch[2]

if (scheme.match(/^Digest$/i)) {

this.digest.realm = (parse(h, 'realm', false) || '').replace(/["]/g, '')
this.digest.realm = (parse(challenge, 'realm', false) || '').replace(/["]/g, '')

this.digest.qop = this.parseQop(h)
this.digest.qop = this.parseQop(challenge)

this.digest.opaque = parse(h, 'opaque')

this.digest.nonce = parse(h, 'nonce') || ''
this.digest.opaque = parse(challenge, 'opaque')

this.digest.cnonce = this.makeNonce()
this.digest.nc++
this.digest.nonce = parse(challenge, 'nonce') || ''

this.digest.cnonce = this.makeNonce()
this.digest.nc++
}
h = h.substr(challenge.length)
if (!h) {
break
}
const comma = h.match("^( *, *).*")
if (!comma) {
// TODO: parse error
break
}
h = h.substr(comma[1].length)
}
}

parseQop (rawAuth) {
Expand Down
26 changes: 24 additions & 2 deletions test/digest-fetch-basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,40 @@ var app = factory.getApp()

describe('digest-fetch', function(){

it('Test Basic Authentication', function() {
it('Test Basic Authentication', function(done) {
var client = new DigestFetch('test', 'test', { basic: true })
const auth = client.addBasicAuth().headers.Authorization
chai.request(app).get('/basic').set('Authorization', auth).then(res => {
expect(res).to.have.status(200)
done()
})
.catch(done)
})
it('Test Basic Authentication with wrong credential', function() {
it('Test Basic Authentication with wrong credential', function(done) {
var client = new DigestFetch('test', 'test-null', { basic: true })
const auth = client.addBasicAuth().headers.Authorization
chai.request(app).get('/basic').set('Authorization', auth).then(res => {
expect(res).to.have.status(401)
done()
})
.catch(done)
})
it('Test Basic Authentication with basic-and-digest', function(done) {
var client = new DigestFetch('test', 'test', { basic: true })
const auth = client.addBasicAuth().headers.Authorization
chai.request(app).get('/basic-and-digest').set('Authorization', auth).then(res => {
expect(res).to.have.status(200)
done()
})
.catch(done)
})
it('Test Basic Authentication with digest-and-basic', function(done) {
var client = new DigestFetch('test', 'test', { basic: true })
const auth = client.addBasicAuth().headers.Authorization
chai.request(app).get('/digest-and-basic').set('Authorization', auth).then(res => {
expect(res).to.have.status(200)
done()
})
.catch(done)
})
})
10 changes: 8 additions & 2 deletions test/digest-fetch-rfc2069.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var app = factory.getApp()

describe('digest-fetch', function(){

it('Test RFC2069', function() {
it('Test RFC2069', function(done) {
var client = new DigestFetch('test', 'test')
chai.request(app).get('/auth').then(res => {
expect(res).to.have.status(401)
Expand All @@ -24,11 +24,14 @@ describe('digest-fetch', function(){
const auth = client.addAuth('/auth', { method: 'GET' }).headers.Authorization
chai.request(app).get('/auth').set('Authorization', auth).then(res => {
expect(res).to.have.status(200)
done()
})
.catch(done)
})
.catch(done)
})

it('Test RFC2069 with wrong credential', function() {
it('Test RFC2069 with wrong credential', function(done) {
var client = new DigestFetch('test', 'test-null')
chai.request(app).get('/auth').then(res => {
res.should.have.status(401)
Expand All @@ -39,7 +42,10 @@ describe('digest-fetch', function(){
const auth = client.addAuth('/auth', { method: 'GET' }).headers.Authorization
chai.request(app).get('/auth').set('Authorization', auth).then(res => {
expect(res).to.have.status(401)
done()
})
.catch(done)
})
.catch(done)
})
})
53 changes: 50 additions & 3 deletions test/digest-fetch-rfc2617.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var app = factory.getApp('auth')

describe('digest-fetch', function(){

it('Test RFC2617', function() {
it('Test RFC2617', function(done) {
var client = new DigestFetch('test', 'test')
chai.request(app).get('/auth').then(res => {
expect(res).to.have.status(401)
Expand All @@ -24,11 +24,14 @@ describe('digest-fetch', function(){
const auth = client.addAuth('/auth', { method: 'GET' }).headers.Authorization
chai.request(app).get('/auth').set('Authorization', auth).then(res => {
expect(res).to.have.status(200)
done()
})
.catch(done)
})
.catch(done)
})

it('Test RFC2617 with precomputed hash', function() {
it('Test RFC2617 with precomputed hash', function(done) {
var client = new DigestFetch('test', DigestFetch.computeHash('test', 'Users', 'test'), { precomputedHash: true })
chai.request(app).get('/auth').then(res => {
expect(res).to.have.status(401)
Expand All @@ -39,11 +42,14 @@ describe('digest-fetch', function(){
const auth = client.addAuth('/auth', { method: 'GET' }).headers.Authorization
chai.request(app).get('/auth').set('Authorization', auth).then(res => {
expect(res).to.have.status(200)
done()
})
.catch(done)
})
.catch(done)
})

it('Test RFC2617 with wrong credential', function() {
it('Test RFC2617 with wrong credential', function(done) {
var client = new DigestFetch('test', 'test-null')
chai.request(app).get('/auth').then(res => {
expect(res).to.have.status(401)
Expand All @@ -54,7 +60,48 @@ describe('digest-fetch', function(){
const auth = client.addAuth('/auth', { method: 'GET' }).headers.Authorization
chai.request(app).get('/auth').set('Authorization', auth).then(res => {
expect(res).to.have.status(401)
done()
})
.catch(done)
})
.catch(done)
})

it('Test RFC2617 with basic-and-digest', function(done) {
var client = new DigestFetch('test', 'test')
chai.request(app).get('/basic-and-digest').then(res => {
expect(res).to.have.status(401)
client.lastAuth = res.res.headers['www-authenticate']
})
.then(() => {
client.parseAuth(client.lastAuth)
const auth = client.addAuth('/basic-and-digest', { method: 'GET' }).headers.Authorization
expect(auth).to.match(/^Digest /)
chai.request(app).get('/basic-and-digest').set('Authorization', auth).then(res => {
expect(res).to.have.status(200)
done()
})
.catch(done)
})
.catch(done)
})

it('Test RFC2617 with digest-and-basic', function(done) {
var client = new DigestFetch('test', 'test')
chai.request(app).get('/digest-and-basic').then(res => {
expect(res).to.have.status(401)
client.lastAuth = res.res.headers['www-authenticate']
})
.then(() => {
client.parseAuth(client.lastAuth)
const auth = client.addAuth('/digest-and-basic', { method: 'GET' }).headers.Authorization
expect(auth).to.match(/^Digest /)
chai.request(app).get('/digest-and-basic').set('Authorization', auth).then(res => {
expect(res).to.have.status(200)
done()
})
.catch(done)
})
.catch(done)
})
})
4 changes: 2 additions & 2 deletions test/digest-fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ describe('digest-fetch', function(){
assert.equal(DigestFetch.parse('a="v2",', 'a'), 'v2')
assert.equal(DigestFetch.parse('a="v1,v2"', 'a'), 'v1,v2')
const client = new DigestFetch("", "")
client.parseAuth('qop=auth-int,realm=test')
client.parseAuth('Digest qop=auth-int,realm=test',1)
assert.equal(client.digest.realm, "test")
client.parseAuth('qop="auth",realm="v1 v2"')
client.parseAuth('Digest qop="auth",realm="v1 v2"')
assert.equal(client.digest.realm, "v1 v2")
})

Expand Down
12 changes: 12 additions & 0 deletions test/test-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ module.exports = { getApp(method) {
function(req, res) {
res.json(req.user);
});
// http basic and digest authentication
app.get('/basic-and-digest',
passport.authenticate(['basic', 'digest'], { session: false }),
function(req, res) {
res.json(req.user);
});
// http digest and basic authentication
app.get('/digest-and-basic',
passport.authenticate(['digest','basic'], { session: false }),
function(req, res) {
res.json(req.user);
});


// app.use("/static", express.static(path.join(__dirname, './static')));
Expand Down