POC for eth-auth

parent d1bd48bc
......@@ -196,6 +196,7 @@ app.locals.authProviders = {
oauth2: config.isOAuth2Enable,
oauth2ProviderName: config.oauth2.providerName,
openID: config.isOpenIDEnable,
ethAuth: config.isEthAuthEnable,
email: config.isEmailEnable,
allowEmailRegister: config.allowEmailRegister
}
......
......@@ -148,6 +148,13 @@ module.exports = {
email: undefined
}
},
ethAuth: {
enable: false,
signature: 'Signature',
message: 'Message',
address: 'Address',
banner: 'codimd-eth-auth'
},
email: true,
allowEmailRegister: true,
allowGravatar: true,
......
......@@ -125,6 +125,13 @@ module.exports = {
email: process.env.CMD_SAML_ATTRIBUTE_EMAIL
}
},
ethAuth: {
enable: toBooleanConfig(process.env.CMD_ETHAUTH_ENABLE),
signature: process.env.CMD_ETHAUTH_SIGNATURE,
message: process.env.CMD_ETHAUTH_MESSAGE,
address: process.env.CMD_ETHAUTH_ADDRESS,
banner: process.env.CMD_ETHAUTH_BANNER
},
email: toBooleanConfig(process.env.CMD_EMAIL),
allowEmailRegister: toBooleanConfig(process.env.CMD_ALLOW_EMAIL_REGISTER),
allowGravatar: toBooleanConfig(process.env.CMD_ALLOW_GRAVATAR),
......
......@@ -120,6 +120,7 @@ config.isMattermostEnable = config.mattermost.clientID && config.mattermost.clie
config.isLDAPEnable = config.ldap.url
config.isSAMLEnable = config.saml.idpSsoUrl
config.isOAuth2Enable = config.oauth2.clientID && config.oauth2.clientSecret
config.isEthAuthEnable = config.ethAuth.enable
config.isPDFExportEnable = config.allowPDFExport
// Check gitlab api version
......
'use strict'
const Router = require('express').Router
const passport = require('passport')
const EthAuth = require('node-eth-auth')
const config = require('../../../config')
const models = require('../../../models')
const logger = require('../../../logger')
const {
setReturnToFromReferer
} = require('../utils')
let ethAuth = module.exports = Router()
class EthAuthStreategy extends passport.Strategy {
constructor (options, verify) {
if (typeof options === 'function') {
verify = options
options = undefined
}
options = options || {}
super(options)
this.name = options.name || 'eth-auth'
this._verify = verify
this._passReqToCallback = options.passReqToCallback || false
}
authenticate (req) {
const verified = (error, user, info) => {
if (error) {
return this.error(error)
}
if (!user) {
return this.fail(info)
}
this.success(user, info)
}
try {
if (this._passReqToCallback && req) {
this._verify(req, verified)
} else {
this._verify(verified)
}
} catch (e) {
return this.error(e)
}
}
}
passport.use(new EthAuthStreategy({
passReqToCallback: true
}, function (req, done) {
const address = req.ethAuth.recoveredAddress
if (!address) {
return done(new Error('EthAuth failed'), null)
}
// construct profile
const profile = {
provider: 'ethAuth',
id: `ethAuth-${address}`,
emails: []
}
const stringifiedProfile = JSON.stringify(profile)
models.User.findOrCreate({
where: {
profileid: address
},
defaults: {
profile: stringifiedProfile
}
}).spread(function (user, created) {
if (user) {
var needSave = false
if (user.profile !== stringifiedProfile) {
user.profile = stringifiedProfile
needSave = true
}
if (needSave) {
user.save().then(function () {
if (config.debug) { logger.debug('user login: ' + user.id) }
return done(null, user)
})
} else {
if (config.debug) { logger.debug('user login: ' + user.id) }
return done(null, user)
}
}
}).catch(function (err) {
logger.error('eth auth failed: ' + err)
return done(err, null)
})
}))
// TODO: read this from config
const { signature, message, address, banner } = config.ethAuth
let ethAuthMiddleware = new EthAuth({ signature, message, address, banner })
ethAuth.get('/auth/ethAuth/:Address', ethAuthMiddleware, function (req, res) {
return req.ethAuth.message ? res.send(req.ethAuth.message) : res.status(400).send()
})
ethAuth.post('/auth/ethAuth/:Message/:Signature', ethAuthMiddleware, function (req, res, next) {
setReturnToFromReferer(req)
passport.authenticate('eth-auth', {
successReturnToOrRedirect: true
})(req, res, next)
})
......@@ -46,6 +46,7 @@ if (config.isSAMLEnable) authRouter.use(require('./saml'))
if (config.isOAuth2Enable) authRouter.use(require('./oauth2'))
if (config.isEmailEnable) authRouter.use(require('./email'))
if (config.isOpenIDEnable) authRouter.use(require('./openid'))
if (config.isEthAuthEnable) authRouter.use(require('./eth-auth'))
// logout
authRouter.get('/logout', function (req, res) {
......
/* eslint-env browser, jquery */
/* global moment, serverurl */
import './lib/eth-auth-helper'
import {
checkIfAuth,
clearLoginState,
......
/* global web3 */
const { serverurl } = require('./config')
let data
let message
let signature
$('#eth-auth-login').click(function () {
// #region Detect metamask
if (typeof web3 !== 'undefined') {
console.log('web3 is detected.')
if (web3.currentProvider.isMetaMask === true) {
if (web3.eth.accounts[0] === undefined && !web3.currentProvider.enable) {
return alert('Please login metamask first.')
}
}
} else {
return alert('No web3 detected. Please install metamask')
}
// #endregion
function authStart () {
return $.get(`${serverurl}/auth/ethAuth/${web3.eth.accounts[0]}`, res => {
data = ''
message = ''
const method = 'eth_signTypedData' // $('#method')[0].value
if (method === 'personal_sign') {
data = '0x' + Array.from(res).map(x => x.charCodeAt(0).toString(16)).join('')
message = res
} else if (method === 'eth_signTypedData') {
data = res
message = res[1].value
}
// Call metamask to sign
const from = web3.eth.accounts[0]
const params = [data, from]
web3.currentProvider.sendAsync({
method,
params,
from
}, async (err, result) => {
if (err) {
return console.error(err)
}
if (result.error) {
return console.error(result.error)
}
signature = result.result
if (message !== null && signature !== null) {
// TODO: submit the form with form tag
const form = document.createElement('form')
document.body.appendChild(form)
form.method = 'post'
form.action = `${serverurl}/auth/ethAuth/${message}/${signature}`
form.submit()
}
})
}).fail(function () {
// TODO: flash error
})
}
if (web3.currentProvider.enable) {
web3.currentProvider.enable()
.then(function () {
authStart()
})
} else if (web3.eth.accounts[0]) {
authStart()
}
})
......@@ -53,6 +53,11 @@
<i class="fa fa-mail-forward"></i> <%= __('Sign in via %s', authProviders.oauth2ProviderName || 'OAuth2') %>
</a>
<% } %>
<% if (authProviders.ethAuth) { %>
<button type="button" id="eth-auth-login" class="btn btn-lg btn-block btn-social btn-reddit">
<i class="fa fa-btc"></i> <%= __('Sign in via %s', 'Eth Auth') %>
</button>
<% } %>
<% if ((authProviders.facebook || authProviders.twitter || authProviders.github || authProviders.gitlab || authProviders.mattermost || authProviders.dropbox || authProviders.google || authProviders.saml || authProviders.oauth2) && authProviders.ldap) { %>
<hr>
<% }%>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment