From c45edf2564718e4381465d6fd95dd59e1cdf8b00 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A8=E3=83=BC?=
 =?UTF-8?q?=E3=81=AB=E3=82=85?=
 <17376330+u1-liquid@users.noreply.github.com>
Date: Wed, 20 Mar 2024 16:15:21 +0900
Subject: [PATCH] =?UTF-8?q?fix(SSO):=20=E3=83=A1=E3=83=BC=E3=83=AB?=
 =?UTF-8?q?=E3=82=A2=E3=83=89=E3=83=AC=E3=82=B9=E3=81=8B=E3=82=89=E3=82=B3?=
 =?UTF-8?q?=E3=83=A1=E3=83=B3=E3=83=88=E3=83=BB=E3=82=BF=E3=82=B0=E3=82=92?=
 =?UTF-8?q?=E9=99=A4=E5=A4=96=20(MisskeyIO#544)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../src/misc/normalize-email-address.ts       |  5 ++++
 .../server/sso/JWTIdentifyProviderService.ts  |  3 ++-
 .../server/sso/SAMLIdentifyProviderService.ts | 27 +++++++------------
 3 files changed, 16 insertions(+), 19 deletions(-)
 create mode 100644 packages/backend/src/misc/normalize-email-address.ts

diff --git a/packages/backend/src/misc/normalize-email-address.ts b/packages/backend/src/misc/normalize-email-address.ts
new file mode 100644
index 0000000000..126eefa7e4
--- /dev/null
+++ b/packages/backend/src/misc/normalize-email-address.ts
@@ -0,0 +1,5 @@
+const specialCharactersRegexp = /(\(.*?\)|(\+.*(?=@)))/gu;
+
+export function normalizeEmailAddress(email: string | null): string | null {
+	return email?.replaceAll(specialCharactersRegexp, '') ?? null;
+}
diff --git a/packages/backend/src/server/sso/JWTIdentifyProviderService.ts b/packages/backend/src/server/sso/JWTIdentifyProviderService.ts
index 03ee7a7298..182b6af04d 100644
--- a/packages/backend/src/server/sso/JWTIdentifyProviderService.ts
+++ b/packages/backend/src/server/sso/JWTIdentifyProviderService.ts
@@ -22,6 +22,7 @@ import type { MiLocalUser } from '@/models/User.js';
 import { CacheService } from '@/core/CacheService.js';
 import { LoggerService } from '@/core/LoggerService.js';
 import { RoleService } from '@/core/RoleService.js';
+import { normalizeEmailAddress } from '@/misc/normalize-email-address.js';
 import type { FastifyInstance } from 'fastify';
 
 @Injectable()
@@ -175,7 +176,7 @@ export class JWTIdentifyProviderService {
 				preferred_username: user.username,
 				profile: `${this.config.url}/@${user.username}`,
 				picture: user.avatarUrl ?? undefined,
-				email: profile.emailVerified ? profile.email : undefined,
+				email: profile.emailVerified ? normalizeEmailAddress(profile.email) : undefined,
 				email_verified: profile.emailVerified,
 				mfa_enabled: profile.twoFactorEnabled,
 				updated_at: Math.floor((user.updatedAt?.getTime() ?? user.createdAt.getTime()) / 1000),
diff --git a/packages/backend/src/server/sso/SAMLIdentifyProviderService.ts b/packages/backend/src/server/sso/SAMLIdentifyProviderService.ts
index b5f35ddf3d..96c32291fe 100644
--- a/packages/backend/src/server/sso/SAMLIdentifyProviderService.ts
+++ b/packages/backend/src/server/sso/SAMLIdentifyProviderService.ts
@@ -26,6 +26,7 @@ import { RoleService } from '@/core/RoleService.js';
 import type { MiLocalUser } from '@/models/User.js';
 import { bindThis } from '@/decorators.js';
 import { DI } from '@/di-symbols.js';
+import { normalizeEmailAddress } from '@/misc/normalize-email-address.js';
 import type { FastifyInstance } from 'fastify';
 
 @Injectable()
@@ -440,7 +441,7 @@ export class SAMLIdentifyProviderService {
 									},
 									'saml:Subject': {
 										'saml:NameID': profile.emailVerified
-											? { '@Format': 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', '#text': profile.email }
+											? { '@Format': 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', '#text': normalizeEmailAddress(profile.email) }
 											: { '@Format': 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', '#text': user.id },
 										'saml:SubjectConfirmation': {
 											'@Method': 'urn:oasis:names:tc:SAML:2.0:cm:bearer',
@@ -531,24 +532,14 @@ export class SAMLIdentifyProviderService {
 													'#text': user.avatarUrl,
 												},
 											}] : []),
-											...(profile.emailVerified ? [
-												{
-													'@Name': 'mail',
-													'@NameFormat': 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
-													'saml:AttributeValue': {
-														'@xsi:type': 'xs:string',
-														'#text': profile.email,
-													},
+											...(profile.emailVerified ? [{
+												'@Name': 'email',
+												'@NameFormat': 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
+												'saml:AttributeValue': {
+													'@xsi:type': 'xs:string',
+													'#text': normalizeEmailAddress(profile.email),
 												},
-												{
-													'@Name': 'email',
-													'@NameFormat': 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
-													'saml:AttributeValue': {
-														'@xsi:type': 'xs:string',
-														'#text': profile.email,
-													},
-												},
-											] : []),
+											}] : []),
 											{
 												'@Name': 'email_verified',
 												'@NameFormat': 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',