WIP on openid
This commit is contained in:
Родитель
4a60edd6b0
Коммит
ff5dd20a6c
|
@ -110,6 +110,7 @@ function main() {
|
|||
customs.close()
|
||||
mailer.stop()
|
||||
database.close()
|
||||
process.exit() //XXX: because of openid dep ಠ_ಠ
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -291,6 +291,12 @@ var conf = convict({
|
|||
format: Boolean,
|
||||
env: 'LOCKOUT_ENABLED',
|
||||
default: false
|
||||
},
|
||||
openIdProviders: {
|
||||
doc: 'root urls of allowed OpenID providers',
|
||||
format: Array,
|
||||
default: [],
|
||||
env: 'OPENID_PROVIDERS'
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -316,4 +322,9 @@ var options = {
|
|||
|
||||
conf.validate(options)
|
||||
|
||||
conf.set('isProduction', conf.get('env') === 'prod')
|
||||
|
||||
conf.set('openIdVerifyUrl', conf.get('publicUrl') + '/v1/account/openid/login')
|
||||
|
||||
|
||||
module.exports = conf
|
||||
|
|
18
lib/db.js
18
lib/db.js
|
@ -368,6 +368,24 @@ module.exports = function (
|
|||
)
|
||||
}
|
||||
|
||||
DB.prototype.openIdRecord = function (id) {
|
||||
log.trace({ op: 'DB.openIdRecord', id: id })
|
||||
return this.pool.get('/openIdRecord/' + Buffer(id, 'utf8').toString('hex'))
|
||||
.then(
|
||||
function (body) {
|
||||
var data = bufferize(body)
|
||||
data.emailVerified = !!data.emailVerified
|
||||
return data
|
||||
},
|
||||
function (err) {
|
||||
if (err.statusCode === 404) {
|
||||
err = error.unknownAccount()
|
||||
}
|
||||
throw err
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
DB.prototype.account = function (uid) {
|
||||
log.trace({ op: 'DB.account', uid: uid })
|
||||
return this.pool.get('/account/' + uid.toString('hex'))
|
||||
|
|
|
@ -7,6 +7,9 @@ var HEX_STRING = validators.HEX_STRING
|
|||
var BASE64_JWT = validators.BASE64_JWT
|
||||
|
||||
var butil = require('../crypto/butil')
|
||||
var openid = require('openid')
|
||||
var url = require('url')
|
||||
var qs = require('querystring')
|
||||
|
||||
module.exports = function (
|
||||
log,
|
||||
|
@ -18,16 +21,28 @@ module.exports = function (
|
|||
db,
|
||||
mailer,
|
||||
Password,
|
||||
redirectDomain,
|
||||
verifierVersion,
|
||||
isProduction,
|
||||
domain,
|
||||
resendBlackoutPeriod,
|
||||
config,
|
||||
customs,
|
||||
isPreVerified,
|
||||
checkPassword
|
||||
) {
|
||||
|
||||
var openIdExtensions = [
|
||||
new openid.AttributeExchange(
|
||||
{
|
||||
'http://axschema.org/contact/email': 'optional'
|
||||
}
|
||||
)
|
||||
]
|
||||
|
||||
function isOpenIdProviderAllowed(id) {
|
||||
return config.openIdProviders.some(
|
||||
function (allowed) {
|
||||
return id.indexOf(allowed) === 0
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
var routes = [
|
||||
{
|
||||
method: 'POST',
|
||||
|
@ -39,7 +54,7 @@ module.exports = function (
|
|||
authPW: isA.string().min(64).max(64).regex(HEX_STRING).required(),
|
||||
preVerified: isA.boolean(),
|
||||
service: isA.string().max(16).alphanum().optional(),
|
||||
redirectTo: validators.redirectTo(redirectDomain).optional(),
|
||||
redirectTo: validators.redirectTo(config.smtp.redirectDomain).optional(),
|
||||
resume: isA.string().max(2048).optional(),
|
||||
preVerifyToken: isA.string().max(2048).regex(BASE64_JWT).optional()
|
||||
}
|
||||
|
@ -74,7 +89,7 @@ module.exports = function (
|
|||
.then(isPreVerified.bind(null, form.email, form.preVerifyToken))
|
||||
.then(
|
||||
function (preverified) {
|
||||
var password = new Password(authPW, authSalt, verifierVersion)
|
||||
var password = new Password(authPW, authSalt, config.verifierVersion)
|
||||
return password.verifyHash()
|
||||
.then(
|
||||
function (verifyHash) {
|
||||
|
@ -331,6 +346,196 @@ module.exports = function (
|
|||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/account/openid/authenticate',
|
||||
handler: function (request, reply) {
|
||||
var id = request.query.identifier
|
||||
if (!isOpenIdProviderAllowed(id)) {
|
||||
log.info({ op: 'Account.openid.authenticate', id: id })
|
||||
return reply.redirect(
|
||||
config.contentServer.url + '/openid?' + qs.stringify(
|
||||
{
|
||||
err: 'This OpenID Provider is not allowed'
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
openid.authenticate(
|
||||
id,
|
||||
config.openIdVerifyUrl,
|
||||
null, // realm
|
||||
false, // immediate
|
||||
false, // stateless
|
||||
function (err, authUrl) {
|
||||
if (err) {
|
||||
log.error({ op: 'Account.openid.authenticate', err: err })
|
||||
return reply.redirect(
|
||||
config.contentServer.url + '/openid?' + qs.stringify(
|
||||
{
|
||||
err: err.message
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
reply.redirect(authUrl)
|
||||
},
|
||||
openIdExtensions,
|
||||
false // strict
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/account/openid/login',
|
||||
handler: function (request, reply) {
|
||||
if (!request.url.search) {
|
||||
return reply(
|
||||
'<?xml version="1.0" encoding="UTF-8"?>\n'
|
||||
+ '<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)"><XRD>'
|
||||
+ '<Service xmlns="xri://$xrd*($v*2.0)">'
|
||||
+ '<Type>http://specs.openid.net/auth/2.0/return_to</Type>'
|
||||
+ '<URI>' + config.openIdVerifyUrl + '</URI>'
|
||||
+ '</Service></XRD></xrds:XRDS>'
|
||||
).type('application/xrds+xml')
|
||||
}
|
||||
log.info({ op: 'Account.openid', url: request.url })
|
||||
openid.verifyAssertion(
|
||||
url.format(request.url),
|
||||
function (err, assertion) {
|
||||
if (err || !assertion || !assertion.authenticated) {
|
||||
log.warn({ op: 'Account.openid', err: err, assertion: assertion })
|
||||
return reply.redirect(
|
||||
config.contentServer.url + '/openid?err=Unknown%20Account'
|
||||
)
|
||||
}
|
||||
var id = assertion.claimedIdentifier
|
||||
var locale = request.app.acceptLanguage
|
||||
|
||||
if (!isOpenIdProviderAllowed(id)) {
|
||||
log.warn({op: 'Account.openid', id: id })
|
||||
return reply.redirect(
|
||||
config.contentServer.url + '/openid?' + qs.stringify(
|
||||
{
|
||||
err: 'This OpenID Provider is not allowed'
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
db.openIdRecord(id)
|
||||
.then(
|
||||
function (record) {
|
||||
return record
|
||||
},
|
||||
function (err) {
|
||||
if (err.errno !== 102) {
|
||||
throw err
|
||||
}
|
||||
var uid = uuid.v4('binary')
|
||||
var email = assertion.email || uid.toString('hex') + '@firefox.com'
|
||||
var authSalt = crypto.randomBytes(32)
|
||||
var kA = crypto.randomBytes(32)
|
||||
return db.createAccount(
|
||||
{
|
||||
uid: uid,
|
||||
createdAt: Date.now(),
|
||||
email: email,
|
||||
emailCode: crypto.randomBytes(16),
|
||||
emailVerified: true,
|
||||
kA: kA,
|
||||
wrapWrapKb: crypto.randomBytes(32),
|
||||
accountResetToken: null,
|
||||
passwordForgotToken: null,
|
||||
authSalt: authSalt,
|
||||
verifierVersion: 0,
|
||||
verifyHash: crypto.randomBytes(32),
|
||||
openId: id,
|
||||
verifierSetAt: Date.now(),
|
||||
locale: locale
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (account) {
|
||||
return db.createSessionToken(
|
||||
{
|
||||
uid: account.uid,
|
||||
email: account.email,
|
||||
emailCode: account.emailCode,
|
||||
emailVerified: true,
|
||||
verifierSetAt: account.verifierSetAt
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (sessionToken) {
|
||||
return db.createKeyFetchToken(
|
||||
{
|
||||
uid: account.uid,
|
||||
kA: account.kA,
|
||||
// wrapKb is undefined without a password
|
||||
// wrapWrapKb has the properties we need for this
|
||||
// value; Its stable, random, and will change on
|
||||
// account reset.
|
||||
wrapKb: account.wrapWrapKb,
|
||||
emailVerified: true
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (keyFetchToken) {
|
||||
return {
|
||||
sessionToken: sessionToken,
|
||||
keyFetchToken: keyFetchToken,
|
||||
unwrapBKey: butil.xorBuffers(
|
||||
account.kA,
|
||||
account.wrapWrapKb
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (tokens) {
|
||||
reply.redirect(
|
||||
config.contentServer.url + '/openid?' +
|
||||
qs.stringify(
|
||||
{
|
||||
uid: tokens.sessionToken.uid.toString('hex'),
|
||||
email: account.email,
|
||||
session: tokens.sessionToken.data.toString('hex'),
|
||||
key: tokens.keyFetchToken ?
|
||||
tokens.keyFetchToken.data.toString('hex')
|
||||
: undefined,
|
||||
unwrap: tokens.unwrapBKey.toString('hex'),
|
||||
service: 'sync',
|
||||
context: 'fx_desktop_v2'
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
.catch(
|
||||
function (err) {
|
||||
log.error({ op: 'Account.openid', err: err })
|
||||
reply.redirect(
|
||||
config.contentServer.url + '/openid?' + qs.stringify(
|
||||
{
|
||||
err: err.toString()
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
false, // stateless
|
||||
openIdExtensions,
|
||||
false // strict
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/account/status',
|
||||
|
@ -438,7 +643,7 @@ module.exports = function (
|
|||
validate: {
|
||||
payload: {
|
||||
service: isA.string().max(16).alphanum().optional(),
|
||||
redirectTo: validators.redirectTo(redirectDomain).optional(),
|
||||
redirectTo: validators.redirectTo(config.smtp.redirectDomain).optional(),
|
||||
resume: isA.string().max(2048).optional()
|
||||
}
|
||||
}
|
||||
|
@ -449,7 +654,7 @@ module.exports = function (
|
|||
db.updateSessionTokenInBackground(sessionToken, request.headers['user-agent'])
|
||||
var service = request.payload.service || request.query.service
|
||||
if (sessionToken.emailVerified ||
|
||||
Date.now() - sessionToken.verifierSetAt < resendBlackoutPeriod) {
|
||||
Date.now() - sessionToken.verifierSetAt < config.smtp.resendBlackoutPeriod) {
|
||||
return reply({})
|
||||
}
|
||||
customs.check(
|
||||
|
@ -523,7 +728,7 @@ module.exports = function (
|
|||
payload: {
|
||||
email: validators.email().required(),
|
||||
service: isA.string().max(16).alphanum().optional(),
|
||||
redirectTo: validators.redirectTo(redirectDomain).optional(),
|
||||
redirectTo: validators.redirectTo(config.smtp.redirectDomain).optional(),
|
||||
resume: isA.string().max(2048).optional()
|
||||
}
|
||||
}
|
||||
|
@ -639,7 +844,7 @@ module.exports = function (
|
|||
var accountResetToken = request.auth.credentials
|
||||
var authPW = Buffer(request.payload.authPW, 'hex')
|
||||
var authSalt = crypto.randomBytes(32)
|
||||
var password = new Password(authPW, authSalt, verifierVersion)
|
||||
var password = new Password(authPW, authSalt, config.verifierVersion)
|
||||
return password.verifyHash()
|
||||
.then(
|
||||
function (verifyHash) {
|
||||
|
@ -705,7 +910,7 @@ module.exports = function (
|
|||
)
|
||||
.then(
|
||||
function () {
|
||||
log.event('delete', { uid: emailRecord.uid.toString('hex') + '@' + domain })
|
||||
log.event('delete', { uid: emailRecord.uid.toString('hex') + '@' + config.domain })
|
||||
return {}
|
||||
}
|
||||
)
|
||||
|
@ -716,7 +921,7 @@ module.exports = function (
|
|||
}
|
||||
]
|
||||
|
||||
if (isProduction) {
|
||||
if (config.isProduction) {
|
||||
delete routes[0].config.validate.payload.preVerified
|
||||
} else {
|
||||
// programmatic account lockout is only available in non-production mode.
|
||||
|
|
|
@ -20,7 +20,6 @@ module.exports = function (
|
|||
config,
|
||||
customs
|
||||
) {
|
||||
var isProduction = config.env === 'prod'
|
||||
var isPreVerified = require('../preverifier')(error, config)
|
||||
var defaults = require('./defaults')(log, P, db, error)
|
||||
var idp = require('./idp')(log, serverPublicKey)
|
||||
|
@ -35,11 +34,7 @@ module.exports = function (
|
|||
db,
|
||||
mailer,
|
||||
Password,
|
||||
config.smtp.redirectDomain,
|
||||
config.verifierVersion,
|
||||
isProduction,
|
||||
config.domain,
|
||||
config.smtp.resendBlackoutPeriod,
|
||||
config,
|
||||
customs,
|
||||
isPreVerified,
|
||||
checkPassword
|
||||
|
|
|
@ -317,7 +317,7 @@
|
|||
"dependencies": {
|
||||
"esprima": {
|
||||
"version": "1.0.4",
|
||||
"from": "esprima@>=1.0.4 <1.1.0",
|
||||
"from": "esprima@>=1.0.2 <1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz"
|
||||
}
|
||||
}
|
||||
|
@ -352,8 +352,8 @@
|
|||
},
|
||||
"fxa-auth-db-mysql": {
|
||||
"version": "0.42.0",
|
||||
"from": "git+https://github.com/mozilla/fxa-auth-db-mysql.git#a09790c20fa8c9f4a2c6f108b86aabf07038220f",
|
||||
"resolved": "git+https://github.com/mozilla/fxa-auth-db-mysql.git#a09790c20fa8c9f4a2c6f108b86aabf07038220f",
|
||||
"from": "git+https://github.com/mozilla/fxa-auth-db-mysql.git#master",
|
||||
"resolved": "git+https://github.com/mozilla/fxa-auth-db-mysql.git#354fc512d181b002b7aa62740889eaf60511a2d7",
|
||||
"dependencies": {
|
||||
"bluebird": {
|
||||
"version": "2.1.3",
|
||||
|
@ -885,7 +885,7 @@
|
|||
},
|
||||
"isarray": {
|
||||
"version": "0.0.1",
|
||||
"from": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
"from": "isarray@0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
|
||||
},
|
||||
"string_decoder": {
|
||||
|
@ -1308,7 +1308,7 @@
|
|||
},
|
||||
"fxa-auth-mailer": {
|
||||
"version": "1.0.7",
|
||||
"from": "git+https://github.com/mozilla/fxa-auth-mailer.git#6aac195ff6d1692b239c7906fd1becd95ee85ebf",
|
||||
"from": "git+https://github.com/mozilla/fxa-auth-mailer.git#master",
|
||||
"resolved": "git+https://github.com/mozilla/fxa-auth-mailer.git#6aac195ff6d1692b239c7906fd1becd95ee85ebf",
|
||||
"dependencies": {
|
||||
"bluebird": {
|
||||
|
@ -1421,8 +1421,8 @@
|
|||
},
|
||||
"fxa-content-server-l10n": {
|
||||
"version": "0.0.0",
|
||||
"from": "git://github.com/mozilla/fxa-content-server-l10n.git#4bbc8a6a71e45cf0abcf6d4de2219c6b0488c0e6",
|
||||
"resolved": "git://github.com/mozilla/fxa-content-server-l10n.git#4bbc8a6a71e45cf0abcf6d4de2219c6b0488c0e6"
|
||||
"from": "git://github.com/mozilla/fxa-content-server-l10n.git",
|
||||
"resolved": "git://github.com/mozilla/fxa-content-server-l10n.git#e89599581ac4b362b8bde684565712e22195647c"
|
||||
},
|
||||
"handlebars": {
|
||||
"version": "1.3.0",
|
||||
|
@ -2153,11 +2153,6 @@
|
|||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dtrace-provider": {
|
||||
"version": "0.2.8",
|
||||
"from": "dtrace-provider@>=0.2.8 <0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.2.8.tgz"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2180,7 +2175,7 @@
|
|||
"dependencies": {
|
||||
"encoding": {
|
||||
"version": "0.1.11",
|
||||
"from": "encoding@>=0.1.11 <0.2.0",
|
||||
"from": "encoding@*",
|
||||
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.11.tgz",
|
||||
"dependencies": {
|
||||
"iconv-lite": {
|
||||
|
@ -2647,7 +2642,7 @@
|
|||
},
|
||||
"es5-ext": {
|
||||
"version": "0.10.7",
|
||||
"from": "es5-ext@>=0.10.6 <0.11.0",
|
||||
"from": "es5-ext@>=0.10.4 <0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.7.tgz",
|
||||
"dependencies": {
|
||||
"es6-symbol": {
|
||||
|
@ -2659,7 +2654,7 @@
|
|||
},
|
||||
"es6-iterator": {
|
||||
"version": "0.1.3",
|
||||
"from": "es6-iterator@>=0.1.1 <0.2.0",
|
||||
"from": "es6-iterator@>=0.1.3 <0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-0.1.3.tgz",
|
||||
"dependencies": {
|
||||
"es6-symbol": {
|
||||
|
@ -2698,12 +2693,12 @@
|
|||
},
|
||||
"es5-ext": {
|
||||
"version": "0.10.7",
|
||||
"from": "es5-ext@>=0.10.6 <0.11.0",
|
||||
"from": "es5-ext@>=0.10.4 <0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.7.tgz"
|
||||
},
|
||||
"es6-iterator": {
|
||||
"version": "0.1.3",
|
||||
"from": "es6-iterator@>=0.1.1 <0.2.0",
|
||||
"from": "es6-iterator@>=0.1.3 <0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-0.1.3.tgz"
|
||||
},
|
||||
"es6-symbol": {
|
||||
|
@ -2726,9 +2721,9 @@
|
|||
}
|
||||
},
|
||||
"espree": {
|
||||
"version": "2.2.3",
|
||||
"version": "2.2.4",
|
||||
"from": "espree@>=2.0.1 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-2.2.3.tgz"
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-2.2.4.tgz"
|
||||
},
|
||||
"estraverse": {
|
||||
"version": "2.0.0",
|
||||
|
@ -2741,9 +2736,9 @@
|
|||
"resolved": "https://registry.npmjs.org/estraverse-fb/-/estraverse-fb-1.3.1.tgz"
|
||||
},
|
||||
"globals": {
|
||||
"version": "8.3.0",
|
||||
"version": "8.4.0",
|
||||
"from": "globals@>=8.0.0 <9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-8.3.0.tgz"
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-8.4.0.tgz"
|
||||
},
|
||||
"inquirer": {
|
||||
"version": "0.8.5",
|
||||
|
@ -2929,9 +2924,9 @@
|
|||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.2.5.tgz"
|
||||
},
|
||||
"fast-levenshtein": {
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"from": "fast-levenshtein@>=1.0.0 <1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.0.6.tgz"
|
||||
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.0.7.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -3414,7 +3409,7 @@
|
|||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "2.0.1",
|
||||
"from": "inherits@>=2.0.1 <2.1.0",
|
||||
"from": "inherits@>=2.0.1 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||
},
|
||||
"typedarray": {
|
||||
|
@ -3722,7 +3717,7 @@
|
|||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "0.2.1",
|
||||
"from": "ansi-regex@>=0.2.1 <0.3.0",
|
||||
"from": "ansi-regex@>=0.2.0 <0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz"
|
||||
}
|
||||
}
|
||||
|
@ -3734,7 +3729,7 @@
|
|||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "0.2.1",
|
||||
"from": "ansi-regex@>=0.2.1 <0.3.0",
|
||||
"from": "ansi-regex@>=0.2.0 <0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz"
|
||||
}
|
||||
}
|
||||
|
@ -3845,6 +3840,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"openid": {
|
||||
"version": "0.5.13",
|
||||
"from": "openid@0.5.13",
|
||||
"resolved": "https://registry.npmjs.org/openid/-/openid-0.5.13.tgz"
|
||||
},
|
||||
"pem-jwk": {
|
||||
"version": "1.5.1",
|
||||
"from": "pem-jwk@1.5.1",
|
||||
|
@ -3857,7 +3857,7 @@
|
|||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "2.0.1",
|
||||
"from": "inherits@>=2.0.0 <3.0.0",
|
||||
"from": "inherits@>=2.0.1 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||
},
|
||||
"minimalistic-assert": {
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
"hkdf": "0.0.2",
|
||||
"joi": "6.4.1",
|
||||
"mozlog": "2.0.1",
|
||||
"openid": "0.5.13",
|
||||
"poolee": "1.0.0",
|
||||
"request": "2.55.0",
|
||||
"scrypt-hash": "1.1.12",
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var test = require('../ptaptest')
|
||||
var TestServer = require('../test_server')
|
||||
var qs = require('querystring')
|
||||
var url = require('url')
|
||||
var Pool = require('poolee')
|
||||
|
||||
process.env.OPENID_PROVIDERS = 'https://me.yahoo.com,https://openid.example.com'
|
||||
var config = require('../../config').getProperties()
|
||||
|
||||
TestServer.start(config)
|
||||
.then(function main(server) {
|
||||
|
||||
test(
|
||||
'openid authenticate redirects to provider',
|
||||
function (t) {
|
||||
Pool.request(
|
||||
config.publicUrl + '/v1/account/openid/authenticate?' + qs.stringify(
|
||||
{
|
||||
identifier: 'https://me.yahoo.com'
|
||||
}
|
||||
),
|
||||
function (err, res) {
|
||||
t.equal(res.statusCode, 302)
|
||||
var location = url.parse(res.headers.location)
|
||||
t.equal(location.hostname, 'open.login.yahooapis.com')
|
||||
t.end()
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'openid authenticate rejects providers not on the allow list',
|
||||
function (t) {
|
||||
Pool.request(
|
||||
config.publicUrl + '/v1/account/openid/authenticate?' + qs.stringify(
|
||||
{
|
||||
identifier: 'https://example.com'
|
||||
}
|
||||
),
|
||||
function (err, res) {
|
||||
t.equal(res.statusCode, 302)
|
||||
var location = url.parse(res.headers.location, true)
|
||||
t.equal(location.query.err, 'This OpenID Provider is not allowed')
|
||||
t.end()
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'openid authenticate rejects allowed but invalid providers',
|
||||
function (t) {
|
||||
// this should never happen in real life
|
||||
Pool.request(
|
||||
config.publicUrl + '/v1/account/openid/authenticate?' + qs.stringify(
|
||||
{
|
||||
identifier: 'https://openid.example.com'
|
||||
}
|
||||
),
|
||||
function (err, res) {
|
||||
t.equal(res.statusCode, 302)
|
||||
var location = url.parse(res.headers.location, true)
|
||||
t.equal(location.query.err, 'No providers found for the given identifier')
|
||||
t.end()
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'bare request to openid login returns the XRDS doc',
|
||||
function (t) {
|
||||
Pool.request(
|
||||
config.publicUrl + '/v1/account/openid/login',
|
||||
function (err, res, body) {
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.headers['content-type'], 'application/xrds+xml')
|
||||
t.end()
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'openid login rejects invalid assertions',
|
||||
function (t) {
|
||||
Pool.request(
|
||||
config.publicUrl + '/v1/account/openid/login?foo=bar',
|
||||
function (err, res) {
|
||||
t.equal(res.statusCode, 302)
|
||||
var location = url.parse(res.headers.location, true)
|
||||
t.equal(location.query.err, 'Unknown Account')
|
||||
t.end()
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
// test(
|
||||
// 'openid login rejects valid assertions for non-allowed providers'
|
||||
// )
|
||||
|
||||
|
||||
test(
|
||||
'teardown',
|
||||
function (t) {
|
||||
server.stop()
|
||||
t.end()
|
||||
}
|
||||
)
|
||||
})
|
|
@ -34,6 +34,9 @@ require('ass')
|
|||
var tap = require('tap')
|
||||
|
||||
module.exports = function(name, testfunc) {
|
||||
if (!testfunc) {
|
||||
return tap.test(name)
|
||||
}
|
||||
var wrappedtestfunc = function(t) {
|
||||
var res = testfunc(t)
|
||||
if (typeof res !== 'undefined') {
|
||||
|
|
Загрузка…
Ссылка в новой задаче