UNPKG

@medusajs/framework

Version:
141 lines 6.01 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getAuthContextFromJwtToken = exports.authenticate = void 0; const utils_1 = require("@medusajs/utils"); const jsonwebtoken_1 = require("jsonwebtoken"); const SESSION_AUTH = "session"; const BEARER_AUTH = "bearer"; const API_KEY_AUTH = "api-key"; // This is the only hard-coded actor type, as API keys have special handling for now. We could also generalize API keys to carry the actor type with them. const ADMIN_ACTOR_TYPE = "user"; const authenticate = (actorType, authType, options = {}) => { const authenticateMiddleware = async (req, res, next) => { const authTypes = Array.isArray(authType) ? authType : [authType]; const actorTypes = Array.isArray(actorType) ? actorType : [actorType]; const req_ = req; // We only allow authenticating using a secret API key on the admin const isExclusivelyUser = actorTypes.length === 1 && actorTypes[0] === ADMIN_ACTOR_TYPE; if (authTypes.includes(API_KEY_AUTH) && isExclusivelyUser) { const apiKey = await getApiKeyInfo(req); if (apiKey) { req_.auth_context = { actor_id: apiKey.id, actor_type: "api-key", auth_identity_id: "", app_metadata: {}, }; return next(); } } // We try to extract the auth context either from the session or from a JWT token let authContext = getAuthContextFromSession(req.session, authTypes, actorTypes); if (!authContext) { const { projectConfig: { http }, } = req.scope.resolve(utils_1.ContainerRegistrationKeys.CONFIG_MODULE); authContext = (0, exports.getAuthContextFromJwtToken)(req.headers.authorization, http.jwtSecret, authTypes, actorTypes, http.jwtPublicKey, http.jwtVerifyOptions ?? http.jwtOptions); } // If the entity is authenticated, and it is a registered actor we can continue if (authContext?.actor_id) { req_.auth_context = authContext; return next(); } // If the entity is authenticated, but there is no registered actor yet, we can continue (eg. in the case of a user invite) if allow unregistered is set // We also don't want to allow creating eg. a customer with a token created for a `user` provider. if (authContext?.auth_identity_id && options.allowUnregistered && isActorTypePermitted(actorTypes, authContext.actor_type)) { req_.auth_context = authContext; return next(); } // If we allow unauthenticated requests (i.e public endpoints), just continue if (options.allowUnauthenticated) { return next(); } res.status(401).json({ message: "Unauthorized" }); }; return authenticateMiddleware; }; exports.authenticate = authenticate; const getApiKeyInfo = async (req) => { const authHeader = req.headers.authorization; if (!authHeader) { return null; } const [tokenType, token] = authHeader.split(" "); if (tokenType.toLowerCase() !== "basic" || !token) { return null; } // The token could have been base64 encoded, we want to decode it first. let normalizedToken = token; if (!token.startsWith("sk_")) { normalizedToken = Buffer.from(token, "base64").toString("utf-8"); } // Basic auth is defined as a username:password set, and since the token is set to the username we need to trim the colon if (normalizedToken.endsWith(":")) { normalizedToken = normalizedToken.slice(0, -1); } // Secret tokens start with 'sk_', and if it doesn't it could be a user JWT or a malformed token if (!normalizedToken.startsWith("sk_")) { return null; } const apiKeyModule = req.scope.resolve(utils_1.Modules.API_KEY); try { const apiKey = await apiKeyModule.authenticate(normalizedToken); if (!apiKey) { return null; } return apiKey; } catch (error) { console.error(error); return null; } }; const getAuthContextFromSession = (session = {}, authTypes, actorTypes) => { if (!authTypes.includes(SESSION_AUTH)) { return null; } if (session.auth_context && isActorTypePermitted(actorTypes, session.auth_context?.actor_type)) { return session.auth_context; } return null; }; const getAuthContextFromJwtToken = (authHeader, jwtSecret, authTypes, actorTypes, jwtPublicKey, jwtOptions) => { if (!authTypes.includes(BEARER_AUTH)) { return null; } if (!authHeader) { return null; } const re = /(\S+)\s+(\S+)/; const matches = authHeader.match(re); // TODO: figure out how to obtain token (and store correct data in token) if (matches) { const tokenType = matches[1]; const token = matches[2]; if (tokenType.toLowerCase() === BEARER_AUTH) { // get config jwt secret // verify token and set authUser try { const options = { ...jwtOptions }; if (!options.algorithms && options.algorithm) { options.algorithms = [options.algorithm]; delete options.algorithm; } const verified = (0, jsonwebtoken_1.verify)(token, jwtPublicKey ?? jwtSecret, options); if (isActorTypePermitted(actorTypes, verified.actor_type)) { return verified; } } catch (err) { return null; } } } return null; }; exports.getAuthContextFromJwtToken = getAuthContextFromJwtToken; const isActorTypePermitted = (actorTypes, currentActorType) => { return actorTypes.includes("*") || actorTypes.includes(currentActorType); }; //# sourceMappingURL=authenticate-middleware.js.map