mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-10-09 03:09:04 +02:00
feat: add configurable login notification modes (#1813)
This commit is contained in:
@@ -202,6 +202,14 @@
|
||||
"this_can_be_useful_for_selfsigned_certificates": "This can be useful for self-signed certificates.",
|
||||
"enabled_emails": "Enabled Emails",
|
||||
"email_login_notification": "Email Login Notification",
|
||||
"email_login_notification_description": "Choose when users receive an email after signing in.",
|
||||
"login_notification_disabled_description": "Do not send sign-in notification emails or use browser recognition cookies.",
|
||||
"login_notification_always": "Every sign-in",
|
||||
"login_notification_always_description": "Send an email after every successful sign-in. No browser recognition cookie is used.",
|
||||
"login_notification_ip_and_user_agent": "New IP address or browser",
|
||||
"login_notification_ip_and_user_agent_description": "Send an email when the exact combination of IP address and browser User-Agent is not in the user's sign-in history. No browser recognition cookie is used.",
|
||||
"login_notification_browser_recognition": "Unrecognized browser",
|
||||
"login_notification_browser_recognition_description": "Send an email unless the browser is recognized by its cookie or a matching IP address and User-Agent in the user's sign-in history.",
|
||||
"send_an_email_to_the_user_when_they_log_in_from_a_new_device": "Send an email to the user when they log in from a new device.",
|
||||
"emai_login_code_requested_by_user": "Email Login Code Requested by User",
|
||||
"allow_users_to_sign_in_with_a_login_code_sent_to_their_email": "Allows users to bypass passkeys by requesting a login code sent to their email. This significantly reduces security as anyone with access to the user's email can gain entry.",
|
||||
|
||||
@@ -31,7 +31,7 @@ export type AllAppConfig = AppConfig & {
|
||||
smtpPassword: string;
|
||||
smtpTls: 'none' | 'starttls' | 'tls';
|
||||
smtpSkipCertVerify: boolean;
|
||||
emailLoginNotificationEnabled: boolean;
|
||||
emailLoginNotificationMode: 'disabled' | 'always' | 'ipAndUserAgent' | 'browserRecognition';
|
||||
emailApiKeyExpirationEnabled: boolean;
|
||||
// LDAP
|
||||
ldapUrl: string;
|
||||
|
||||
+55
-9
@@ -30,6 +30,22 @@
|
||||
tls: 'TLS'
|
||||
};
|
||||
|
||||
const notificationOptions = $derived({
|
||||
disabled: { label: m.never(), description: m.login_notification_disabled_description() },
|
||||
always: {
|
||||
label: m.login_notification_always(),
|
||||
description: m.login_notification_always_description()
|
||||
},
|
||||
ipAndUserAgent: {
|
||||
label: m.login_notification_ip_and_user_agent(),
|
||||
description: m.login_notification_ip_and_user_agent_description()
|
||||
},
|
||||
browserRecognition: {
|
||||
label: m.login_notification_browser_recognition(),
|
||||
description: m.login_notification_browser_recognition_description()
|
||||
}
|
||||
});
|
||||
|
||||
let isSendingTestEmail = $state(false);
|
||||
|
||||
const formSchema = z
|
||||
@@ -49,7 +65,12 @@
|
||||
emailOneTimeAccessAsUnauthenticatedEnabled: z.boolean(),
|
||||
emailVerificationEnabled: z.boolean(),
|
||||
emailOneTimeAccessAsAdminEnabled: z.boolean(),
|
||||
emailLoginNotificationEnabled: z.boolean(),
|
||||
emailLoginNotificationMode: z.enum([
|
||||
'disabled',
|
||||
'always',
|
||||
'ipAndUserAgent',
|
||||
'browserRecognition'
|
||||
]),
|
||||
emailApiKeyExpirationEnabled: z.boolean()
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
@@ -63,7 +84,6 @@
|
||||
'emailOneTimeAccessAsUnauthenticatedEnabled',
|
||||
'emailVerificationEnabled',
|
||||
'emailOneTimeAccessAsAdminEnabled',
|
||||
'emailLoginNotificationEnabled',
|
||||
'emailApiKeyExpirationEnabled'
|
||||
];
|
||||
|
||||
@@ -84,7 +104,8 @@
|
||||
const anyProvided = requiredSmtpFields.some((f) => !!data[f]);
|
||||
requireFieldsWhen(anyProvided, m.smtp_field_required_when_other_provided());
|
||||
|
||||
const emailEnabled = emailFields.some((f) => data[f]);
|
||||
const emailEnabled =
|
||||
data.emailLoginNotificationMode !== 'disabled' || emailFields.some((f) => data[f]);
|
||||
requireFieldsWhen(emailEnabled, m.smtp_field_required_when_email_enabled());
|
||||
});
|
||||
|
||||
@@ -194,12 +215,37 @@
|
||||
</div>
|
||||
<h4 class="mt-10 text-lg font-semibold">{m.enabled_emails()}</h4>
|
||||
<div class="mt-4 flex flex-col gap-5">
|
||||
<SwitchWithLabel
|
||||
id="email-login-notification"
|
||||
label={m.email_login_notification()}
|
||||
description={m.send_an_email_to_the_user_when_they_log_in_from_a_new_device()}
|
||||
bind:checked={$inputs.emailLoginNotificationEnabled.value}
|
||||
/>
|
||||
<Field.Field>
|
||||
<div>
|
||||
<Field.Label for="email-login-notification">{m.email_login_notification()}</Field.Label>
|
||||
<Field.Description>{m.email_login_notification_description()}</Field.Description>
|
||||
</div>
|
||||
<Select.Root
|
||||
type="single"
|
||||
disabled={$appConfigStore.uiConfigDisabled}
|
||||
value={$inputs.emailLoginNotificationMode.value}
|
||||
allowDeselect={false}
|
||||
onValueChange={(value) =>
|
||||
($inputs.emailLoginNotificationMode.value =
|
||||
value as AllAppConfig['emailLoginNotificationMode'])}
|
||||
>
|
||||
<Select.Trigger id="email-login-notification" class="w-full">
|
||||
{notificationOptions[$inputs.emailLoginNotificationMode.value].label}
|
||||
</Select.Trigger>
|
||||
<Select.Content class="w-[calc(var(--bits-select-anchor-width)+--spacing(3))]">
|
||||
<Select.Group>
|
||||
{#each Object.entries(notificationOptions) as [value, option] (value)}
|
||||
<Select.Item {value} label={option.label}>
|
||||
<div class="flex flex-col items-start gap-1 whitespace-normal">
|
||||
<span class="font-medium">{option.label}</span>
|
||||
<span class="text-muted-foreground text-xs">{option.description}</span>
|
||||
</div>
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Group>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</Field.Field>
|
||||
<SwitchWithLabel
|
||||
id="email-verification"
|
||||
label={m.email_verification()}
|
||||
|
||||
Reference in New Issue
Block a user