mirror of
https://codeberg.org/timelimit/timelimit-server.git
synced 2026-08-31 19:03:45 +02:00
Show the source device in login code mails
This commit is contained in:
+2
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* server component for the TimeLimit App
|
||||
* Copyright (C) 2019 - 2020 Jonas Lochmann
|
||||
* Copyright (C) 2019 - 2021 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -48,6 +48,7 @@ export const createAuthRouter = (database: Database) => {
|
||||
} else {
|
||||
const { mailLoginToken } = await sendLoginCode({
|
||||
mail,
|
||||
deviceAuthToken: req.body.deviceAuthToken,
|
||||
locale: req.body.locale,
|
||||
database
|
||||
})
|
||||
|
||||
@@ -132,6 +132,7 @@ export interface RequestWithAuthToken {
|
||||
export interface SendMailLoginCodeRequest {
|
||||
mail: string
|
||||
locale: string
|
||||
deviceAuthToken?: string
|
||||
}
|
||||
|
||||
export interface SignInByMailCodeRequest {
|
||||
|
||||
@@ -2966,6 +2966,9 @@ export const isSendMailLoginCodeRequest: (value: object) => value is SendMailLog
|
||||
},
|
||||
"locale": {
|
||||
"type": "string"
|
||||
},
|
||||
"deviceAuthToken": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* server component for the TimeLimit App
|
||||
* Copyright (C) 2019 - 2020 Jonas Lochmann
|
||||
* Copyright (C) 2019 - 2021 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Forbidden, Gone, TooManyRequests } from 'http-errors'
|
||||
import { Forbidden, Gone, TooManyRequests, Unauthorized } from 'http-errors'
|
||||
import { Database } from '../../database'
|
||||
import { sendAuthenticationMail } from '../../util/mail'
|
||||
import { areWordSequencesEqual, randomWords } from '../../util/random-words'
|
||||
@@ -23,12 +23,52 @@ import { checkMailSendLimit } from '../../util/ratelimit-authmail'
|
||||
import { generateAuthToken } from '../../util/token'
|
||||
import { createAuthTokenByMailAddress } from './index'
|
||||
|
||||
export const sendLoginCode = async ({ mail, locale, database }: {
|
||||
export const sendLoginCode = async ({ mail, deviceAuthToken, locale, database }: {
|
||||
mail: string
|
||||
deviceAuthToken?: string
|
||||
locale: string
|
||||
database: Database
|
||||
// no transaction here because this is directly called from an API endpoint
|
||||
}): Promise<{ mailLoginToken: string }> => {
|
||||
let deviceName = null
|
||||
|
||||
if (deviceAuthToken !== undefined) {
|
||||
const info = await database.transaction(async (transaction) => {
|
||||
const deviceEntryUnsafe = await database.device.findOne({
|
||||
where: { deviceAuthToken },
|
||||
attributes: ['familyId', 'name'],
|
||||
transaction
|
||||
})
|
||||
|
||||
if (!deviceEntryUnsafe) {
|
||||
throw new Unauthorized()
|
||||
}
|
||||
|
||||
const deviceEntry = {
|
||||
familyId: deviceEntryUnsafe.familyId,
|
||||
name: deviceEntryUnsafe.name
|
||||
}
|
||||
|
||||
const userEntryCounter = await database.user.count({
|
||||
where: {
|
||||
familyId: deviceEntry.familyId,
|
||||
mail
|
||||
},
|
||||
transaction
|
||||
})
|
||||
|
||||
if (userEntryCounter === 1) {
|
||||
return { deviceName: deviceEntry.name }
|
||||
} else {
|
||||
// do not show the device name if it is from another family
|
||||
// otherwise third parties could chose a part of the content of the mail
|
||||
return { deviceName: null }
|
||||
}
|
||||
})
|
||||
|
||||
deviceName = info.deviceName
|
||||
}
|
||||
|
||||
try {
|
||||
await checkMailSendLimit(mail)
|
||||
} catch (ex) {
|
||||
@@ -41,7 +81,8 @@ export const sendLoginCode = async ({ mail, locale, database }: {
|
||||
await sendAuthenticationMail({
|
||||
receiver: mail,
|
||||
code,
|
||||
locale
|
||||
locale,
|
||||
deviceName
|
||||
})
|
||||
|
||||
await database.transaction(async (transaction) => {
|
||||
|
||||
+10
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* server component for the TimeLimit App
|
||||
* Copyright (C) 2019 - 2020 Jonas Lochmann
|
||||
* Copyright (C) 2019 - 2021 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
@@ -36,7 +36,11 @@ const email = new Email({
|
||||
}
|
||||
})
|
||||
|
||||
export const sendAuthenticationMail = async ({ receiver, code, locale }: {receiver: string, code: string, locale: string}) => {
|
||||
export const sendAuthenticationMail = async ({
|
||||
receiver, code, locale, deviceName
|
||||
}: {
|
||||
receiver: string, code: string, locale: string, deviceName: string | null
|
||||
}) => {
|
||||
await email.send({
|
||||
template: join(__dirname, '../../other/mail/login'),
|
||||
message: {
|
||||
@@ -47,7 +51,10 @@ export const sendAuthenticationMail = async ({ receiver, code, locale }: {receiv
|
||||
introtext: locale === 'de' ? 'Geben Sie zum Authentifizieren folgenden Code in TimeLimit ein' : 'To authenticate, enter the following code in TimeLimit',
|
||||
code,
|
||||
outrotext: locale === 'de' ? 'Geben Sie diesen Code nicht an Dritte weiter.' : 'Do not share this code with third parties.',
|
||||
mailimprint
|
||||
mailimprint,
|
||||
deviceName,
|
||||
deviceNameIntro: locale === 'de' ? 'Die Anmeldung wurde am Gerät' : 'The login was attempted at the device',
|
||||
deviceNameOutro: locale === 'de' ? 'versucht.' : '.'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user