@vendure/core
Version:
A modern, headless ecommerce framework
190 lines • 10.1 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthGuard = void 0;
const common_1 = require("@nestjs/common");
const core_1 = require("@nestjs/core");
const generated_types_1 = require("@vendure/common/lib/generated-types");
const ms_1 = __importDefault(require("ms"));
const errors_1 = require("../../common/error/errors");
const config_1 = require("../../config");
const config_service_1 = require("../../config/config.service");
const vendure_logger_1 = require("../../config/logger/vendure-logger");
const customer_channel_assignment_service_1 = require("../../service/helpers/customer-channel-assignment/customer-channel-assignment.service");
const request_context_service_1 = require("../../service/helpers/request-context/request-context.service");
const api_key_service_1 = require("../../service/services/api-key.service");
const session_service_1 = require("../../service/services/session.service");
const extract_session_token_1 = require("../common/extract-session-token");
const get_api_type_1 = require("../common/get-api-type");
const is_field_resolver_1 = require("../common/is-field-resolver");
const parse_context_1 = require("../common/parse-context");
const request_context_1 = require("../common/request-context");
const set_session_token_1 = require("../common/set-session-token");
const allow_decorator_1 = require("../decorators/allow.decorator");
/**
* @description
* A guard which:
*
* 1. checks for the existence of a valid session token in the request and if found,
* attaches the current User entity to the request.
* 2. enforces any permissions required by the target handler (resolver, field resolver or route),
* and throws a ForbiddenError if those permissions are not present.
*/
let AuthGuard = class AuthGuard {
constructor(reflector, configService, requestContextService, sessionService, customerChannelAssignmentService, apiKeyService) {
this.reflector = reflector;
this.configService = configService;
this.requestContextService = requestContextService;
this.sessionService = sessionService;
this.customerChannelAssignmentService = customerChannelAssignmentService;
this.apiKeyService = apiKeyService;
}
async canActivate(context) {
var _a;
const { req, res, info } = (0, parse_context_1.parseContext)(context);
const targetIsFieldResolver = (0, is_field_resolver_1.isFieldResolver)(info);
const permissions = this.reflector.get(allow_decorator_1.PERMISSIONS_METADATA_KEY, context.getHandler());
if (targetIsFieldResolver && !permissions) {
return true;
}
const authDisabled = this.configService.authOptions.disableAuth;
const hasOwnerPermission = !!permissions && permissions.includes(generated_types_1.Permission.Owner);
let requestContext;
if (targetIsFieldResolver) {
requestContext = (0, request_context_1.internal_getRequestContext)(req);
}
else {
const session = await this.getSession(req, res, hasOwnerPermission, info);
requestContext = await this.requestContextService.fromRequest(req, info, permissions, session);
const requestContextShouldBeReinitialized = await this.setActiveChannel(requestContext, session);
if (requestContextShouldBeReinitialized) {
requestContext = await this.requestContextService.fromRequest(req, info, permissions, session);
}
(0, request_context_1.internal_setRequestContext)(req, requestContext, context);
}
if (authDisabled) {
return true;
}
const strategy = this.configService.authOptions.entityAccessControlStrategy;
const isAllowed = await strategy.canAccess(requestContext, permissions !== null && permissions !== void 0 ? permissions : []);
if (!isAllowed) {
throw new errors_1.ForbiddenError(vendure_logger_1.LogLevel.Verbose);
}
await ((_a = strategy.prepareAccessControl) === null || _a === void 0 ? void 0 : _a.call(strategy, requestContext));
return true;
}
async setActiveChannel(requestContext, session) {
if (!session) {
return false;
}
// In case the session does not have an activeChannelId or the activeChannelId
// does not correspond to the current channel, the activeChannelId on the session is set
const activeChannelShouldBeSet = !session.activeChannelId || session.activeChannelId !== requestContext.channelId;
if (!activeChannelShouldBeSet) {
return false;
}
if (requestContext.activeUserId) {
await this.customerChannelAssignmentService.tryAssignToActiveChannel(requestContext);
}
await this.sessionService.setActiveChannel(session, requestContext.channel);
return true;
}
async getSession(req, res, hasOwnerPermission, info) {
const sessionToken = (0, extract_session_token_1.extractSessionToken)(req, this.configService.authOptions.tokenMethod, this.configService.authOptions.apiKeyHeaderKey);
let serializedSession;
if (sessionToken === null || sessionToken === void 0 ? void 0 : sessionToken.token) {
serializedSession = await this.getSessionFromToken(req, sessionToken, info);
if (serializedSession) {
return serializedSession;
}
// if there is a token but it cannot be validated to a Session,
// then the token is no longer valid and should be unset.
(0, set_session_token_1.setSessionToken)({
req,
res,
authOptions: this.configService.authOptions,
rememberMe: false,
sessionToken: '',
});
}
if (hasOwnerPermission && !serializedSession) {
serializedSession = await this.sessionService.createAnonymousSession();
(0, set_session_token_1.setSessionToken)({
sessionToken: serializedSession.token,
rememberMe: true,
authOptions: this.configService.authOptions,
req,
res,
});
}
return serializedSession;
}
async getSessionFromToken(req, extracted, info) {
if (extracted.method !== 'api-key') {
return this.sessionService.getSessionFromToken(extracted.token);
}
const strategy = this.apiKeyService.getApiKeyStrategyByApiType((0, get_api_type_1.getApiType)(info));
const parseResult = strategy.parse(extracted.token);
if (!parseResult) {
return;
}
const ctx = await this.requestContextService.fromRequest(req, info);
const apiKey = await this.apiKeyService.findOneByLookupId(ctx, parseResult.lookupId, [
'user',
'user.roles',
'user.roles.channels',
]);
if (!apiKey) {
return;
}
const isHashMatching = await strategy.hashingStrategy.check(extracted.token, apiKey.apiKeyHash);
if (!isHashMatching) {
return;
}
const lastUsedThreshold = new Date(Date.now() -
(typeof strategy.lastUsedAtUpdateInterval === 'string'
? (0, ms_1.default)(strategy.lastUsedAtUpdateInterval)
: strategy.lastUsedAtUpdateInterval));
if (!apiKey.lastUsedAt || apiKey.lastUsedAt < lastUsedThreshold) {
this.apiKeyService
.updateLastUsedAtByLookupId(apiKey.lookupId)
// Update the lastUsedAt timestamp in the background, we don't want to hold up the request
.catch(err => vendure_logger_1.Logger.error(`Failed to update lastUsedAt for ApiKey with lookupId ${parseResult.lookupId}`, undefined, err === null || err === void 0 ? void 0 : err.stack));
}
const session = await this.sessionService.getSessionFromToken(apiKey.apiKeyHash);
if (session) {
return session;
}
// At this point we may assert:
// 1. The token came from the api-key header
// 2. The hash matches
// 3. There is no session
// We can conclude that the API-Key is actually broken.
// For example someone could have deleted the session manually in the DB.
// We must create a new session, otherwise the API-Key is unusable.
await this.sessionService.createNewAuthenticatedSession(ctx, apiKey.user, config_1.API_KEY_AUTH_STRATEGY_NAME, apiKey.apiKeyHash);
return this.sessionService.getSessionFromToken(apiKey.apiKeyHash);
}
};
exports.AuthGuard = AuthGuard;
exports.AuthGuard = AuthGuard = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [core_1.Reflector,
config_service_1.ConfigService,
request_context_service_1.RequestContextService,
session_service_1.SessionService,
customer_channel_assignment_service_1.CustomerChannelAssignmentService,
api_key_service_1.ApiKeyService])
], AuthGuard);
//# sourceMappingURL=auth-guard.js.map