UNPKG

nest-authify

Version:

Complete authentication and authorization package for NestJS - Monolith and Microservices ready with OAuth, JWT, Redis sessions

479 lines 22.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); 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 __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var AuthModule_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.AuthModule = void 0; const common_1 = require("@nestjs/common"); const config_1 = require("@nestjs/config"); const jwt_1 = require("@nestjs/jwt"); const passport_1 = require("@nestjs/passport"); const Joi = __importStar(require("joi")); const auth_controller_1 = require("./controllers/auth.controller"); const oauth_controller_1 = require("./controllers/oauth.controller"); const auth_service_1 = require("./services/auth.service"); const jwt_strategy_1 = require("./strategies/jwt.strategy"); const local_strategy_1 = require("./strategies/local.strategy"); const facebook_auth_guard_1 = require("./guards/facebook-auth.guard"); const github_auth_guard_1 = require("./guards/github-auth.guard"); const google_auth_guard_1 = require("./guards/google-auth.guard"); const jwt_auth_guard_1 = require("./guards/jwt-auth.guard"); const local_auth_guard_1 = require("./guards/local-auth.guard"); const roles_guard_1 = require("./guards/roles.guard"); const core_1 = require("@nestjs/core"); const ioredis_1 = __importDefault(require("ioredis")); const constants_1 = require("./constants"); const hash_service_1 = require("./services/hash.service"); const memory_session_store_1 = require("./session/memory-session.store"); const redis_session_store_1 = require("./session/redis-session.store"); let AuthModule = AuthModule_1 = class AuthModule { static forRoot(options, app) { const providers = this.createProviders(options); const controllers = this.createControllers(options); const imports = this.createImports(options); return { module: AuthModule_1, imports, controllers, providers: [ ...providers, { provide: core_1.APP_GUARD, useClass: jwt_auth_guard_1.JwtAuthGuard, }, { provide: core_1.APP_GUARD, useClass: roles_guard_1.RolesGuard, }, { provide: 'NEST_APP', useValue: app }, ], exports: [ constants_1.AUTH_SERVICE, constants_1.SESSION_STORE, jwt_1.JwtModule, passport_1.PassportModule, hash_service_1.HashService, jwt_auth_guard_1.JwtAuthGuard, local_auth_guard_1.LocalAuthGuard, roles_guard_1.RolesGuard, google_auth_guard_1.GoogleAuthGuard, facebook_auth_guard_1.FacebookAuthGuard, github_auth_guard_1.GithubAuthGuard, ], }; } static forRootAsync(options) { const providers = this.createAsyncProviders(options); const controllers = []; const imports = [ config_1.ConfigModule.forRoot({ validationSchema: this.createValidationSchema(), isGlobal: true, }), passport_1.PassportModule.register({ defaultStrategy: 'jwt', session: false }), jwt_1.JwtModule.registerAsync({ imports: [config_1.ConfigModule], useFactory: async (configService) => ({ secret: configService.get('JWT_SECRET'), signOptions: { expiresIn: configService.get('JWT_EXPIRES_IN', '60m'), }, }), inject: [config_1.ConfigService], }), ...(options.imports || []), ]; return { module: AuthModule_1, imports, controllers, providers: [ ...providers, { provide: core_1.APP_GUARD, useClass: jwt_auth_guard_1.JwtAuthGuard, }, { provide: core_1.APP_GUARD, useClass: roles_guard_1.RolesGuard, }, ], exports: [ constants_1.AUTH_SERVICE, constants_1.SESSION_STORE, jwt_1.JwtModule, passport_1.PassportModule, hash_service_1.HashService, jwt_auth_guard_1.JwtAuthGuard, local_auth_guard_1.LocalAuthGuard, roles_guard_1.RolesGuard, google_auth_guard_1.GoogleAuthGuard, facebook_auth_guard_1.FacebookAuthGuard, github_auth_guard_1.GithubAuthGuard, ], }; } static createImports(options) { const imports = [ config_1.ConfigModule.forRoot({ validationSchema: this.createValidationSchema(), isGlobal: true, }), passport_1.PassportModule.register({ defaultStrategy: 'jwt', session: false }), jwt_1.JwtModule.register({ secret: options.jwtSecret, signOptions: { expiresIn: options.jwtExpiresIn || '60m', }, }), ]; return imports; } static isSessionStoreConfig(store) { return !!store && typeof store === 'object' && 'type' in store; } static createProviders(options) { const providers = [ { provide: constants_1.AUTH_MODULE_OPTIONS, useValue: options, }, { provide: hash_service_1.HashService, useFactory: () => { return new hash_service_1.HashService(options.hashCallback); }, }, ...(options.authRepository ? [ { provide: 'AUTH_REPOSITORY', useClass: options.authRepository, }, ] : []), { provide: constants_1.AUTH_SERVICE, useFactory: (jwtService, sessionStore, hashService, repository, configService) => { if (options.authService) { return new options.authService(jwtService, sessionStore, hashService, repository, configService); } return new auth_service_1.AuthService(jwtService, sessionStore, hashService, repository, configService); }, inject: [ jwt_1.JwtService, constants_1.SESSION_STORE, hash_service_1.HashService, { token: 'AUTH_REPOSITORY', optional: true }, { token: config_1.ConfigService, optional: true }, ], }, ]; if (options.sessionStore === null) { } else if (typeof options.sessionStore === 'function') { providers.push({ provide: constants_1.SESSION_STORE, useClass: options.sessionStore, }); } else if (this.isSessionStoreConfig(options.sessionStore)) { providers.push({ provide: constants_1.SESSION_STORE, useFactory: (configService) => { return this.createSessionStore(options.sessionStore, configService); }, inject: [{ token: config_1.ConfigService, optional: true }], }); } else { throw new Error('sessionStore inválido'); } if (options.strategies?.local) { providers.push(local_strategy_1.LocalStrategy); } if (options.strategies?.jwt) { providers.push(jwt_strategy_1.JwtStrategy); } if (options.strategies?.google && options.google) { providers.push({ provide: constants_1.GOOGLE_STRATEGY, useFactory: async (authService) => { if (!options.strategies?.google || !options.google) { return null; } try { const { Strategy } = await Promise.resolve().then(() => __importStar(require('passport-google-oauth20'))); const GoogleStrategyClass = class extends Strategy { constructor() { super({ clientID: options.google.clientId, clientSecret: options.google.clientSecret, callbackURL: options.google.callbackUrl, scope: ['email', 'profile'], }, async (_, __, profile, done) => { const user = await authService.validateOAuthUser('google', profile.id, profile); done(null, profile); }); } }; const strategy = new GoogleStrategyClass(); const passport = (await Promise.resolve().then(() => __importStar(require('passport')))).default; passport.use('google', strategy); return strategy; } catch (err) { console.warn('passport-google-oauth20 no instalado. Google desactivado.'); return null; } }, inject: [constants_1.AUTH_SERVICE], }); } if (options.strategies?.facebook && options.facebook) { providers.push({ provide: constants_1.FACEBOOK_STRATEGY, useFactory: async (authService) => { if (!options.strategies?.facebook || !options.facebook) { return null; } try { const { Strategy } = await Promise.resolve().then(() => __importStar(require('passport-facebook'))); const FacebookStrategyClass = class extends Strategy { constructor() { super({ clientID: options.facebook.clientId, clientSecret: options.facebook.clientSecret, callbackURL: options.facebook.callbackUrl || 'http://localhost:3000/auth/facebook/callback', profileFields: ['id', 'emails', 'name'], }, async (_, __, profile, done) => { const user = await authService.validateOAuthUser('facebook', profile.id, { email: profile.emails?.[0]?.value, username: profile.emails?.[0]?.value || `fb_${profile.id}`, fullName: profile.displayName, }); done(null, profile); }); } }; const strategy = new FacebookStrategyClass(); const passport = (await Promise.resolve().then(() => __importStar(require('passport')))).default; passport.use('facebook', strategy); return strategy; } catch (err) { console.warn('passport-facebook no instalado. Facebook desactivado.'); return null; } }, inject: [constants_1.AUTH_SERVICE], }); } if (options.strategies?.github && options.github) { providers.push({ provide: constants_1.GITHUB_STRATEGY, useFactory: async (authService) => { if (!options.strategies?.github || !options.github) { return null; } try { const { Strategy } = await Promise.resolve().then(() => __importStar(require('passport-github2'))); const GithubStrategyClass = class extends Strategy { constructor() { super({ clientID: options.github.clientId, clientSecret: options.github.clientSecret, callbackURL: options.github.callbackUrl, scope: ['user:email'] }, async (_, __, profile, done) => { const user = await authService.validateOAuthUser('github', profile.id, profile); done(null, profile); }); } }; const strategy = new GithubStrategyClass(); const passport = (await Promise.resolve().then(() => __importStar(require('passport')))).default; passport.use('github', strategy); return strategy; } catch (err) { console.warn('passport-github2 no instalado. GitHub desactivado.'); return null; } }, inject: [constants_1.AUTH_SERVICE], }); } providers.push(jwt_auth_guard_1.JwtAuthGuard, local_auth_guard_1.LocalAuthGuard, roles_guard_1.RolesGuard, google_auth_guard_1.GoogleAuthGuard, facebook_auth_guard_1.FacebookAuthGuard, github_auth_guard_1.GithubAuthGuard); return providers; } static createAsyncProviders(options) { const providers = [ { provide: constants_1.AUTH_MODULE_OPTIONS, useFactory: options.useFactory, inject: options.inject || [], }, { provide: hash_service_1.HashService, useFactory: (moduleOptions) => { return new hash_service_1.HashService(moduleOptions.hashCallback); }, inject: [constants_1.AUTH_MODULE_OPTIONS], }, { provide: constants_1.AUTH_SERVICE, useFactory: (moduleOptions, jwtService, sessionStore, hashService, repository, configService) => { if (moduleOptions.authService) { return new moduleOptions.authService(jwtService, sessionStore, hashService, repository, configService); } return new auth_service_1.AuthService(jwtService, sessionStore, hashService, repository, configService); }, inject: [ constants_1.AUTH_MODULE_OPTIONS, jwt_1.JwtService, constants_1.SESSION_STORE, hash_service_1.HashService, { token: 'AUTH_REPOSITORY', optional: true }, { token: config_1.ConfigService, optional: true }, ], }, ]; providers.push({ provide: constants_1.SESSION_STORE, useFactory: (moduleOptions, configService) => { if (moduleOptions.sessionStore === null) { throw new Error('En modo async: provee SESSION_STORE manualmente en AppModule'); } if (typeof moduleOptions.sessionStore === 'function') { throw new Error('Custom class no soportado en async. Provee manual en AppModule'); } return this.createSessionStore(moduleOptions.sessionStore, configService); }, inject: [constants_1.AUTH_MODULE_OPTIONS, config_1.ConfigService], }); providers.push(jwt_strategy_1.JwtStrategy, local_strategy_1.LocalStrategy, jwt_auth_guard_1.JwtAuthGuard, local_auth_guard_1.LocalAuthGuard, roles_guard_1.RolesGuard, google_auth_guard_1.GoogleAuthGuard, facebook_auth_guard_1.FacebookAuthGuard, github_auth_guard_1.GithubAuthGuard); return providers; } static createControllers(options) { const controllers = []; if (options.mode === 'normal' || options.mode === 'server') { controllers.push(auth_controller_1.AuthController); } const hasOAuth = (options.strategies?.google && options.google) || (options.strategies?.facebook && options.facebook) || (options.strategies?.github && options.github); if (hasOAuth) { controllers.push(oauth_controller_1.OAuthController); } return controllers; } static createSessionStore(sessionConfig, configService) { if (!sessionConfig || sessionConfig.type === 'memory') { return new memory_session_store_1.MemorySessionStore(); } if (sessionConfig.type === 'redis') { const redisConfig = sessionConfig.redis || { host: configService?.get('REDIS_HOST', 'localhost') || 'localhost', port: configService?.get('REDIS_PORT', 6379) || 6379, password: configService?.get('REDIS_PASSWORD'), db: configService?.get('REDIS_DB', 0) || 0, keyPrefix: configService?.get('REDIS_KEY_PREFIX', 'auth:') || 'auth:', }; const redisClient = new ioredis_1.default({ host: redisConfig.host, port: redisConfig.port, password: redisConfig.password, db: redisConfig.db, keyPrefix: redisConfig.keyPrefix, }); const providers = [ { provide: 'REDIS_CLIENT', useValue: redisClient, }, { provide: 'REDIS_CONFIG', useValue: { keyPrefix: redisConfig.keyPrefix }, }, ]; return new redis_session_store_1.RedisSessionStore(redisClient, { keyPrefix: redisConfig.keyPrefix }); } return new memory_session_store_1.MemorySessionStore(); } static createValidationSchema() { return Joi.object({ JWT_SECRET: Joi.string().required(), JWT_EXPIRES_IN: Joi.string().default('60m'), REFRESH_EXPIRES_IN: Joi.string().default('7d'), REDIS_HOST: Joi.string().when('SESSION_STORE_TYPE', { is: 'redis', then: Joi.required(), otherwise: Joi.optional(), }), REDIS_PORT: Joi.number().when('SESSION_STORE_TYPE', { is: 'redis', then: Joi.required(), otherwise: Joi.optional(), }), REDIS_PASSWORD: Joi.string().optional(), REDIS_DB: Joi.number().default(0), REDIS_KEY_PREFIX: Joi.string().default('auth:'), GOOGLE_CLIENT_ID: Joi.string().optional(), GOOGLE_CLIENT_SECRET: Joi.string().optional(), GOOGLE_CALLBACK_URL: Joi.string().optional(), FACEBOOK_APP_ID: Joi.string().optional(), FACEBOOK_APP_SECRET: Joi.string().optional(), FACEBOOK_CALLBACK_URL: Joi.string().optional(), GITHUB_CLIENT_ID: Joi.string().optional(), GITHUB_CLIENT_SECRET: Joi.string().optional(), GITHUB_CALLBACK_URL: Joi.string().optional(), }); } }; exports.AuthModule = AuthModule; exports.AuthModule = AuthModule = AuthModule_1 = __decorate([ (0, common_1.Global)(), (0, common_1.Module)({}) ], AuthModule); //# sourceMappingURL=auth.module.js.map