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,
|
oauth2: config.isOAuth2Enable,
|
||||||
oauth2ProviderName: config.oauth2.providerName,
|
oauth2ProviderName: config.oauth2.providerName,
|
||||||
openID: config.isOpenIDEnable,
|
openID: config.isOpenIDEnable,
|
||||||
|
eauth: config.isEAuthEnable,
|
||||||
email: config.isEmailEnable,
|
email: config.isEmailEnable,
|
||||||
allowEmailRegister: config.allowEmailRegister
|
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.isOAuth2Enable) authRouter.use(require('./oauth2'))
|
||||||
if (config.isEmailEnable) authRouter.use(require('./email'))
|
if (config.isEmailEnable) authRouter.use(require('./email'))
|
||||||
if (config.isOpenIDEnable) authRouter.use(require('./openid'))
|
if (config.isOpenIDEnable) authRouter.use(require('./openid'))
|
||||||
|
if (config.isEAuthEnable) authRouter.use(require('./eauth'))
|
||||||
|
|
||||||
// logout
|
// logout
|
||||||
authRouter.get('/logout', function (req, res) {
|
authRouter.get('/logout', function (req, res) {
|
||||||
|
|
|
@ -165,6 +165,13 @@ module.exports = {
|
||||||
email: undefined
|
email: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
eauth: {
|
||||||
|
enable: false,
|
||||||
|
signature: 'Signature',
|
||||||
|
message: 'Message',
|
||||||
|
address: 'Address',
|
||||||
|
banner: 'codimd-eauth'
|
||||||
|
},
|
||||||
plantuml: {
|
plantuml: {
|
||||||
server: 'https://www.plantuml.com/plantuml'
|
server: 'https://www.plantuml.com/plantuml'
|
||||||
},
|
},
|
||||||
|
|
|
@ -137,6 +137,13 @@ module.exports = {
|
||||||
email: process.env.CMD_SAML_ATTRIBUTE_EMAIL
|
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: {
|
plantuml: {
|
||||||
server: process.env.CMD_PLANTUML_SERVER
|
server: process.env.CMD_PLANTUML_SERVER
|
||||||
},
|
},
|
||||||
|
|
|
@ -124,6 +124,7 @@ config.isMattermostEnable = config.mattermost.clientID && config.mattermost.clie
|
||||||
config.isLDAPEnable = config.ldap.url
|
config.isLDAPEnable = config.ldap.url
|
||||||
config.isSAMLEnable = config.saml.idpSsoUrl
|
config.isSAMLEnable = config.saml.idpSsoUrl
|
||||||
config.isOAuth2Enable = config.oauth2.clientID && config.oauth2.clientSecret
|
config.isOAuth2Enable = config.oauth2.clientID && config.oauth2.clientSecret
|
||||||
|
config.isEAuthEnable = config.eauth.enable
|
||||||
config.isPDFExportEnable = config.allowPDFExport
|
config.isPDFExportEnable = config.allowPDFExport
|
||||||
|
|
||||||
// Check gitlab api version
|
// Check gitlab api version
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
/* eslint-env browser, jquery */
|
/* eslint-env browser, jquery */
|
||||||
/* global moment, serverurl */
|
/* global moment, serverurl */
|
||||||
|
|
||||||
|
import './lib/eauth-helper'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
checkIfAuth,
|
checkIfAuth,
|
||||||
clearLoginState,
|
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') %>
|
<i class="fa fa-mail-forward"></i> <%= __('Sign in via %s', authProviders.oauth2ProviderName || 'OAuth2') %>
|
||||||
</a>
|
</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>
|
<hr>
|
||||||
<% }%>
|
<% }%>
|
||||||
<% if (authProviders.ldap) { %>
|
<% if (authProviders.ldap) { %>
|
||||||
|
@ -83,7 +88,7 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</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>
|
<hr>
|
||||||
<% }%>
|
<% }%>
|
||||||
<% if (authProviders.openID) { %>
|
<% if (authProviders.openID) { %>
|
||||||
|
@ -102,7 +107,7 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</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>
|
<hr>
|
||||||
<% }%>
|
<% }%>
|
||||||
<% if (authProviders.email) { %>
|
<% if (authProviders.email) { %>
|
||||||
|
|
Loading…
Reference in New Issue