add google authentication method
This commit is contained in:
parent
4a6004aa7c
commit
1e027c4b88
@ -57,6 +57,10 @@
|
||||
# api=""
|
||||
# api_secret=""
|
||||
|
||||
[google]
|
||||
# api=""
|
||||
# api_secret=""
|
||||
|
||||
# reCAPTCHA configuration
|
||||
[security]
|
||||
[security.recaptcha]
|
||||
|
@ -18,4 +18,6 @@
|
||||
<p>By logging in with Facebook, we will only ask you for your Public Profile and Email Address. We will use your Name as your Display Name, which can be changed from your Account Settings after logging in. Your profile picture may be downloaded onto our servers and used as your network-wide profile image. You may change your profile picture from your Account Settings at any time. Your Email Address will only be used to send you updates, which you can opt-out of. We can not: post on your behalf, see your friends list nor see your posts.</p>
|
||||
<h3>Discord</h3>
|
||||
<p>By logging in with Discord, we will only ask you for your Username and Email Address for the above-mentioned purposes. We do not ask you for any other information and we will not know which Discord Servers you're on.</p>
|
||||
<h3>Google</h3>
|
||||
<p>By logging in with Google, we will only ask you for your Name and Email Address for the above-mentioned purposes. We do not ask you for any other information and we will not have access to anything other than your public profile information.</p>
|
||||
</div>
|
||||
|
@ -292,6 +292,75 @@ const API = {
|
||||
return {error: null, user: newUser}
|
||||
}
|
||||
},
|
||||
Google: {
|
||||
callback: async (user, data, ipAddress) => {
|
||||
let uid
|
||||
|
||||
try {
|
||||
let test = await http.GET('https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=' + data.id_token)
|
||||
if (!test) throw new Error('No response!')
|
||||
|
||||
let jsondata = JSON.parse(test)
|
||||
if (!jsondata || !jsondata.email || !jsondata.name) throw new Error('Please allow Basic Profile and Email.')
|
||||
|
||||
if (jsondata.email !== data.email || jsondata.name !== data.name) throw new Error('Conflicting data. Please try again!')
|
||||
|
||||
if (new Date(parseInt(jsondata.exp) * 1000) < Date.now()) throw new Error('Expired token! Please try again!')
|
||||
|
||||
uid = jsondata.sub
|
||||
} catch (e) {
|
||||
return {error: e.message}
|
||||
}
|
||||
|
||||
let exists = await API.Common.getExternal('google', uid)
|
||||
|
||||
if (user) {
|
||||
// Get bans for user
|
||||
let bans = await API.Common.getBan(user)
|
||||
if (bans.length) return { banned: bans, ip: false }
|
||||
|
||||
if (exists) return {error: null, user: user}
|
||||
|
||||
await API.Common.new('google', uid, user)
|
||||
return {error: null, user: user}
|
||||
}
|
||||
|
||||
// Callback succeeded with user id and the external table exists, we log in the user
|
||||
if (exists) {
|
||||
// Get bans for user
|
||||
let bans = await API.Common.getBan(exists.user)
|
||||
if (bans.length) return { banned: bans, ip: false }
|
||||
return {error: null, user: exists.user}
|
||||
}
|
||||
|
||||
// Get bans for IP
|
||||
let bans = await API.Common.getBan(null, ipAddress)
|
||||
if (bans.length) return { banned: bans, ip: true }
|
||||
|
||||
// Determine profile picture
|
||||
let profilepic = null
|
||||
if (data.image) {
|
||||
let imgdata = await API.Common.saveAvatar(data.image)
|
||||
if (imgdata && imgdata.fileName) {
|
||||
profilepic = imgdata.fileName
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new user
|
||||
let newUData = {
|
||||
username: data.name.replace(/\W+/gi, ''),
|
||||
display_name: data.name,
|
||||
email: data.email || '',
|
||||
avatar_file: profilepic,
|
||||
ip_address: ipAddress
|
||||
}
|
||||
|
||||
let newUser = await API.Common.newUser('google', uid, newUData)
|
||||
if (!newUser) return {error: 'Failed to create user.'}
|
||||
|
||||
return {error: null, user: newUser}
|
||||
}
|
||||
},
|
||||
Discord: {
|
||||
oauth2App: function () {
|
||||
if (discordApp) return
|
||||
|
@ -243,6 +243,64 @@ router.get('/external/discord/remove', wrap(async (req, res) => {
|
||||
res.redirect('/user/manage')
|
||||
}))
|
||||
|
||||
router.get('/external/discord/remove', wrap(async (req, res) => {
|
||||
if (!req.session.user) return res.redirect('/login')
|
||||
|
||||
let done = await APIExtern.Common.remove(req.session.user, 'discord')
|
||||
|
||||
if (!done) {
|
||||
req.flash('message', {error: true, text: 'Unable to unlink social media account'})
|
||||
}
|
||||
|
||||
res.redirect('/user/manage')
|
||||
}))
|
||||
|
||||
/** GOOGLE LOGIN
|
||||
* Google Token Verification
|
||||
* Tokens in configs
|
||||
*/
|
||||
router.get('/external/google/login', wrap(async (req, res) => {
|
||||
if (!config.google || !config.google.api) return res.redirect('/')
|
||||
|
||||
res.redirect('/login')
|
||||
}))
|
||||
|
||||
router.post('/external/google/callback', wrap(async (req, res) => {
|
||||
if (!config.google || !config.google.api) return res.redirect('/login')
|
||||
|
||||
if (!req.body.id_token) {
|
||||
return JsonData(req, res, 'Invalid or missing ID token!', '/login')
|
||||
}
|
||||
|
||||
let response = await APIExtern.Google.callback(req.session.user, req.body, req.realIP)
|
||||
if (response.banned) {
|
||||
return JsonData(req, res, 'Banned user.', '/login')
|
||||
}
|
||||
|
||||
if (response.error) {
|
||||
return JsonData(req, res, response.error, '/login')
|
||||
}
|
||||
|
||||
if (!req.session.user) {
|
||||
let user = response.user
|
||||
createSession(req, user)
|
||||
}
|
||||
|
||||
JsonData(req, res, null, '/login')
|
||||
}))
|
||||
|
||||
router.get('/external/google/remove', wrap(async (req, res) => {
|
||||
if (!req.session.user) return res.redirect('/login')
|
||||
|
||||
let done = await APIExtern.Common.remove(req.session.user, 'google')
|
||||
|
||||
if (!done) {
|
||||
req.flash('message', {error: true, text: 'Unable to unlink social media account'})
|
||||
}
|
||||
|
||||
res.redirect('/user/manage')
|
||||
}))
|
||||
|
||||
/* ========
|
||||
* NEWS
|
||||
* ========
|
||||
|
@ -42,7 +42,6 @@ function setSession (req, user) {
|
||||
function redirectLogin (req, res) {
|
||||
let uri = '/'
|
||||
|
||||
console.log('goto', req.session.redirectUri)
|
||||
if (req.session.redirectUri) {
|
||||
uri = req.session.redirectUri
|
||||
delete req.session.redirectUri
|
||||
@ -116,6 +115,10 @@ function extraButtons (req, res, next) {
|
||||
res.locals.facebook_auth = config.facebook.client
|
||||
}
|
||||
|
||||
if (config.google && config.google.api) {
|
||||
res.locals.google_auth = config.google.api
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
|
||||
@ -184,10 +187,11 @@ router.get('/user/manage', ensureLogin, wrap(async (req, res) => {
|
||||
totpEnabled = await API.User.Login.totpTokenRequired(req.session.user)
|
||||
}
|
||||
|
||||
// Decide whether we need a disconnect or a log in with button for social account logins
|
||||
if (config.twitter && config.twitter.api) {
|
||||
if (!socialStatus.enabled.twitter) {
|
||||
res.locals.twitter_auth = true
|
||||
} else if (!socialStatus.source && socialStatus.source !== 'twitter') {
|
||||
} else if (socialStatus.source !== 'twitter') {
|
||||
res.locals.twitter_auth = false
|
||||
}
|
||||
}
|
||||
@ -195,7 +199,7 @@ router.get('/user/manage', ensureLogin, wrap(async (req, res) => {
|
||||
if (config.discord && config.discord.api) {
|
||||
if (!socialStatus.enabled.discord) {
|
||||
res.locals.discord_auth = true
|
||||
} else if (!socialStatus.source && socialStatus.source !== 'discord') {
|
||||
} else if (socialStatus.source !== 'discord') {
|
||||
res.locals.discord_auth = false
|
||||
}
|
||||
}
|
||||
@ -203,11 +207,19 @@ router.get('/user/manage', ensureLogin, wrap(async (req, res) => {
|
||||
if (config.facebook && config.facebook.client) {
|
||||
if (!socialStatus.enabled.fb) {
|
||||
res.locals.facebook_auth = config.facebook.client
|
||||
} else if (!socialStatus.source && socialStatus.source !== 'fb') {
|
||||
} else if (socialStatus.source !== 'fb') {
|
||||
res.locals.facebook_auth = false
|
||||
}
|
||||
}
|
||||
|
||||
if (config.google && config.google.api) {
|
||||
if (!socialStatus.enabled.google) {
|
||||
res.locals.google_auth = config.google.api
|
||||
} else if (socialStatus.source !== 'google') {
|
||||
res.locals.google_auth = false
|
||||
}
|
||||
}
|
||||
|
||||
res.render('user/settings', {totp: totpEnabled, password: socialStatus.password})
|
||||
}))
|
||||
|
||||
|
@ -267,20 +267,37 @@ $(document).ready(function () {
|
||||
data: response,
|
||||
success: function (data) {
|
||||
if (data.error) {
|
||||
$('.message').addClass('error')
|
||||
$('.message span').text(data.error)
|
||||
alert(data.error)
|
||||
return
|
||||
}
|
||||
|
||||
window.location.reload()
|
||||
}
|
||||
}).fail(function () {
|
||||
$('.message').addClass('error')
|
||||
$('.message span').text('An error occured.')
|
||||
alert('An error occured.')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
window.googlePOST = function (response) {
|
||||
$.ajax({
|
||||
type: 'post',
|
||||
url: '/api/external/google/callback',
|
||||
dataType: 'json',
|
||||
data: response,
|
||||
success: function (data) {
|
||||
if (data.error) {
|
||||
alert(data.error)
|
||||
return
|
||||
}
|
||||
|
||||
window.location.reload()
|
||||
}
|
||||
}).fail(function () {
|
||||
alert('An error occured.')
|
||||
})
|
||||
}
|
||||
|
||||
$('.loginDiag').click(function (e) {
|
||||
e.preventDefault()
|
||||
var url = $(this).attr('href')
|
||||
|
@ -274,14 +274,14 @@ input:not([type="submit"])
|
||||
border-radius: 5px
|
||||
text-decoration: none
|
||||
i
|
||||
color: #03A9F4;
|
||||
font-size: 22px;
|
||||
color: #03A9F4
|
||||
font-size: 22px
|
||||
span
|
||||
color: #000;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-top: 3px;
|
||||
margin-left: 12px;
|
||||
color: #000
|
||||
display: inline-block
|
||||
vertical-align: top
|
||||
margin-top: 3px
|
||||
margin-left: 12px
|
||||
|
||||
.discordLogin
|
||||
display: block
|
||||
@ -302,6 +302,26 @@ input:not([type="submit"])
|
||||
width: 45px
|
||||
display: inline-block
|
||||
|
||||
.googleLogin
|
||||
padding: 10px
|
||||
width: 215px
|
||||
margin-top: 5px
|
||||
background-color: #4285f4
|
||||
border: 1px solid #ddd
|
||||
border-radius: 5px
|
||||
text-decoration: none
|
||||
display: inline-block
|
||||
cursor: pointer
|
||||
i
|
||||
color: #fff
|
||||
font-size: 22px
|
||||
span
|
||||
color: #fff
|
||||
display: inline-block
|
||||
vertical-align: top
|
||||
margin-top: 3px
|
||||
margin-left: 12px
|
||||
|
||||
.accdisconnect
|
||||
margin-top: 5px
|
||||
padding: 10px
|
||||
|
@ -20,6 +20,45 @@
|
||||
fjs.parentNode.insertBefore(js, fjs);
|
||||
}(document, 'script', 'facebook-jssdk'));
|
||||
fb:login-button(scope="public_profile,email", onlogin="checkLoginState();" data-max-rows="1", data-size="large", data-button-type="login_with", data-show-faces="false", data-auto-logout-link="false", data-use-continue-as="false")
|
||||
if google_auth
|
||||
script(src="https://apis.google.com/js/api:client.js")
|
||||
a.googleLogin
|
||||
i.fa.fa-fw.fa-google
|
||||
span Log in With Google
|
||||
script.
|
||||
var googleUser = {};
|
||||
var startApp = function() {
|
||||
gapi.load('auth2', function(){
|
||||
// Retrieve the singleton for the GoogleAuth library and set up the client.
|
||||
auth2 = gapi.auth2.init({
|
||||
client_id: '#{google_auth}',
|
||||
cookiepolicy: 'single_host_origin',
|
||||
fetch_basic_profile: true
|
||||
});
|
||||
attachSignin(document.querySelector('.googleLogin'));
|
||||
});
|
||||
};
|
||||
|
||||
function attachSignin(element) {
|
||||
auth2.attachClickHandler(element, {},
|
||||
function (googleUser) {
|
||||
let profile = googleUser.getBasicProfile();
|
||||
let dataTree = {
|
||||
id_token: googleUser.getAuthResponse().id_token,
|
||||
name: profile.getName(),
|
||||
email: profile.getEmail(),
|
||||
image: profile.getImageUrl()
|
||||
};
|
||||
|
||||
if (window.googlePOST) {
|
||||
window.googlePOST(dataTree);
|
||||
}
|
||||
}, function(error) {
|
||||
alert('Failed to log you in using Google.');
|
||||
});
|
||||
}
|
||||
|
||||
startApp()
|
||||
if twitter_auth
|
||||
a.twitterLogin.loginDiag(href="/api/external/twitter/login")
|
||||
i.fa.fa-fw.fa-twitter
|
||||
|
@ -33,6 +33,10 @@ block body
|
||||
h3 Social Media Accounts
|
||||
.specify(title="You can add social media accounts to your account for ease of login. Once added, logging in from linked sources logs you into this account automatically.") ?
|
||||
include ../includes/external.pug
|
||||
if google_auth == false
|
||||
a.option.accdisconnect(href="/api/external/google/remove")
|
||||
i.fa.fa-fw.fa-times
|
||||
|Unlink Google
|
||||
if twitter_auth == false
|
||||
a.option.accdisconnect(href="/api/external/twitter/remove")
|
||||
i.fa.fa-fw.fa-times
|
||||
|
Reference in New Issue
Block a user