diff --git a/packages/backend/server/src/core/user/service.ts b/packages/backend/server/src/core/user/service.ts index 06ee0e9ec..4602e50f8 100644 --- a/packages/backend/server/src/core/user/service.ts +++ b/packages/backend/server/src/core/user/service.ts @@ -184,7 +184,7 @@ export class UserService { const user = await this.findUserWithHashedPasswordByEmail(email); if (!user) { - throw new WrongSignInCredentials(); + throw new WrongSignInCredentials({ email }); } if (!user.password) { @@ -197,7 +197,7 @@ export class UserService { ); if (!passwordMatches) { - throw new WrongSignInCredentials(); + throw new WrongSignInCredentials({ email }); } return user; diff --git a/packages/backend/server/src/fundamentals/error/def.ts b/packages/backend/server/src/fundamentals/error/def.ts index 385eebd3f..21b9e354f 100644 --- a/packages/backend/server/src/fundamentals/error/def.ts +++ b/packages/backend/server/src/fundamentals/error/def.ts @@ -37,6 +37,25 @@ const BaseTypeToHttpStatusMap: Record = { internal_server_error: HttpStatus.INTERNAL_SERVER_ERROR, }; +const IncludedEvents = new Set([ + // email + 'invalid_email', + 'email_token_not_found', + 'invalid_email_token', + 'email_already_used', + 'same_email_provided', + // magic link + 'action_forbidden', + 'link_expired', + 'email_verification_required', + // oauth + 'missing_oauth_query_parameter', + 'unknown_oauth_provider', + 'invalid_oauth_callback_state', + 'oauth_state_expired', + 'oauth_account_already_connected', +]); + export class UserFriendlyError extends Error { /** * Standard HTTP status code @@ -100,13 +119,11 @@ export class UserFriendlyError extends Error { log(context: string) { // ignore all user behavior error log - if (this.type !== 'internal_server_error') { - // always record auth related error - const isAuthError = - typeof this.stack === 'string' && - (this.stack.includes('/core/auth/') || - this.stack.includes('/plugins/oauth/')); - if (!isAuthError) return; + if ( + this.type !== 'internal_server_error' && + !IncludedEvents.has(this.name) + ) { + return; } new Logger(context).error( @@ -239,7 +256,8 @@ export const USER_FRIENDLY_ERRORS = { }, wrong_sign_in_credentials: { type: 'invalid_input', - message: 'Wrong user email or password.', + args: { email: 'string' }, + message: ({ email }) => `Wrong user email or password: ${email}`, }, unknown_oauth_provider: { type: 'invalid_input', diff --git a/packages/backend/server/src/fundamentals/error/errors.gen.ts b/packages/backend/server/src/fundamentals/error/errors.gen.ts index c3212d71c..e87010b0b 100644 --- a/packages/backend/server/src/fundamentals/error/errors.gen.ts +++ b/packages/backend/server/src/fundamentals/error/errors.gen.ts @@ -45,10 +45,14 @@ export class SameEmailProvided extends UserFriendlyError { super('invalid_input', 'same_email_provided', message); } } +@ObjectType() +class WrongSignInCredentialsDataType { + @Field() email!: string +} export class WrongSignInCredentials extends UserFriendlyError { - constructor(message?: string) { - super('invalid_input', 'wrong_sign_in_credentials', message); + constructor(args: WrongSignInCredentialsDataType, message?: string | ((args: WrongSignInCredentialsDataType) => string)) { + super('invalid_input', 'wrong_sign_in_credentials', message, args); } } @ObjectType() @@ -670,5 +674,5 @@ registerEnumType(ErrorNames, { export const ErrorDataUnionType = createUnionType({ name: 'ErrorDataUnion', types: () => - [UnknownOauthProviderDataType, MissingOauthQueryParameterDataType, InvalidEmailDataType, InvalidPasswordLengthDataType, SpaceNotFoundDataType, NotInSpaceDataType, AlreadyInSpaceDataType, SpaceAccessDeniedDataType, SpaceOwnerNotFoundDataType, DocNotFoundDataType, DocAccessDeniedDataType, VersionRejectedDataType, InvalidHistoryTimestampDataType, DocHistoryNotFoundDataType, BlobNotFoundDataType, UnsupportedSubscriptionPlanDataType, SubscriptionAlreadyExistsDataType, SubscriptionNotExistsDataType, SameSubscriptionRecurringDataType, SubscriptionPlanNotFoundDataType, CopilotMessageNotFoundDataType, CopilotPromptNotFoundDataType, CopilotProviderSideErrorDataType, RuntimeConfigNotFoundDataType, InvalidRuntimeConfigTypeDataType] as const, + [WrongSignInCredentialsDataType, UnknownOauthProviderDataType, MissingOauthQueryParameterDataType, InvalidEmailDataType, InvalidPasswordLengthDataType, SpaceNotFoundDataType, NotInSpaceDataType, AlreadyInSpaceDataType, SpaceAccessDeniedDataType, SpaceOwnerNotFoundDataType, DocNotFoundDataType, DocAccessDeniedDataType, VersionRejectedDataType, InvalidHistoryTimestampDataType, DocHistoryNotFoundDataType, BlobNotFoundDataType, UnsupportedSubscriptionPlanDataType, SubscriptionAlreadyExistsDataType, SubscriptionNotExistsDataType, SameSubscriptionRecurringDataType, SubscriptionPlanNotFoundDataType, CopilotMessageNotFoundDataType, CopilotPromptNotFoundDataType, CopilotProviderSideErrorDataType, RuntimeConfigNotFoundDataType, InvalidRuntimeConfigTypeDataType] as const, }); diff --git a/packages/backend/server/src/schema.gql b/packages/backend/server/src/schema.gql index fe3c4bbc8..19763fda7 100644 --- a/packages/backend/server/src/schema.gql +++ b/packages/backend/server/src/schema.gql @@ -209,7 +209,7 @@ type EditorType { name: String! } -union ErrorDataUnion = AlreadyInSpaceDataType | BlobNotFoundDataType | CopilotMessageNotFoundDataType | CopilotPromptNotFoundDataType | CopilotProviderSideErrorDataType | DocAccessDeniedDataType | DocHistoryNotFoundDataType | DocNotFoundDataType | InvalidEmailDataType | InvalidHistoryTimestampDataType | InvalidPasswordLengthDataType | InvalidRuntimeConfigTypeDataType | MissingOauthQueryParameterDataType | NotInSpaceDataType | RuntimeConfigNotFoundDataType | SameSubscriptionRecurringDataType | SpaceAccessDeniedDataType | SpaceNotFoundDataType | SpaceOwnerNotFoundDataType | SubscriptionAlreadyExistsDataType | SubscriptionNotExistsDataType | SubscriptionPlanNotFoundDataType | UnknownOauthProviderDataType | UnsupportedSubscriptionPlanDataType | VersionRejectedDataType +union ErrorDataUnion = AlreadyInSpaceDataType | BlobNotFoundDataType | CopilotMessageNotFoundDataType | CopilotPromptNotFoundDataType | CopilotProviderSideErrorDataType | DocAccessDeniedDataType | DocHistoryNotFoundDataType | DocNotFoundDataType | InvalidEmailDataType | InvalidHistoryTimestampDataType | InvalidPasswordLengthDataType | InvalidRuntimeConfigTypeDataType | MissingOauthQueryParameterDataType | NotInSpaceDataType | RuntimeConfigNotFoundDataType | SameSubscriptionRecurringDataType | SpaceAccessDeniedDataType | SpaceNotFoundDataType | SpaceOwnerNotFoundDataType | SubscriptionAlreadyExistsDataType | SubscriptionNotExistsDataType | SubscriptionPlanNotFoundDataType | UnknownOauthProviderDataType | UnsupportedSubscriptionPlanDataType | VersionRejectedDataType | WrongSignInCredentialsDataType enum ErrorNames { ACCESS_DENIED @@ -1048,6 +1048,10 @@ type WorkspaceType { team: Boolean! } +type WrongSignInCredentialsDataType { + email: String! +} + type tokenType { refresh: String! sessionToken: String diff --git a/packages/backend/server/tests/auth/service.spec.ts b/packages/backend/server/tests/auth/service.spec.ts index 8b3d85742..af1795588 100644 --- a/packages/backend/server/tests/auth/service.spec.ts +++ b/packages/backend/server/tests/auth/service.spec.ts @@ -50,7 +50,7 @@ test('should throw if user not found', async t => { const { auth } = t.context; await t.throwsAsync(() => auth.signIn('u2@affine.pro', '1'), { - message: 'Wrong user email or password.', + message: 'Wrong user email or password: u2@affine.pro', }); }); @@ -72,7 +72,7 @@ test('should throw if password not match', async t => { const { auth } = t.context; await t.throwsAsync(() => auth.signIn('u1@affine.pro', '2'), { - message: 'Wrong user email or password.', + message: 'Wrong user email or password: u1@affine.pro', }); }); @@ -87,7 +87,7 @@ test('should be able to change password', async t => { await t.throwsAsync( () => auth.signIn('u1@affine.pro', '1' /* old password */), { - message: 'Wrong user email or password.', + message: 'Wrong user email or password: u1@affine.pro', } ); @@ -104,7 +104,7 @@ test('should be able to change email', async t => { await auth.changeEmail(u1.id, 'u2@affine.pro'); await t.throwsAsync(() => auth.signIn('u1@affine.pro' /* old email */, '1'), { - message: 'Wrong user email or password.', + message: 'Wrong user email or password: u1@affine.pro', }); signedInU1 = await auth.signIn('u2@affine.pro', '1'); diff --git a/packages/frontend/graphql/src/schema.ts b/packages/frontend/graphql/src/schema.ts index b6fe4918d..4baeb351a 100644 --- a/packages/frontend/graphql/src/schema.ts +++ b/packages/frontend/graphql/src/schema.ts @@ -285,7 +285,8 @@ export type ErrorDataUnion = | SubscriptionPlanNotFoundDataType | UnknownOauthProviderDataType | UnsupportedSubscriptionPlanDataType - | VersionRejectedDataType; + | VersionRejectedDataType + | WrongSignInCredentialsDataType; export enum ErrorNames { ACCESS_DENIED = 'ACCESS_DENIED', @@ -1408,6 +1409,11 @@ export interface WorkspaceTypePublicPageArgs { pageId: Scalars['String']['input']; } +export interface WrongSignInCredentialsDataType { + __typename?: 'WrongSignInCredentialsDataType'; + email: Scalars['String']['output']; +} + export interface TokenType { __typename?: 'tokenType'; refresh: Scalars['String']['output'];