Browse Source

fix(core): Fix interop issues between native and external auth (#1968)

jcdgit 3 years ago
parent
commit
82f6b61140

+ 5 - 2
packages/core/src/config/auth/native-authentication-strategy.ts

@@ -62,7 +62,7 @@ export class NativeAuthenticationStrategy implements AuthenticationStrategy<Nati
     private getUserFromIdentifier(ctx: RequestContext, identifier: string): Promise<User | undefined> {
         return this.connection.getRepository(ctx, User).findOne({
             where: { identifier, deletedAt: null },
-            relations: ['roles', 'roles.channels'],
+            relations: ['roles', 'roles.channels', 'authenticationMethods'],
         });
     }
 
@@ -76,7 +76,10 @@ export class NativeAuthenticationStrategy implements AuthenticationStrategy<Nati
         if (!user) {
             return false;
         }
-        const nativeAuthMethod = user.getNativeAuthenticationMethod();
+        const nativeAuthMethod = user.getNativeAuthenticationMethod(false);
+        if (!nativeAuthMethod) {
+            return false;
+        }
         const pw =
             (
                 await this.connection

+ 5 - 2
packages/core/src/entity/user/user.entity.ts

@@ -48,14 +48,17 @@ export class User extends VendureEntity implements HasCustomFields, SoftDeletabl
     @Column(type => CustomUserFields)
     customFields: CustomUserFields;
 
-    getNativeAuthenticationMethod(): NativeAuthenticationMethod {
+    getNativeAuthenticationMethod(): NativeAuthenticationMethod;
+    getNativeAuthenticationMethod(strict? : boolean): NativeAuthenticationMethod | undefined;
+
+    getNativeAuthenticationMethod(strict? : boolean): NativeAuthenticationMethod | undefined {
         if (!this.authenticationMethods) {
             throw new InternalServerError('error.user-authentication-methods-not-loaded');
         }
         const match = this.authenticationMethods.find(
             (m): m is NativeAuthenticationMethod => m instanceof NativeAuthenticationMethod,
         );
-        if (!match) {
+        if (!match && (strict === undefined || strict)) {
             throw new InternalServerError('error.native-authentication-methods-not-found');
         }
         return match;

+ 6 - 11
packages/core/src/service/helpers/external-authentication/external-authentication.service.ts

@@ -214,21 +214,16 @@ export class ExternalAuthenticationService {
         strategy: string,
         externalIdentifier: string,
     ): Promise<User | undefined> {
-        const usersWithMatchingIdentifier = await this.connection
+        const user = await this.connection
             .getRepository(ctx, User)
             .createQueryBuilder('user')
-            .leftJoinAndSelect('user.authenticationMethods', 'authMethod')
+            .leftJoinAndSelect('user.authenticationMethods', 'aums')
+            .leftJoin('user.authenticationMethods', 'authMethod')
             .andWhere('authMethod.externalIdentifier = :externalIdentifier', { externalIdentifier })
+            .andWhere('authMethod.strategy = :strategy', { strategy })
             .andWhere('user.deletedAt IS NULL')
-            .getMany();
-
-        const matchingUser = usersWithMatchingIdentifier.find(user =>
-            user.authenticationMethods.find(
-                m => m instanceof ExternalAuthenticationMethod && m.strategy === strategy,
-            ),
-        );
-
-        return matchingUser;
+            .getOne();
+        return user;
     }
 
     private async findExistingCustomerUserByEmailAddress(ctx: RequestContext, emailAddress: string) {

+ 3 - 1
packages/core/src/service/services/auth.service.ts

@@ -19,6 +19,7 @@ import { ConfigService } from '../../config/config.service';
 import { TransactionalConnection } from '../../connection/transactional-connection';
 import { AuthenticatedSession } from '../../entity/session/authenticated-session.entity';
 import { User } from '../../entity/user/user.entity';
+import { ExternalAuthenticationMethod } from '../../entity/authentication-method/external-authentication-method.entity';
 import { EventBus } from '../../event-bus/event-bus';
 import { AttemptedLoginEvent } from '../../event-bus/events/attempted-login-event';
 import { LoginEvent } from '../../event-bus/events/login-event';
@@ -87,7 +88,8 @@ export class AuthService {
             user.roles = userWithRoles?.roles || [];
         }
 
-        if (this.configService.authOptions.requireVerification && !user.verified) {
+        const extAuths = (user.authenticationMethods ?? []).filter(am => am instanceof ExternalAuthenticationMethod);
+        if (!extAuths.length && this.configService.authOptions.requireVerification && !user.verified) {
             return new NotVerifiedError();
         }
         if (ctx.session && ctx.session.activeOrderId) {

+ 11 - 5
packages/core/src/service/services/user.service.ts

@@ -179,8 +179,9 @@ export class UserService {
         const user = await this.connection
             .getRepository(ctx, User)
             .createQueryBuilder('user')
-            .leftJoinAndSelect('user.authenticationMethods', 'authenticationMethod')
-            .addSelect('authenticationMethod.passwordHash')
+            .leftJoinAndSelect('user.authenticationMethods', 'aums')
+            .leftJoin('user.authenticationMethods', 'authenticationMethod')
+            .addSelect('aums.passwordHash')
             .where('authenticationMethod.verificationToken = :verificationToken', { verificationToken })
             .getOne();
         if (user) {
@@ -222,7 +223,10 @@ export class UserService {
         if (!user) {
             return;
         }
-        const nativeAuthMethod = user.getNativeAuthenticationMethod();
+        const nativeAuthMethod = user.getNativeAuthenticationMethod(false);
+        if (!nativeAuthMethod) {
+            return undefined;
+        }
         nativeAuthMethod.passwordResetToken = this.verificationTokenGenerator.generateVerificationToken();
         await this.connection.getRepository(ctx, NativeAuthenticationMethod).save(nativeAuthMethod);
         return user;
@@ -245,7 +249,8 @@ export class UserService {
         const user = await this.connection
             .getRepository(ctx, User)
             .createQueryBuilder('user')
-            .leftJoinAndSelect('user.authenticationMethods', 'authenticationMethod')
+            .leftJoinAndSelect('user.authenticationMethods', 'aums')
+            .leftJoin('user.authenticationMethods', 'authenticationMethod')
             .where('authenticationMethod.passwordResetToken = :passwordResetToken', { passwordResetToken })
             .getOne();
         if (!user) {
@@ -330,7 +335,8 @@ export class UserService {
         const user = await this.connection
             .getRepository(ctx, User)
             .createQueryBuilder('user')
-            .leftJoinAndSelect('user.authenticationMethods', 'authenticationMethod')
+            .leftJoinAndSelect('user.authenticationMethods', 'aums')
+            .leftJoin('user.authenticationMethods', 'authenticationMethod')
             .where('authenticationMethod.identifierChangeToken = :identifierChangeToken', {
                 identifierChangeToken: token,
             })