0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-20 22:42:53 -05:00

Added support for fetching device details when creating session

This commit is contained in:
Princi Vershwal 2024-10-10 16:21:12 +01:00 committed by Kevin Ansfield
parent 29d1026606
commit 5ee2f91557
3 changed files with 84 additions and 4 deletions

View file

@ -2,6 +2,10 @@ const {
BadRequestError BadRequestError
} = require('@tryghost/errors'); } = require('@tryghost/errors');
const emailTemplate = require('../lib/emails/signin'); const emailTemplate = require('../lib/emails/signin');
const UAParser = require('ua-parser-js');
const got = require('got');
const IPV4_REGEX = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
const IPV6_REGEX = /^(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}$/i;
const {totp} = require('otplib'); const {totp} = require('otplib');
totp.options = { totp.options = {
@ -143,6 +147,75 @@ module.exports = function createSessionService({
return isValid; return isValid;
} }
const formatTime = new Intl.DateTimeFormat('en-GB', {
day: '2-digit',
month: 'short',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
timeZone: 'UTC',
timeZoneName: 'short'
}).format;
/**
* Get a readable location string from an IP address.
* @param {string} ip - The IP address to look up.
* @returns {Promise<string>} - A readable location string or 'Unknown Location'.
*/
async function getGeolocationFromIP(ip) {
ip = '212.19.89.120';
if (!ip || (!IPV4_REGEX.test(ip) && !IPV6_REGEX.test(ip))) {
return;
}
const gotOpts = {
timeout: 500
};
if (process.env.NODE_ENV?.startsWith('test')) {
gotOpts.retry = 0;
}
const geojsUrl = `https://get.geojs.io/v1/ip/geo/${encodeURIComponent(ip)}.json`;
const response = await got(geojsUrl, gotOpts).json();
const {
city = '',
region = '',
country = ''
} = response || {};
const locationParts = [];
if (city) {
locationParts.push(city);
}
if (region) {
locationParts.push(region);
}
if (country) {
locationParts.push(country);
}
return locationParts.join(', ').trim() || 'Unknown Location';
}
async function getDeviceDetails(userAgent, ip) {
const parser = new UAParser();
parser.setUA(userAgent);
const result = parser.getResult();
const deviceParts = [
result.browser?.name || '',
result.os?.name || ''
].filter(Boolean);
return {
device: deviceParts.join(', '),
location: await getGeolocationFromIP(ip),
time: formatTime(new Date())
};
}
/** /**
* sendAuthCodeToUser * sendAuthCodeToUser
* *
@ -151,9 +224,8 @@ module.exports = function createSessionService({
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async function sendAuthCodeToUser(req, res) { async function sendAuthCodeToUser(req, res) {
const token = await generateAuthCodeForUser(req, res);
const session = await getSession(req, res); const session = await getSession(req, res);
const token = await generateAuthCodeForUser(req, res);
const user = await findUserById({id: session.user_id}); const user = await findUserById({id: session.user_id});
if (!user) { if (!user) {
@ -172,7 +244,8 @@ module.exports = function createSessionService({
email: recipient, email: recipient,
siteDomain: siteDomain, siteDomain: siteDomain,
siteUrl: siteUrl, siteUrl: siteUrl,
token token: token,
deviceDetails: await getDeviceDetails(session.user_agent, session.ip)
}); });
await mailer.send({ await mailer.send({

View file

@ -26,6 +26,8 @@
}, },
"dependencies": { "dependencies": {
"@tryghost/errors": "1.3.5", "@tryghost/errors": "1.3.5",
"otplib" : "12.0.1" "otplib" : "12.0.1",
"ua-parser-js": "1.0.39",
"got": "11.8.6"
} }
} }

View file

@ -30491,6 +30491,11 @@ typescript@5.6.2, typescript@^5.0.4:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.2.tgz#d1de67b6bef77c41823f822df8f0b3bcff60a5a0" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.2.tgz#d1de67b6bef77c41823f822df8f0b3bcff60a5a0"
integrity sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw== integrity sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==
ua-parser-js@1.0.39:
version "1.0.39"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.39.tgz#bfc07f361549bf249bd8f4589a4cccec18fd2018"
integrity sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw==
uc.micro@^1.0.1, uc.micro@^1.0.5: uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"