mirror of https://github.com/status-im/codimd.git
Implement web3 frontend eauth
Signed-off-by: Yukai Huang <yukaihuangtw@gmail.com>
This commit is contained in:
parent
f98994b9a7
commit
0d405d1939
1
app.js
1
app.js
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
})
|
|
@ -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) {
|
||||
|
|
|
@ -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'
|
||||
},
|
||||
|
|
|
@ -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
|
||||
},
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
/* eslint-env browser, jquery */
|
||||
/* global moment, serverurl */
|
||||
|
||||
import './lib/eauth-helper'
|
||||
|
||||
import {
|
||||
checkIfAuth,
|
||||
clearLoginState,
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
})
|
|
@ -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', 'Eauth') %>
|
||||
</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) { %>
|
||||
|
@ -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) { %>
|
||||
|
@ -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) { %>
|
||||
|
|
Loading…
Reference in New Issue