Skip to content

Commit

Permalink
Implement web3 frontend eauth
Browse files Browse the repository at this point in the history
Signed-off-by: Yukai Huang <yukaihuangtw@gmail.com>
  • Loading branch information
Yukaii committed Jul 13, 2020
1 parent f98994b commit 876b5da
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 3 deletions.
1 change: 1 addition & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ app.locals.authProviders = {
oauth2: config.isOAuth2Enable,
oauth2ProviderName: config.oauth2.providerName,
openID: config.isOpenIDEnable,
eauth: config.isEAuthEnable,
email: config.isEmailEnable,
allowEmailRegister: config.allowEmailRegister
}
Expand Down
115 changes: 115 additions & 0 deletions lib/auth/eauth/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
'use strict'

const Router = require('express').Router
const passport = require('passport')
const Eauth = require('express-eauth')

const config = require('../../config')
const models = require('../../models')
const logger = require('../../logger')
const {
setReturnToFromReferer
} = require('../utils')

const eauth = module.exports = Router()

class EAuthStreategy extends passport.Strategy {
constructor (options, verify) {
if (typeof options === 'function') {
verify = options
options = undefined
}

options = options || {}

super(options)

this.name = options.name || 'eauth'
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 EAuthStreategy({
passReqToCallback: true
}, function (req, done) {
const address = req.eauth.recoveredAddress
if (!address) {
return done(new Error('EAuth failed'), null)
}

// construct profile
const profile = {
provider: 'eauth',
id: `eauth-${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)
})
}))

const { signature, message, address, banner } = config.eauth
const eauthMiddleware = new Eauth({ signature, message, address, banner })

eauth.get('/auth/eauth/:Address', eauthMiddleware, function (req, res) {
return req.eauth.message ? res.send(req.eauth.message) : res.status(400).send()
})

eauth.post('/auth/eauth/:Message/:Signature', eauthMiddleware, function (req, res, next) {
setReturnToFromReferer(req)
passport.authenticate('eauth', {
successReturnToOrRedirect: true
})(req, res, next)
})
1 change: 1 addition & 0 deletions lib/auth/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,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.isEAuthEnable) authRouter.use(require('./eauth'))

// logout
authRouter.get('/logout', function (req, res) {
Expand Down
7 changes: 7 additions & 0 deletions lib/config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,13 @@ module.exports = {
email: undefined
}
},
eauth: {
enable: false,
signature: 'Signature',
message: 'Message',
address: 'Address',
banner: 'codimd-eauth'
},
plantuml: {
server: 'https://www.plantuml.com/plantuml'
},
Expand Down
7 changes: 7 additions & 0 deletions lib/config/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ module.exports = {
email: process.env.CMD_SAML_ATTRIBUTE_EMAIL
}
},
eauth: {
enable: toBooleanConfig(process.env.CMD_EAUTH_ENABLE),
signature: process.env.CMD_EAUTH_SIGNATURE,
message: process.env.CMD_EAUTH_MESSAGE,
address: process.env.CMD_EAUTH_ADDRESS,
banner: process.env.CMD_EAUTH_BANNER
},
plantuml: {
server: process.env.CMD_PLANTUML_SERVER
},
Expand Down
1 change: 1 addition & 0 deletions lib/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,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.isEAuthEnable = config.eauth.enable
config.isPDFExportEnable = config.allowPDFExport

// Check gitlab api version
Expand Down
2 changes: 2 additions & 0 deletions public/js/cover.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/* eslint-env browser, jquery */
/* global moment, serverurl */

import './lib/eauth-helper'

import {
checkIfAuth,
clearLoginState,
Expand Down
75 changes: 75 additions & 0 deletions public/js/lib/eauth-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* global web3, $ */

const { serverurl } = require('./config')

let data
let message
let signature

$('#eth-auth-login').on('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 window.alert('Please login metamask first.')
}
}
} else {
return window.alert('No web3 detected. Please install metamask')
}
// #endregion

function authStart () {
return $.get(`${serverurl}/auth/eauth/${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) {
const form = document.createElement('form')
document.body.appendChild(form)
form.method = 'post'
form.action = `${serverurl}/auth/eauth/${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()
}
})
11 changes: 8 additions & 3 deletions public/views/shared/signin-modal.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,12 @@
<i class="fa fa-mail-forward"></i> <%= __('Sign in via %s', authProviders.oauth2ProviderName || 'OAuth2') %>
</a>
<% } %>
<% if ((authProviders.facebook || authProviders.twitter || authProviders.github || authProviders.bitbucket || authProviders.gitlab || authProviders.mattermost || authProviders.dropbox || authProviders.google || authProviders.saml || authProviders.oauth2) && authProviders.ldap) { %>
<% if (authProviders.eauth) { %>
<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.bitbucket || authProviders.gitlab || authProviders.mattermost || authProviders.dropbox || authProviders.google || authProviders.saml || authProviders.oauth2 || authProviders.eauth) && authProviders.ldap) { %>
<hr>
<% }%>
<% if (authProviders.ldap) { %>
Expand All @@ -83,7 +88,7 @@
</div>
</form>
<% } %>
<% if ((authProviders.facebook || authProviders.twitter || authProviders.github || authProviders.bitbucket || authProviders.gitlab || authProviders.mattermost || authProviders.dropbox || authProviders.google || authProviders.ldap || authProviders.oauth2) && authProviders.openID) { %>
<% if ((authProviders.facebook || authProviders.twitter || authProviders.github || authProviders.bitbucket || authProviders.gitlab || authProviders.mattermost || authProviders.dropbox || authProviders.google || authProviders.ldap || authProviders.oauth2 || authProviders.eauth) && authProviders.openID) { %>
<hr>
<% }%>
<% if (authProviders.openID) { %>
Expand All @@ -102,7 +107,7 @@
</div>
</form>
<% } %>
<% if ((authProviders.facebook || authProviders.twitter || authProviders.github|| authProviders.bitbucket || authProviders.gitlab || authProviders.mattermost || authProviders.dropbox || authProviders.google || authProviders.ldap || authProviders.oauth2 || authProviders.openID) && authProviders.email) { %>
<% if ((authProviders.facebook || authProviders.twitter || authProviders.github|| authProviders.bitbucket || authProviders.gitlab || authProviders.mattermost || authProviders.dropbox || authProviders.google || authProviders.ldap || authProviders.oauth2 || authProviders.openID || authProviders.eauth) && authProviders.email) { %>
<hr>
<% }%>
<% if (authProviders.email) { %>
Expand Down

0 comments on commit 876b5da

Please sign in to comment.