Add user agent info to login code mail headers

This commit is contained in:
Jonas Lochmann 2024-03-18 01:00:00 +01:00
parent e55d1fd1a9
commit 120ea33547
No known key found for this signature in database
GPG key ID: 8B8C9AEE10FA5B36
3 changed files with 28 additions and 11 deletions

View file

@ -1,6 +1,6 @@
/* /*
* server component for the TimeLimit App * server component for the TimeLimit App
* Copyright (C) 2019 - 2021 Jonas Lochmann * Copyright (C) 2019 - 2024 Jonas Lochmann
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as * it under the terms of the GNU Affero General Public License as
@ -30,6 +30,10 @@ export const createAuthRouter = (database: Database) => {
const router = Router() const router = Router()
router.post('/send-mail-login-code-v2', json(), async (req, res, next) => { router.post('/send-mail-login-code-v2', json(), async (req, res, next) => {
const info = {
ua: req.headers['user-agent']
}
try { try {
if (!isSendMailLoginCodeRequest(req.body)) { if (!isSendMailLoginCodeRequest(req.body)) {
throw new BadRequest() throw new BadRequest()
@ -50,7 +54,8 @@ export const createAuthRouter = (database: Database) => {
mail, mail,
deviceAuthToken: req.body.deviceAuthToken, deviceAuthToken: req.body.deviceAuthToken,
locale: req.body.locale, locale: req.body.locale,
database database,
info: Buffer.from(JSON.stringify(info), 'utf8')
}) })
res.json({ mailLoginToken }) res.json({ mailLoginToken })

View file

@ -1,6 +1,6 @@
/* /*
* server component for the TimeLimit App * server component for the TimeLimit App
* Copyright (C) 2019 - 2023 Jonas Lochmann * Copyright (C) 2019 - 2024 Jonas Lochmann
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as * it under the terms of the GNU Affero General Public License as
@ -23,11 +23,12 @@ import { checkMailSendLimit } from '../../util/ratelimit-authmail'
import { generateAuthToken } from '../../util/token' import { generateAuthToken } from '../../util/token'
import { createAuthTokenByMailAddress } from './index' import { createAuthTokenByMailAddress } from './index'
export const sendLoginCode = async ({ mail, deviceAuthToken, locale, database }: { export const sendLoginCode = async ({ mail, deviceAuthToken, locale, database, info }: {
mail: string mail: string
deviceAuthToken?: string deviceAuthToken?: string
locale: string locale: string
database: Database database: Database
info: Buffer
// no transaction here because this is directly called from an API endpoint // no transaction here because this is directly called from an API endpoint
}): Promise<{ mailLoginToken: string }> => { }): Promise<{ mailLoginToken: string }> => {
let deviceName = null let deviceName = null
@ -82,7 +83,8 @@ export const sendLoginCode = async ({ mail, deviceAuthToken, locale, database }:
receiver: mail, receiver: mail,
code, code,
locale, locale,
deviceName deviceName,
info
}) })
await database.transaction(async (transaction) => { await database.transaction(async (transaction) => {

View file

@ -1,6 +1,6 @@
/* /*
* server component for the TimeLimit App * server component for the TimeLimit App
* Copyright (C) 2019 - 2023 Jonas Lochmann * Copyright (C) 2019 - 2024 Jonas Lochmann
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as * it under the terms of the GNU Affero General Public License as
@ -45,9 +45,10 @@ function createMailTemplateSender (templateName: string) {
const textTemplate = compileTemplate('text.ejs') const textTemplate = compileTemplate('text.ejs')
const htmlTemplate = compileTemplate('html.ejs') const htmlTemplate = compileTemplate('html.ejs')
const sendMail = async ({ receiver, params }: { const sendMail = async ({ receiver, params, info }: {
receiver: string receiver: string
params: object params: object
info?: Buffer
}) => { }) => {
if (!mailTransport) { if (!mailTransport) {
throw new Error('can not send mails without mail config and without NODE_ENV=development') throw new Error('can not send mails without mail config and without NODE_ENV=development')
@ -56,6 +57,9 @@ function createMailTemplateSender (templateName: string) {
const subject = subjectTemplate(params).replace(/\n/g, ' ') const subject = subjectTemplate(params).replace(/\n/g, ' ')
const text = textTemplate(params) const text = textTemplate(params)
const html = htmlTemplate(params) const html = htmlTemplate(params)
const headers: {[key: string]: string} = info ? {
'X-TlInfo': info.toString('base64')
} : {}
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
mailTransport.sendMail({ mailTransport.sendMail({
@ -63,7 +67,8 @@ function createMailTemplateSender (templateName: string) {
to: receiver, to: receiver,
subject, subject,
text, text,
html html,
headers
}, (err, info) => { }, (err, info) => {
if (err) { if (err) {
reject(err) reject(err)
@ -90,9 +95,13 @@ function createMailTemplateSender (templateName: string) {
const loginMailSender = createMailTemplateSender('login') const loginMailSender = createMailTemplateSender('login')
export const sendAuthenticationMail = async ({ export const sendAuthenticationMail = async ({
receiver, code, locale, deviceName receiver, code, locale, deviceName, info
}: { }: {
receiver: string, code: string, locale: string, deviceName: string | null receiver: string
code: string
locale: string
deviceName: string | null
info: Buffer
}) => { }) => {
await loginMailSender.sendMail({ await loginMailSender.sendMail({
receiver, receiver,
@ -105,7 +114,8 @@ export const sendAuthenticationMail = async ({
deviceName, deviceName,
deviceNameIntro: locale === 'de' ? 'Die Anmeldung wurde am Gerät' : 'The login was attempted at the device', deviceNameIntro: locale === 'de' ? 'Die Anmeldung wurde am Gerät' : 'The login was attempted at the device',
deviceNameOutro: locale === 'de' ? 'versucht.' : '.' deviceNameOutro: locale === 'de' ? 'versucht.' : '.'
} },
info
}) })
} }