nest-authify
Version:
Complete authentication and authorization package for NestJS - Monolith and Microservices ready with OAuth, JWT, Redis sessions
479 lines • 22.1 kB
JavaScript
"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