Skip to content

Commit b5f3dc4

Browse files
Re-add WebID-TLS auth code
1 parent 9e71eb9 commit b5f3dc4

File tree

23 files changed

+1486
-5
lines changed

23 files changed

+1486
-5
lines changed

lib/api/authn/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ function setUserHeader (req, res, next) {
2929

3030
module.exports = {
3131
oidc: require('./webid-oidc'),
32+
tls: require('./webid-tls'),
3233
overrideWith,
3334
setUserHeader
3435
}

lib/api/authn/webid-tls.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
module.exports = handler
2+
module.exports.authenticate = authenticate
3+
4+
var webid = require('webid/tls')
5+
var debug = require('../../debug').authentication
6+
7+
function authenticate () {
8+
return handler
9+
}
10+
11+
function handler (req, res, next) {
12+
// User already logged in? skip
13+
if (req.session.userId && req.session.identified) {
14+
debug('User: ' + req.session.userId)
15+
res.set('User', req.session.userId)
16+
return next()
17+
}
18+
19+
var certificate = req.connection.getPeerCertificate()
20+
// Certificate is empty? skip
21+
if (certificate === null || Object.keys(certificate).length === 0) {
22+
debug('No client certificate found in the request. Did the user click on a cert?')
23+
setEmptySession(req)
24+
return next()
25+
}
26+
27+
// Verify webid
28+
webid.verify(certificate, function (err, result) {
29+
if (err) {
30+
debug('Error processing certificate: ' + err.message)
31+
setEmptySession(req)
32+
return next()
33+
}
34+
req.session.userId = result
35+
req.session.identified = true
36+
debug('Identified user: ' + req.session.userId)
37+
res.set('User', req.session.userId)
38+
return next()
39+
})
40+
}
41+
42+
function setEmptySession (req) {
43+
req.session.userId = ''
44+
req.session.identified = false
45+
}

lib/api/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
module.exports = {
44
authn: require('./authn'),
55
oidc: require('./authn/webid-oidc'),
6+
tls: require('./authn/webid-tls'),
67
accounts: require('./accounts/user-accounts')
78
}

lib/create-app.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,16 @@ function initWebId (argv, app, ldp) {
174174
function initAuthentication (argv, app) {
175175
let authMethod = argv.auth
176176

177+
if (argv.forceUser) {
178+
app.use('/', API.authn.overrideWith(argv.forceUser))
179+
return
180+
}
181+
177182
switch (authMethod) {
183+
case 'tls':
184+
// Enforce authentication with WebID-TLS on all LDP routes
185+
app.use('/', API.tls.authenticate())
186+
break
178187
case 'oidc':
179188
let oidc = OidcManager.fromServerConfig(argv)
180189
app.locals.oidc = oidc
@@ -189,15 +198,12 @@ function initAuthentication (argv, app) {
189198
// Enforce authentication with WebID-OIDC on all LDP routes
190199
app.use('/', oidc.rs.authenticate())
191200

192-
if (argv.forceUser) {
193-
app.use('/', API.authn.overrideWith(argv.forceUser))
194-
}
201+
app.use('/', API.authn.setUserHeader)
202+
195203
break
196204
default:
197205
throw new TypeError('Unsupported authentication scheme')
198206
}
199-
200-
app.use('/', API.authn.setUserHeader)
201207
}
202208

203209
/**

lib/create-server.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ function createServer (argv, app) {
5858
cert: cert
5959
}
6060

61+
if (ldp.webid && ldp.auth === 'tls') {
62+
credentials.requestCert = true
63+
}
64+
6165
server = https.createServer(credentials, app)
6266
}
6367

lib/requests/create-account-request.js

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict'
22

33
const AuthRequest = require('./auth-request')
4+
const WebIdTlsCertificate = require('../models/webid-tls-certificate')
45
const debug = require('../debug').accounts
56

67
/**
@@ -72,6 +73,9 @@ class CreateAccountRequest extends AuthRequest {
7273
options.password = req.body.password
7374
options.userStore = locals.oidc.users
7475
return new CreateOidcAccountRequest(options)
76+
case 'tls':
77+
options.spkac = req.body.spkac
78+
return new CreateTlsAccountRequest(options)
7579
default:
7680
throw new TypeError('Unsupported authentication scheme')
7781
}
@@ -255,5 +259,112 @@ class CreateOidcAccountRequest extends CreateAccountRequest {
255259
}
256260
}
257261

262+
/**
263+
* Models a Create Account request for a server using WebID-TLS as primary
264+
* authentication mode. Handles generating and saving a TLS certificate, etc.
265+
*
266+
* @class CreateTlsAccountRequest
267+
* @extends CreateAccountRequest
268+
*/
269+
class CreateTlsAccountRequest extends CreateAccountRequest {
270+
/**
271+
* @constructor
272+
*
273+
* @param [options={}] {Object} See `CreateAccountRequest` constructor docstring
274+
* @param [options.spkac] {string}
275+
*/
276+
constructor (options = {}) {
277+
super(options)
278+
this.spkac = options.spkac
279+
this.certificate = null
280+
}
281+
282+
/**
283+
* Generates a new X.509v3 RSA certificate (if `spkac` was passed in) and
284+
* adds it to the user account. Used for storage in an agent's WebID
285+
* Profile, for WebID-TLS authentication.
286+
*
287+
* @param userAccount {UserAccount}
288+
* @param userAccount.webId {string} An agent's WebID URI
289+
*
290+
* @throws {Error} HTTP 400 error if errors were encountering during
291+
* certificate generation.
292+
*
293+
* @return {Promise<UserAccount>} Chainable
294+
*/
295+
generateTlsCertificate (userAccount) {
296+
if (!this.spkac) {
297+
debug('Missing spkac param, not generating cert during account creation')
298+
return Promise.resolve(userAccount)
299+
}
300+
301+
return Promise.resolve()
302+
.then(() => {
303+
let host = this.accountManager.host
304+
return WebIdTlsCertificate.fromSpkacPost(this.spkac, userAccount, host)
305+
.generateCertificate()
306+
})
307+
.catch(err => {
308+
err.status = 400
309+
err.message = 'Error generating a certificate: ' + err.message
310+
throw err
311+
})
312+
.then(certificate => {
313+
debug('Generated a WebID-TLS certificate as part of account creation')
314+
this.certificate = certificate
315+
return userAccount
316+
})
317+
}
318+
319+
/**
320+
* Generates a WebID-TLS certificate and saves it to the user's profile
321+
* graph.
322+
*
323+
* @param userAccount {UserAccount}
324+
*
325+
* @return {Promise<UserAccount>} Chainable
326+
*/
327+
saveCredentialsFor (userAccount) {
328+
return this.generateTlsCertificate(userAccount)
329+
.then(userAccount => {
330+
if (this.certificate) {
331+
return this.accountManager
332+
.addCertKeyToProfile(this.certificate, userAccount)
333+
.then(() => {
334+
debug('Saved generated WebID-TLS certificate to profile')
335+
})
336+
} else {
337+
debug('No certificate generated, no need to save to profile')
338+
}
339+
})
340+
.then(() => {
341+
return userAccount
342+
})
343+
}
344+
345+
/**
346+
* Writes the generated TLS certificate to the http Response object.
347+
*
348+
* @param userAccount {UserAccount}
349+
*
350+
* @return {UserAccount} Chainable
351+
*/
352+
sendResponse (userAccount) {
353+
let res = this.response
354+
res.set('User', userAccount.webId)
355+
res.status(200)
356+
357+
if (this.certificate) {
358+
res.set('Content-Type', 'application/x-x509-user-cert')
359+
res.send(this.certificate.toDER())
360+
} else {
361+
res.end()
362+
}
363+
364+
return userAccount
365+
}
366+
}
367+
258368
module.exports = CreateAccountRequest
259369
module.exports.CreateAccountRequest = CreateAccountRequest
370+
module.exports.CreateTlsAccountRequest = CreateTlsAccountRequest

0 commit comments

Comments
 (0)