This repository has been archived on 2022-11-26. You can view files and clone it, but cannot push or open issues or pull requests.
IcyNet.eu/server/api/external.js

364 lines
11 KiB
JavaScript

import config from '../../scripts/load-config'
import http from '../../scripts/http'
import models from './models'
import UAPI from './index'
import qs from 'querystring'
import oauth from 'oauth-libre'
let twitterApp
let discordApp
const API = {
Common: {
getExternal: async (service, identifier) => {
let extr = await models.External.query().where('service', service).andWhere('identifier', identifier)
if (!extr || !extr.length) return null
extr = extr[0]
extr.user = null
if (extr.user_id !== null) {
let user = await UAPI.User.get(extr.user_id)
if (user) {
extr.user = user
}
}
return extr
},
new: async (service, identifier, user) => {
let data = {
user_id: user.id,
service: service,
identifier: identifier,
created_at: new Date()
}
await await models.External.query().insert(data)
return true
},
remove: async (user, service) => {
user = await UAPI.User.ensureObject(user, ['password'])
let userExterns = await models.External.query().orderBy('created_at', 'asc').where('user_id', user.id)
if (!userExterns.length) {
return false
}
// Do not remove the service the user signed up with
if (userExterns[0] && (user.password === '' || user.password === null) && userExterns[0].service === service) {
return false
}
return models.External.query().delete().where('user_id', user.id).andWhere('service', service)
}
},
Facebook: {
callback: async (user, data) => {
if (!data.authResponse || data.status !== 'connected') {
return {error: 'No Authorization'}
}
let uid = data.authResponse.userID
if (!uid) {
return {error: 'No Authorization'}
}
// Get facebook user information in order to create a new user or verify
let fbdata
let intel = {
access_token: data.authResponse.accessToken,
fields: 'name,email,picture,short_name'
}
try {
fbdata = await http.GET('https://graph.facebook.com/v2.10/' + uid + '?' + qs.stringify(intel))
fbdata = JSON.parse(fbdata)
} catch (e) {
return {error: 'Could not get user information', errorObject: e}
}
if (fbdata.error) {
return {error: fbdata.error.message}
}
let exists = await API.Common.getExternal('fb', uid)
if (user) {
if (exists) return {error: null, user: user}
await API.Common.new('fb', uid, user)
return {error: null, user: user}
}
// Callback succeeded with user id and the external table exists, we log in the user
if (exists) {
return {error: null, user: exists.user}
}
// Determine profile picture
let profilepic = ''
if (fbdata.picture) {
if (fbdata.picture.is_silhouette === false && fbdata.picture.url) {
// TODO: Download the profile image and save it locally
profilepic = fbdata.picture.url
}
}
// Create a new user
let udataLimited = {
username: fbdata.short_name || 'FB' + UAPI.Hash(4),
display_name: fbdata.name,
email: fbdata.email || '',
avatar_file: profilepic,
activated: 1,
ip_address: data.ip_address,
created_at: new Date(),
updated_at: new Date()
}
// Check if the username is already taken
if (await UAPI.User.get(udataLimited.username) != null) {
udataLimited.username = udataLimited.username + UAPI.Hash(4)
}
// Check if the email Facebook gave us is already registered, if so,
// associate an external node with the user bearing the email
if (udataLimited.email && udataLimited.email !== '') {
let getByEmail = await UAPI.User.get(udataLimited.email)
if (getByEmail) {
await API.Common.new('fb', getByEmail.id, getByEmail)
return {error: null, user: getByEmail}
}
}
// Create a new user based on the information we got from Facebook
let newUser = await models.User.query().insert(udataLimited)
await API.Common.new('fb', uid, newUser)
return {error: null, user: newUser}
}
},
Twitter: {
oauthApp: function () {
if (!twitterApp) {
let redirectUri = config.server.domain + '/api/external/twitter/callback'
twitterApp = new oauth.PromiseOAuth(
'https://api.twitter.com/oauth/request_token',
'https://api.twitter.com/oauth/access_token',
config.twitter.api,
config.twitter.api_secret,
'1.0A',
redirectUri,
'HMAC-SHA1'
)
}
},
getRequestToken: async function () {
if (!twitterApp) API.Twitter.oauthApp()
let tokens
try {
tokens = await twitterApp.getOAuthRequestToken()
} catch (e) {
console.error(e)
return {error: 'No tokens returned'}
}
if (tokens[2].oauth_callback_confirmed !== 'true') return {error: 'No tokens returned.'}
return {error: null, token: tokens[0], token_secret: tokens[1]}
},
getAccessTokens: async function (token, secret, verifier) {
if (!twitterApp) API.Twitter.oauthApp()
let tokens
try {
tokens = await twitterApp.getOAuthAccessToken(token, secret, verifier)
} catch (e) {
console.error(e)
return {error: 'No tokens returned'}
}
if (!tokens || !tokens.length) return {error: 'No tokens returned'}
return {error: null, access_token: tokens[0], access_token_secret: tokens[1]}
},
callback: async function (user, accessTokens, ipAddress) {
if (!twitterApp) API.Twitter.oauthApp()
let twdata
try {
let resp = await twitterApp.get('https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true',
accessTokens.access_token, accessTokens.access_token_secret)
twdata = JSON.parse(resp[0])
} catch (e) {
console.error(e)
return {error: 'Failed to verify user credentials.'}
}
let uid = twdata.id_str
let exists = await API.Common.getExternal('twitter', uid)
if (user) {
if (exists) return {error: null, user: user}
await API.Common.new('twitter', uid, user)
return {error: null, user: user}
}
// Callback succeeded with user id and the external table exists, we log in the user
if (exists) {
return {error: null, user: exists.user}
}
// Determine profile picture
let profilepic = ''
if (twdata.profile_image_url_https) {
// TODO: Download the profile image and save it locally
profilepic = twdata.profile_image_url_https
}
// Create a new user
let udataLimited = {
username: twdata.screen_name,
display_name: twdata.name,
email: twdata.email || '',
avatar_file: profilepic,
activated: 1,
ip_address: ipAddress,
updated_at: new Date(),
created_at: new Date()
}
// Check if the username is already taken
if (await UAPI.User.get(udataLimited.username) != null) {
udataLimited.username = udataLimited.username + UAPI.Hash(4)
}
// Check if the email Twitter gave us is already registered, if so,
// associate an external node with the user bearing the email
if (udataLimited.email && udataLimited.email !== '') {
let getByEmail = await UAPI.User.get(udataLimited.email)
if (getByEmail) {
await API.Common.new('twitter', getByEmail.id, getByEmail)
return {error: null, user: getByEmail}
}
}
// Create a new user based on the information we got from Twitter
let newUser = await models.User.query().insert(udataLimited)
await API.Common.new('twitter', uid, newUser)
return {error: null, user: newUser}
}
},
Discord: {
oauth2App: function () {
if (discordApp) return
discordApp = new oauth.PromiseOAuth2(
config.discord.api,
config.discord.api_secret,
'https://discordapp.com/api/',
'oauth2/authorize',
'oauth2/token'
)
discordApp.useAuthorizationHeaderforGET(true)
},
getAuthorizeURL: function () {
if (!discordApp) API.Discord.oauth2App()
let state = UAPI.Hash(6)
let redirectUri = config.server.domain + '/api/external/discord/callback'
const params = {
'client_id': config.discord.api,
'redirect_uri': redirectUri,
'scope': 'identify email',
'response_type': 'code',
'state': state
}
let url = discordApp.getAuthorizeUrl(params)
return {error: null, state: state, url: url}
},
getAccessToken: async function (code) {
if (!discordApp) API.Discord.oauth2App()
let redirectUri = config.server.domain + '/api/external/discord/callback'
let tokens
try {
tokens = await discordApp.getOAuthAccessToken(code, {grant_type: 'authorization_code', redirect_uri: redirectUri})
} catch (e) {
console.error(e)
return {error: 'No Authorization'}
}
if (!tokens.length) return {error: 'No Tokens'}
tokens = tokens[2]
return {error: null, accessToken: tokens.access_token}
},
callback: async function (user, accessToken, ipAddress) {
if (!discordApp) API.Discord.oauth2App()
let ddata
try {
let resp = await discordApp.get('https://discordapp.com/api/users/@me', accessToken)
ddata = JSON.parse(resp)
} catch (e) {
console.error(e)
return {error: 'Could not get user information'}
}
let uid = ddata.id
let exists = await API.Common.getExternal('discord', uid)
if (user) {
if (exists) return {error: null, user: user}
await API.Common.new('discord', uid, user)
return {error: null, user: user}
}
// Callback succeeded with user id and the external table exists, we log in the user
if (exists) {
return {error: null, user: exists.user}
}
// Determine profile picture
let profilepic = ''
// TODO: Download the profile image and save it locally
// Create a new user
let udataLimited = {
username: 'D' + ddata.discriminator,
display_name: ddata.username,
email: ddata.email || '',
avatar_file: profilepic,
activated: 1,
ip_address: ipAddress,
updated_at: new Date(),
created_at: new Date()
}
// Check if the email Discord gave us is already registered, if so,
// associate an external node with the user bearing the email
if (udataLimited.email && udataLimited.email !== '') {
let getByEmail = await UAPI.User.get(udataLimited.email)
if (getByEmail) {
await API.Common.new('discord', uid, getByEmail)
return {error: null, user: getByEmail}
}
}
// Create a new user based on the information we got from Discord
let newUser = await models.User.query().insert(udataLimited)
await API.Common.new('discord', uid, newUser)
return {error: null, user: newUser}
}
}
}
module.exports = API