mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-05 17:26:36 +00:00
Merge pull request #4585 from Infisical/ENG-3806
Add reset email password email for oauth user
This commit is contained in:
@@ -788,6 +788,7 @@ export const registerRoutes = async (
|
|||||||
smtpService,
|
smtpService,
|
||||||
authDAL,
|
authDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
|
orgMembershipDAL,
|
||||||
totpConfigDAL
|
totpConfigDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { OrgServiceActor } from "@app/lib/types";
|
|||||||
|
|
||||||
import { TAuthTokenServiceFactory } from "../auth-token/auth-token-service";
|
import { TAuthTokenServiceFactory } from "../auth-token/auth-token-service";
|
||||||
import { TokenType } from "../auth-token/auth-token-types";
|
import { TokenType } from "../auth-token/auth-token-types";
|
||||||
|
import { TOrgMembershipDALFactory } from "../org-membership/org-membership-dal";
|
||||||
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
||||||
import { TTotpConfigDALFactory } from "../totp/totp-config-dal";
|
import { TTotpConfigDALFactory } from "../totp/totp-config-dal";
|
||||||
import { TUserDALFactory } from "../user/user-dal";
|
import { TUserDALFactory } from "../user/user-dal";
|
||||||
@@ -22,6 +23,7 @@ import { ActorType, AuthMethod, AuthTokenType } from "./auth-type";
|
|||||||
type TAuthPasswordServiceFactoryDep = {
|
type TAuthPasswordServiceFactoryDep = {
|
||||||
authDAL: TAuthDALFactory;
|
authDAL: TAuthDALFactory;
|
||||||
userDAL: TUserDALFactory;
|
userDAL: TUserDALFactory;
|
||||||
|
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "find">;
|
||||||
tokenService: TAuthTokenServiceFactory;
|
tokenService: TAuthTokenServiceFactory;
|
||||||
smtpService: TSmtpService;
|
smtpService: TSmtpService;
|
||||||
totpConfigDAL: Pick<TTotpConfigDALFactory, "delete">;
|
totpConfigDAL: Pick<TTotpConfigDALFactory, "delete">;
|
||||||
@@ -31,6 +33,7 @@ export type TAuthPasswordFactory = ReturnType<typeof authPaswordServiceFactory>;
|
|||||||
export const authPaswordServiceFactory = ({
|
export const authPaswordServiceFactory = ({
|
||||||
authDAL,
|
authDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
|
orgMembershipDAL,
|
||||||
tokenService,
|
tokenService,
|
||||||
smtpService,
|
smtpService,
|
||||||
totpConfigDAL
|
totpConfigDAL
|
||||||
@@ -47,21 +50,46 @@ export const authPaswordServiceFactory = ({
|
|||||||
|
|
||||||
if (user && user.isAccepted) {
|
if (user && user.isAccepted) {
|
||||||
const cfg = getConfig();
|
const cfg = getConfig();
|
||||||
const token = await tokenService.createTokenForUser({
|
|
||||||
type: TokenType.TOKEN_EMAIL_PASSWORD_RESET,
|
|
||||||
userId: user.id
|
|
||||||
});
|
|
||||||
|
|
||||||
await smtpService.sendMail({
|
const hasEmailAuth = user.authMethods?.includes(AuthMethod.EMAIL);
|
||||||
template: SmtpTemplates.ResetPassword,
|
|
||||||
recipients: [email],
|
if (!hasEmailAuth) {
|
||||||
subjectLine: "Infisical password reset",
|
const orgMemberships = await orgMembershipDAL.find({ userId: user.id });
|
||||||
substitutions: {
|
const lastLoginMethod =
|
||||||
|
orgMemberships
|
||||||
|
.filter((membership) => membership.lastLoginAuthMethod)
|
||||||
|
.sort((a, b) => (b.updatedAt || new Date(0)).getTime() - (a.updatedAt || new Date(0)).getTime())[0]
|
||||||
|
?.lastLoginAuthMethod || null;
|
||||||
|
const substitutions = {
|
||||||
email,
|
email,
|
||||||
token,
|
lastLoginMethod,
|
||||||
callback_url: cfg.SITE_URL ? `${cfg.SITE_URL}/password-reset` : ""
|
isCloud: cfg.isCloud,
|
||||||
}
|
siteUrl: cfg.SITE_URL || ""
|
||||||
});
|
};
|
||||||
|
|
||||||
|
await smtpService.sendMail({
|
||||||
|
template: SmtpTemplates.OAuthPasswordReset,
|
||||||
|
recipients: [email],
|
||||||
|
subjectLine: "Password reset not available",
|
||||||
|
substitutions
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const token = await tokenService.createTokenForUser({
|
||||||
|
type: TokenType.TOKEN_EMAIL_PASSWORD_RESET,
|
||||||
|
userId: user.id
|
||||||
|
});
|
||||||
|
|
||||||
|
await smtpService.sendMail({
|
||||||
|
template: SmtpTemplates.ResetPassword,
|
||||||
|
recipients: [email],
|
||||||
|
subjectLine: "Infisical password reset",
|
||||||
|
substitutions: {
|
||||||
|
email,
|
||||||
|
token,
|
||||||
|
callback_url: cfg.SITE_URL ? `${cfg.SITE_URL}/password-reset` : ""
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import { Heading, Section, Text } from "@react-email/components";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||||
|
import { BaseLink } from "./BaseLink";
|
||||||
|
|
||||||
|
interface OAuthPasswordResetTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||||
|
email: string;
|
||||||
|
lastLoginMethod?: string | null;
|
||||||
|
isCloud: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OAuthPasswordResetTemplate = ({
|
||||||
|
email,
|
||||||
|
lastLoginMethod,
|
||||||
|
isCloud,
|
||||||
|
siteUrl
|
||||||
|
}: OAuthPasswordResetTemplateProps) => {
|
||||||
|
const getAuthMethodDisplayName = (method: string) => {
|
||||||
|
return method
|
||||||
|
.split("-")
|
||||||
|
.map((word) => {
|
||||||
|
const upperWord = word.toUpperCase();
|
||||||
|
if (["SAML", "LDAP", "OIDC", "SSO"].includes(upperWord)) {
|
||||||
|
return upperWord;
|
||||||
|
}
|
||||||
|
return word.charAt(0).toUpperCase() + word.slice(1);
|
||||||
|
})
|
||||||
|
.join(" ");
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAuthMethodMessage = () => {
|
||||||
|
if (lastLoginMethod) {
|
||||||
|
const displayName = getAuthMethodDisplayName(lastLoginMethod);
|
||||||
|
return `Please continue by signing in with ${displayName}.`;
|
||||||
|
}
|
||||||
|
return "Please continue using the same authentication method you previously used to sign in (e.g., SSO, SAML, OAuth, or another configured provider).";
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<BaseEmailWrapper
|
||||||
|
title="Password Reset Not Available"
|
||||||
|
preview="Your account doesn't have password login enabled."
|
||||||
|
siteUrl={siteUrl}
|
||||||
|
>
|
||||||
|
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
|
||||||
|
<strong>Password Reset Not Available</strong>
|
||||||
|
</Heading>
|
||||||
|
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||||
|
<Text className="text-[14px]">
|
||||||
|
<strong>Password reset is not available for this account.</strong>
|
||||||
|
</Text>
|
||||||
|
<Text className="text-[14px]">
|
||||||
|
A password reset was requested for your Infisical account ({email}), but password login has not been enabled
|
||||||
|
for your account.
|
||||||
|
</Text>
|
||||||
|
<Text className="text-[14px]">{getAuthMethodMessage()}</Text>
|
||||||
|
<Text className="text-[14px]">
|
||||||
|
If you did not initiate this request, please contact{" "}
|
||||||
|
{isCloud ? (
|
||||||
|
<>
|
||||||
|
us immediately at <BaseLink href="mailto:[email protected]">support@infisical.com</BaseLink>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
"your administrator immediately"
|
||||||
|
)}
|
||||||
|
.
|
||||||
|
</Text>
|
||||||
|
</Section>
|
||||||
|
</BaseEmailWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OAuthPasswordResetTemplate;
|
||||||
|
|
||||||
|
OAuthPasswordResetTemplate.PreviewProps = {
|
||||||
|
email: "[email protected]",
|
||||||
|
lastLoginMethod: "github",
|
||||||
|
isCloud: true,
|
||||||
|
siteUrl: "https://infisical.com"
|
||||||
|
} as OAuthPasswordResetTemplateProps;
|
||||||
@@ -7,6 +7,7 @@ export * from "./ExternalImportStartedTemplate";
|
|||||||
export * from "./ExternalImportSucceededTemplate";
|
export * from "./ExternalImportSucceededTemplate";
|
||||||
export * from "./IntegrationSyncFailedTemplate";
|
export * from "./IntegrationSyncFailedTemplate";
|
||||||
export * from "./NewDeviceLoginTemplate";
|
export * from "./NewDeviceLoginTemplate";
|
||||||
|
export * from "./OAuthPasswordResetTemplate";
|
||||||
export * from "./OrgAdminBreakglassAccessTemplate";
|
export * from "./OrgAdminBreakglassAccessTemplate";
|
||||||
export * from "./OrgAdminProjectGrantAccessTemplate";
|
export * from "./OrgAdminProjectGrantAccessTemplate";
|
||||||
export * from "./OrganizationAssignmentTemplate";
|
export * from "./OrganizationAssignmentTemplate";
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
ExternalImportSucceededTemplate,
|
ExternalImportSucceededTemplate,
|
||||||
IntegrationSyncFailedTemplate,
|
IntegrationSyncFailedTemplate,
|
||||||
NewDeviceLoginTemplate,
|
NewDeviceLoginTemplate,
|
||||||
|
OAuthPasswordResetTemplate,
|
||||||
OrgAdminBreakglassAccessTemplate,
|
OrgAdminBreakglassAccessTemplate,
|
||||||
OrgAdminProjectGrantAccessTemplate,
|
OrgAdminProjectGrantAccessTemplate,
|
||||||
OrganizationAssignmentTemplate,
|
OrganizationAssignmentTemplate,
|
||||||
@@ -63,6 +64,7 @@ export enum SmtpTemplates {
|
|||||||
NewDeviceJoin = "newDevice",
|
NewDeviceJoin = "newDevice",
|
||||||
OrgInvite = "organizationInvitation",
|
OrgInvite = "organizationInvitation",
|
||||||
OrgAssignment = "organizationAssignment",
|
OrgAssignment = "organizationAssignment",
|
||||||
|
OAuthPasswordReset = "oAuthPasswordReset",
|
||||||
ResetPassword = "passwordReset",
|
ResetPassword = "passwordReset",
|
||||||
SetupPassword = "passwordSetup",
|
SetupPassword = "passwordSetup",
|
||||||
SecretLeakIncident = "secretLeakIncident",
|
SecretLeakIncident = "secretLeakIncident",
|
||||||
@@ -121,6 +123,7 @@ const EmailTemplateMap: Record<SmtpTemplates, React.FC<any>> = {
|
|||||||
[SmtpTemplates.OrgAdminProjectDirectAccess]: OrgAdminProjectGrantAccessTemplate,
|
[SmtpTemplates.OrgAdminProjectDirectAccess]: OrgAdminProjectGrantAccessTemplate,
|
||||||
[SmtpTemplates.ProjectAccessRequest]: ProjectAccessRequestTemplate,
|
[SmtpTemplates.ProjectAccessRequest]: ProjectAccessRequestTemplate,
|
||||||
[SmtpTemplates.SecretApprovalRequestNeedsReview]: SecretApprovalRequestNeedsReviewTemplate,
|
[SmtpTemplates.SecretApprovalRequestNeedsReview]: SecretApprovalRequestNeedsReviewTemplate,
|
||||||
|
[SmtpTemplates.OAuthPasswordReset]: OAuthPasswordResetTemplate,
|
||||||
[SmtpTemplates.ResetPassword]: PasswordResetTemplate,
|
[SmtpTemplates.ResetPassword]: PasswordResetTemplate,
|
||||||
[SmtpTemplates.SetupPassword]: PasswordSetupTemplate,
|
[SmtpTemplates.SetupPassword]: PasswordSetupTemplate,
|
||||||
[SmtpTemplates.PkiExpirationAlert]: PkiExpirationAlertTemplate,
|
[SmtpTemplates.PkiExpirationAlert]: PkiExpirationAlertTemplate,
|
||||||
|
|||||||
Reference in New Issue
Block a user