2019-06-16 09:28:46 +00:00
|
|
|
import express from 'express'
|
|
|
|
import bcrypt from 'bcrypt'
|
|
|
|
import { PromiseOAuth2 } from 'oauth-libre'
|
|
|
|
import crypto from 'crypto'
|
|
|
|
|
|
|
|
const router = express.Router()
|
|
|
|
|
|
|
|
async function userInfoPublic (db, id) {
|
|
|
|
let u = await db.get('SELECT id, username, image FROM User WHERE id = ?', id)
|
|
|
|
if (!u) return {}
|
|
|
|
return {
|
|
|
|
id: u.id,
|
|
|
|
username: u.username,
|
|
|
|
image: u.image
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function userInfo (db, id) {
|
|
|
|
return db.get('SELECT * FROM User WHERE id = ?', id)
|
|
|
|
}
|
|
|
|
|
|
|
|
export function userMiddleware (req, res, next) {
|
|
|
|
if (!req.session || !req.session.user) return res.status(401).jsonp({ error: 'Unauthorized' })
|
|
|
|
next()
|
|
|
|
}
|
|
|
|
|
2019-06-16 09:55:36 +00:00
|
|
|
export function user (dbPromise, oauth, registrations) {
|
2019-06-16 09:28:46 +00:00
|
|
|
router.get('/info', userMiddleware, async (req, res) => {
|
|
|
|
res.jsonp(await userInfoPublic(await dbPromise, req.session.user))
|
|
|
|
})
|
|
|
|
|
|
|
|
router.get('/info/:id', userMiddleware, async (req, res) => {
|
|
|
|
if (isNaN(parseInt(req.params.id))) throw new Error('Invalid user ID!')
|
|
|
|
res.jsonp(await userInfoPublic(await dbPromise, parseInt(req.params.id)))
|
|
|
|
})
|
|
|
|
|
|
|
|
if (!oauth) return router
|
|
|
|
|
|
|
|
let oauth2 = new PromiseOAuth2(oauth.clientId, oauth.clientSecret, oauth.baseUrl, oauth.authorizePath, oauth.tokenPath)
|
|
|
|
|
|
|
|
router.get('/login/oauth/_redirect', async (req, res) => {
|
|
|
|
let code = req.query.code
|
|
|
|
let state = req.query.state
|
|
|
|
if (!code || !state) throw new Error('Something went wrong!')
|
2019-06-16 09:55:36 +00:00
|
|
|
if (!req.session || !req.session.oauthState || req.session.oauthState !== state) throw new Error('Possible request forgery detected! Try again.')
|
2019-06-16 09:28:46 +00:00
|
|
|
|
|
|
|
delete req.session.oauthState
|
|
|
|
|
|
|
|
let tokens
|
|
|
|
try {
|
|
|
|
tokens = await oauth2.getOAuthAccessToken(code, { grant_type: 'authorization_code', redirect_uri: oauth.redirectUri })
|
|
|
|
} catch (e) {
|
|
|
|
throw new Error('No authorization!')
|
|
|
|
}
|
|
|
|
|
|
|
|
let accessToken = tokens[2].access_token
|
|
|
|
|
|
|
|
// Get user information on remote
|
|
|
|
let userInfo
|
|
|
|
try {
|
|
|
|
userInfo = await oauth2.get(oauth.baseUrl + oauth.userPath, accessToken)
|
|
|
|
userInfo = JSON.parse(userInfo)
|
|
|
|
} catch (e) {
|
|
|
|
userInfo = null
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!userInfo) throw new Error('Couldn\'t get user information!')
|
|
|
|
|
|
|
|
// Let's see if there's a link for this user already..
|
|
|
|
let db = await dbPromise
|
|
|
|
let userLocal = await db.get('SELECT * FROM OAuth WHERE remoteId = ?', userInfo.id)
|
|
|
|
|
|
|
|
// User and link both exist
|
|
|
|
if (userLocal) {
|
|
|
|
if (req.session.user) throw new Error('You are already logged in!')
|
|
|
|
|
|
|
|
req.session.user = userLocal.userId
|
|
|
|
return res.redirect('/')
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we're logged in, create a link
|
|
|
|
if (req.session.user) {
|
|
|
|
await db.run('INSERT INTO OAuth (userId,remoteId,created) VALUES (?,?,?)', req.session.user, userInfo.id, new Date())
|
|
|
|
return res.redirect('/')
|
|
|
|
}
|
|
|
|
|
2019-06-16 09:55:36 +00:00
|
|
|
if (!registrations) throw new Error('Registrations are currently closed!')
|
|
|
|
|
2019-06-16 09:28:46 +00:00
|
|
|
// Create a new user and log in
|
|
|
|
let newU = await db.get('INSERT INTO User (username,email,image,created) VALUES (?,?,?,?)', userInfo.username, userInfo.email, userInfo.image, new Date())
|
|
|
|
await db.run('INSERT INTO OAuth (userId,remoteId,created) VALUES (?,?,?)', newU.id, userInfo.id, new Date())
|
|
|
|
req.session.user = newU.id
|
|
|
|
res.redirect('/')
|
|
|
|
})
|
|
|
|
|
|
|
|
router.get('/login/oauth', async (req, res) => {
|
2019-06-16 10:12:40 +00:00
|
|
|
let state = crypto.randomBytes(16).toString('hex')
|
|
|
|
req.session.oauthState = state
|
2019-06-16 09:55:36 +00:00
|
|
|
|
2019-06-16 09:28:46 +00:00
|
|
|
return res.redirect(oauth2.getAuthorizeUrl({
|
|
|
|
'redirect_uri': oauth.redirectUri,
|
|
|
|
'scope': oauth.scope,
|
|
|
|
'response_type': 'code',
|
|
|
|
'state': state
|
|
|
|
}))
|
|
|
|
})
|
|
|
|
|
|
|
|
router.use('/login', async (req, res, next) => {
|
|
|
|
if (req.session && req.session.user) return res.redirect('/')
|
|
|
|
let header = req.get('authorization') || ''
|
|
|
|
let token = header.split(/\s+/).pop() || ''
|
|
|
|
let auth = Buffer.from(token, 'base64').toString()
|
|
|
|
let parts = auth.split(/:/)
|
|
|
|
let username = parts[0]
|
|
|
|
let password = parts[1]
|
|
|
|
|
|
|
|
let message = oauth != null ? 'Enter \'oauth\' to log in remotely.' : 'Log in'
|
|
|
|
req.message = message
|
|
|
|
|
|
|
|
if ((!username || !password) && (username !== 'oauth' && oauth)) {
|
|
|
|
return next()
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((username === 'oauth' || username === 'oa') && oauth) {
|
|
|
|
return res.redirect('/user/login/oauth')
|
|
|
|
}
|
|
|
|
|
|
|
|
let db = await dbPromise
|
|
|
|
let user = await db.get('SELECT * FROM User WHERE username = ?', username)
|
|
|
|
if (!user) return next()
|
|
|
|
|
|
|
|
if (!user.password && oauth) {
|
|
|
|
return res.redirect('/user/login/oauth')
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compare passwords
|
|
|
|
let ures = await bcrypt.compare(password, user.password)
|
|
|
|
if (!ures) return next()
|
|
|
|
|
|
|
|
// Set login success
|
|
|
|
req.message = ''
|
|
|
|
req.session.user = user.id
|
|
|
|
res.redirect('/')
|
|
|
|
}, function (req, res, next) {
|
|
|
|
if (!req.message) return next()
|
|
|
|
res.status(401).set('WWW-Authenticate', 'Basic realm="' + req.message + '", charset="UTF-8"').end()
|
|
|
|
})
|
|
|
|
|
|
|
|
router.use('/logout', function (req, res) {
|
|
|
|
delete req.session.user
|
|
|
|
res.redirect('/')
|
|
|
|
})
|
|
|
|
|
|
|
|
return router
|
|
|
|
}
|