diff --git a/packages/core/src/api/resolvers/admin/auth.resolver.ts b/packages/core/src/api/resolvers/admin/auth.resolver.ts index 8d200c7719..25430933cb 100644 --- a/packages/core/src/api/resolvers/admin/auth.resolver.ts +++ b/packages/core/src/api/resolvers/admin/auth.resolver.ts @@ -37,7 +37,7 @@ export class AuthResolver extends BaseAuthResolver { @Context('req') req: Request, @Context('res') res: Response, ): Promise { - return super.login(args, ctx, req, res, 'admin'); + return super.login(args, ctx, req, res); } @Mutation() @@ -48,7 +48,7 @@ export class AuthResolver extends BaseAuthResolver { @Context('req') req: Request, @Context('res') res: Response, ): Promise { - return this.createAuthenticatedSession(ctx, args, req, res, 'shop'); + return this.createAuthenticatedSession(ctx, args, req, res); } @Mutation() diff --git a/packages/core/src/api/resolvers/base/base-auth.resolver.ts b/packages/core/src/api/resolvers/base/base-auth.resolver.ts index ba03056eec..adffad02c3 100644 --- a/packages/core/src/api/resolvers/base/base-auth.resolver.ts +++ b/packages/core/src/api/resolvers/base/base-auth.resolver.ts @@ -38,7 +38,6 @@ export class BaseAuthResolver { ctx: RequestContext, req: Request, res: Response, - apiType: ApiType, ): Promise { return await this.createAuthenticatedSession( ctx, @@ -47,7 +46,6 @@ export class BaseAuthResolver { }, req, res, - apiType, ); } @@ -56,7 +54,7 @@ export class BaseAuthResolver { if (!token) { return false; } - await this.authService.deleteSessionByToken(ctx, token); + await this.authService.destroyAuthenticatedSession(ctx, token); setAuthToken({ req, res, @@ -93,9 +91,9 @@ export class BaseAuthResolver { args: MutationAuthenticateArgs, req: Request, res: Response, - apiType: ApiType, ) { const [method, data] = Object.entries(args.input)[0]; + const { apiType } = ctx; const session = await this.authService.authenticate(ctx, apiType, method, data); if (apiType && apiType === 'admin') { const administrator = await this.administratorService.findOneByUserId(session.user.id); diff --git a/packages/core/src/api/resolvers/shop/shop-auth.resolver.ts b/packages/core/src/api/resolvers/shop/shop-auth.resolver.ts index b67a91cf07..4418a602a5 100644 --- a/packages/core/src/api/resolvers/shop/shop-auth.resolver.ts +++ b/packages/core/src/api/resolvers/shop/shop-auth.resolver.ts @@ -62,7 +62,7 @@ export class ShopAuthResolver extends BaseAuthResolver { @Context('res') res: Response, ): Promise { this.requireNativeAuthStrategy(); - return super.login(args, ctx, req, res, 'shop'); + return super.login(args, ctx, req, res); } @Mutation() @@ -73,7 +73,7 @@ export class ShopAuthResolver extends BaseAuthResolver { @Context('req') req: Request, @Context('res') res: Response, ): Promise { - return this.createAuthenticatedSession(ctx, args, req, res, 'shop'); + return this.createAuthenticatedSession(ctx, args, req, res); } @Mutation() @@ -129,7 +129,6 @@ export class ShopAuthResolver extends BaseAuthResolver { }, req, res, - 'shop', ); } else { throw new VerificationTokenError(); @@ -176,7 +175,6 @@ export class ShopAuthResolver extends BaseAuthResolver { }, req, res, - 'shop', ); } else { throw new PasswordResetTokenError(); diff --git a/packages/core/src/entity/session/anonymous-session.entity.ts b/packages/core/src/entity/session/anonymous-session.entity.ts index 0c5ccba988..e24973a1c7 100644 --- a/packages/core/src/entity/session/anonymous-session.entity.ts +++ b/packages/core/src/entity/session/anonymous-session.entity.ts @@ -5,6 +5,14 @@ import { Order } from '../order/order.entity'; import { Session } from './session.entity'; +/** + * @description + * An anonymous session is created when a unauthenticated user interacts with restricted operations, + * such as calling the `activeOrder` query in the Shop API. Anonymous sessions allow a guest Customer + * to maintain an order without requiring authentication and a registered account beforehand. + * + * @docsCategory entities + */ @ChildEntity() export class AnonymousSession extends Session { constructor(input: DeepPartial) { diff --git a/packages/core/src/entity/session/authenticated-session.entity.ts b/packages/core/src/entity/session/authenticated-session.entity.ts index b350360479..cfb9d6e02e 100644 --- a/packages/core/src/entity/session/authenticated-session.entity.ts +++ b/packages/core/src/entity/session/authenticated-session.entity.ts @@ -5,12 +5,30 @@ import { User } from '../user/user.entity'; import { Session } from './session.entity'; +/** + * @description + * An AuthenticatedSession is created upon successful authentication. + * + * @docsCategory entities + */ @ChildEntity() export class AuthenticatedSession extends Session { constructor(input: DeepPartial) { super(input); } + /** + * @description + * The {@link User} who has authenticated to create this session. + */ @ManyToOne(type => User) user: User; + + /** + * @description + * The name of the {@link AuthenticationStrategy} used when authenticating + * to create this session. + */ + @Column() + authenticationStrategy: string; } diff --git a/packages/core/src/entity/session/session.entity.ts b/packages/core/src/entity/session/session.entity.ts index 050c1fb20e..38d0c59e01 100644 --- a/packages/core/src/entity/session/session.entity.ts +++ b/packages/core/src/entity/session/session.entity.ts @@ -8,8 +8,8 @@ import { User } from '../user/user.entity'; /** * @description - * A Session is created when a user makes a request to the API. A Session can be an AnonymousSession - * in the case of un-authenticated users, otherwise it is an AuthenticatedSession. + * A Session is created when a user makes a request to restricted API operations. A Session can be an {@link AnonymousSession} + * in the case of un-authenticated users, otherwise it is an {@link AuthenticatedSession}. * * @docsCategory entities */ diff --git a/packages/core/src/service/services/auth.service.ts b/packages/core/src/service/services/auth.service.ts index aa64a4994a..15182cf9fd 100644 --- a/packages/core/src/service/services/auth.service.ts +++ b/packages/core/src/service/services/auth.service.ts @@ -79,7 +79,7 @@ export class AuthService { } user.lastLogin = new Date(); await this.connection.manager.save(user, { reload: false }); - const session = await this.createNewAuthenticatedSession(ctx, user); + const session = await this.createNewAuthenticatedSession(ctx, user, authenticationStrategy); const newSession = await this.connection.getRepository(AuthenticatedSession).save(session); this.eventBus.publish(new LoginEvent(ctx, user)); return newSession; @@ -167,12 +167,20 @@ export class AuthService { /** * Deletes all sessions for the user associated with the given session token. */ - async deleteSessionByToken(ctx: RequestContext, token: string): Promise { + async destroyAuthenticatedSession(ctx: RequestContext, token: string): Promise { const session = await this.connection.getRepository(AuthenticatedSession).findOne({ where: { token }, - relations: ['user'], + relations: ['user', 'user.authenticationMethods'], }); + if (session) { + const authenticationStrategy = this.getAuthenticationStrategy( + ctx.apiType, + session.authenticationStrategy, + ); + if (typeof authenticationStrategy.onLogOut === 'function') { + await authenticationStrategy.onLogOut(session.user); + } this.eventBus.publish(new LogoutEvent(ctx)); return this.deleteSessionsByUser(session.user); } @@ -181,6 +189,7 @@ export class AuthService { private async createNewAuthenticatedSession( ctx: RequestContext, user: User, + authenticationStrategy: AuthenticationStrategy, ): Promise { const token = await this.generateSessionToken(); const guestOrder = @@ -193,6 +202,7 @@ export class AuthService { token, user, activeOrder, + authenticationStrategy: authenticationStrategy.name, expires: this.getExpiryDate(this.sessionDurationInMs), invalidated: false, }); diff --git a/packages/core/src/service/services/user.service.ts b/packages/core/src/service/services/user.service.ts index 1627e8b03e..83579e3f70 100644 --- a/packages/core/src/service/services/user.service.ts +++ b/packages/core/src/service/services/user.service.ts @@ -51,7 +51,9 @@ export class UserService { user.identifier = identifier; const customerRole = await this.roleService.getCustomerRole(); user.roles = [customerRole]; - return this.connection.manager.save(this.addNativeAuthenticationMethod(user, identifier, password)); + return this.connection.manager.save( + await this.addNativeAuthenticationMethod(user, identifier, password), + ); } async addNativeAuthenticationMethod(user: User, identifier: string, password?: string): Promise {