1,441 changes: 719 additions & 722 deletions on_demand/package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions on_demand/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
"bluebird": "^3.5.1",
"body-parser": "^1.17.1",
"express": "^4.15.2",
"express-jwt": "^5.3.1",
"jsonwebtoken": "^8.2.1",
"jwks-rsa": "^1.3.0",
"mongodb": "^3.0.4",
"mtga": "^1.0.5",
"request": "^2.85.0",
"shawkinsl-express-jwt": "^5.3.2",
"underscore": "^1.9.1",
"webtask-tools": "^3.2.0"
"webtask-tools": "^3.4.0"
},
"devDependencies": {
"asar": "^0.14.3",
Expand Down
103 changes: 90 additions & 13 deletions util.js
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,57 @@ let buildUrl = (url, params) => {
return url + "?" + Object.keys(params).map(key => key + "=" + params[key]).join("&")
}

let getRefreshTokenFromDB = (options) => {
return new Promise((resolve, reject) => {
let { db, userKey } = options;
let collection = db.collection(userCollection)
collection.findOne({userKey: userKey}, null).then(findResult => {
if (!findResult) return reject(new Error("no_user_found"))
let { refreshToken } = findResult
options.refresh_token = refreshToken
return resolve(options)
})
})
}

let doTokenRefresh = (options) => {
let rootUrl;
if (options.issuer == "twitch") {
options.client_secret = options.twitch_client_secret
options.client_id = options.twitch_client_id
rootUrl = "https://id.twitch.tv/oauth2/token"
} else if (options.issuer == "discord") {
options.client_secret = options.discord_client_secret
options.client_id = options.discord_client_id
rootUrl = "https://discordapp.com/api/oauth2/token"
options.scope = "identify"
}
let { client_id, client_secret, refresh_token, access_token } = options
return new Promise((resolve, reject) => {
let params = {
client_id: client_id,
client_secret: client_secret,
refresh_token: encodeURI(refresh_token),
grant_type: "refresh_token",
scope: "openid",
}
if (options.scope) params.scope = options.scope // for discord only
let tokenURL = buildUrl(rootUrl, params)
request.post({
url: tokenURL,
json: true,
headers: {'User-Agent': 'MTGATracker-Webtask'}
}, (err, reqRes, data) => {
if (err) return reject(err)
if (reqRes.statusCode != 200) return reject(new Error(`token refresh returned ${reqRes.status}`))
options.access_token = data.access_token
options.refresh_token = data.refresh_token
return resolve(options)
})
})

}

let getTwitchIDToken = (options) => {
let { client_id, client_secret, accessCode } = options
// TODO: generalize this somehow (maybe getToken(req, issuer, accessCode) ? )
Expand All @@ -378,6 +429,7 @@ let getTwitchIDToken = (options) => {
headers: {'User-Agent': 'MTGATracker-Webtask'}
}, (err, reqRes, data) => {
if (err) return reject(err)
if (reqRes.statusCode != 200) return reject(new Error(`token ID returned ${reqRes.status}`))
options.id_token = data.id_token
options.access_token = data.access_token
options.refresh_token = data.refresh_token
Expand All @@ -390,9 +442,7 @@ let getTwitchIDToken = (options) => {
let verifyAndDecodeToken = (tokenObj) => {
let { id_token, issuer } = tokenObj;
return new Promise((resolve, reject) => {
// TODO: make this a bit more resilient to things like verifyAndDecodeKey(key, 'twithc') typos and such
let keySet;
// TODO: this is only for twitch, remove this conditional
if (issuer == "twitch") {
keySet = twitchJWKClient;
} else {
Expand All @@ -402,7 +452,8 @@ let verifyAndDecodeToken = (tokenObj) => {
if (err) {
return reject(err)
} else {
tokenObj.decoded = decoded;
tokenObj.username = decoded.preferred_username
tokenObj.userId = decoded.sub
return resolve(tokenObj)
}
})
Expand All @@ -428,6 +479,7 @@ let getDiscordAccessToken = (options) => {
headers: {'User-Agent': 'MTGATracker-Webtask'},
}, (err, reqRes, data) => {
if (err) return reject(err)
if (reqRes.statusCode != 200) return reject(new Error(`discord access token returned ${reqRes.status}`))
options.id_token = data.id_token
options.access_token = data.access_token
options.refresh_token = data.refresh_token
Expand All @@ -437,34 +489,54 @@ let getDiscordAccessToken = (options) => {
})
}

let verifyDiscordAccessToken = (tokenObj) => {
let verifyAccessToken = (tokenObj) => {
return new Promise((resolve, reject) => {
let { access_token, issuer } = tokenObj;
let discordUserUrl = "https://discordapp.com/api/users/@me"
let verifyUrl;
let authString;
if (tokenObj.issuer == "discord") {
verifyUrl = "https://discordapp.com/api/users/@me"
authString = `Bearer ${access_token}`
} else if (tokenObj.issuer == "twitch") {
verifyUrl = "https://id.twitch.tv/oauth2/validate"
authString = `OAuth ${access_token}`
}
request.get({
url: discordUserUrl,
url: verifyUrl,
headers: {
'Authorization': `Bearer ${access_token}`
'Authorization': authString
},
json: true
}, (err, reqRes, data) => {
if (err) {
if (err ) {
return reject(err)
} else if (reqRes.statusCode != 200) {
return reject(new Error(`token refresh returned ${reqRes.status}`))
} else {
tokenObj.discordUser = data
// set username and subject ID so we can generate a token
if (tokenObj.issuer == "twitch") {
tokenObj.username = data.login
tokenObj.userId = data.user_id
} else if (tokenObj.issuer == "discord") {
tokenObj.username = data.username
tokenObj.userId = data.id
}
return resolve(tokenObj)
}
})
})
}

let generateInternalToken = options => {
let generateInternalToken = (options, req) => {
return new Promise((resolve, reject) => {
options.decoded = {preferred_username: options.discordUser.username, sub: options.discordUser.id}
options.decoded = {preferred_username: options.username, sub: options.userId}
if (options.issuer) {
options.decoded.proxyFor = options.issuer;
} else if (options.proxyFor) {
options.decoded.proxyFor = options.proxyFor
}
options.id_token = createToken(options.decoded, options.jwtSecret, "7d")
options.id_token = createToken(options.decoded, options.jwtSecret, "2 hours")
if (req) req.user = options.decoded;
resolve(options)
})
}
Expand All @@ -480,6 +552,9 @@ let getOrCreateUser = options => {
return collection.findOne({userKey: userKey}, null).then(findResult => {
if (findResult) {
options.user = findResult;
findResult.refreshToken = refresh_token
findResult.accessToken = access_token
collection.save(findResult)
resolve(options)
} else {
// make a new result we can save
Expand Down Expand Up @@ -533,9 +608,11 @@ module.exports = {
createDeckFilter: createDeckFilter,
notificationCollection: notificationCollection,
verifyAndDecodeToken: verifyAndDecodeToken,
verifyAccessToken: verifyAccessToken,
getRefreshTokenFromDB: getRefreshTokenFromDB,
doTokenRefresh: doTokenRefresh,
getTwitchIDToken: getTwitchIDToken,
getDiscordAccessToken: getDiscordAccessToken,
verifyDiscordAccessToken: verifyDiscordAccessToken,
generateInternalToken: generateInternalToken,
getOrCreateUser: getOrCreateUser,
twitchJWKExpressSecret: twitchJWKExpressSecret,
Expand Down