From b19ffb31fdc618e1c67cbe322dee03761c26bb83 Mon Sep 17 00:00:00 2001 From: "dan.castillo" Date: Mon, 5 Aug 2024 08:54:31 -0400 Subject: [PATCH] fix: move tests into root/test/ directory --- package.json | 6 +- src/AuthenticationRoute.d.ts | 48 ++++ src/AuthenticationRoute.js | 230 ++++++++++++++++++ src/Authenticator.d.ts | 45 ++++ src/Authenticator.js | 128 ++++++++++ src/CreateInitializePlugin.d.ts | 2 + src/CreateInitializePlugin.js | 23 ++ src/decorators/index.d.ts | 5 + src/decorators/index.js | 11 + src/decorators/is-authenticated.d.ts | 2 + src/decorators/is-authenticated.js | 7 + src/decorators/is-unauthenticated.d.ts | 2 + src/decorators/is-unauthenticated.js | 6 + src/decorators/login.d.ts | 7 + src/decorators/login.js | 20 ++ src/decorators/logout.d.ts | 2 + src/decorators/logout.js | 8 + src/errors.d.ts | 5 + src/errors.js | 12 + src/index.d.ts | 6 + src/index.js | 12 + .../SecureSessionManager.d.ts | 18 ++ src/session-managers/SecureSessionManager.js | 59 +++++ src/strategies/SessionStrategy.d.ts | 11 + src/strategies/SessionStrategy.js | 45 ++++ src/strategies/base.d.ts | 11 + src/strategies/base.js | 12 + src/strategies/index.d.ts | 5 + src/strategies/index.js | 18 ++ src/test/esm/default-esm-export.mjs | 3 - src/test/esm/named-esm-export.mjs | 3 - src/type-extensions.d.ts | 23 ++ src/type-extensions.js | 2 + {src/test => test}/authorize.test.ts | 2 +- {src/test => test}/csrf-fixation.test.ts | 0 {src/test => test}/decorators.test.ts | 0 test/esm/default-esm-export.mjs | 3 + {src/test => test}/esm/esm.test.ts | 4 +- test/esm/named-esm-export.mjs | 3 + {src/test => test}/helpers.ts | 6 +- .../independent-strategy-instances.test.ts | 2 +- {src/test => test}/multi-instance.test.ts | 4 +- {src/test => test}/passport.test.ts | 6 +- .../secure-session-manager.test.ts | 4 +- {src/test => test}/secure.key | 0 {src/test => test}/session-isolation.test.ts | 0 .../session-serialization.test.ts | 2 +- {src/test => test}/session-strategy.test.ts | 4 +- .../strategies-integration.test.ts | 0 {src/test => test}/strategy.test.ts | 4 +- tsconfig.json | 4 +- 51 files changed, 815 insertions(+), 30 deletions(-) create mode 100644 src/AuthenticationRoute.d.ts create mode 100644 src/AuthenticationRoute.js create mode 100644 src/Authenticator.d.ts create mode 100644 src/Authenticator.js create mode 100644 src/CreateInitializePlugin.d.ts create mode 100644 src/CreateInitializePlugin.js create mode 100644 src/decorators/index.d.ts create mode 100644 src/decorators/index.js create mode 100644 src/decorators/is-authenticated.d.ts create mode 100644 src/decorators/is-authenticated.js create mode 100644 src/decorators/is-unauthenticated.d.ts create mode 100644 src/decorators/is-unauthenticated.js create mode 100644 src/decorators/login.d.ts create mode 100644 src/decorators/login.js create mode 100644 src/decorators/logout.d.ts create mode 100644 src/decorators/logout.js create mode 100644 src/errors.d.ts create mode 100644 src/errors.js create mode 100644 src/index.d.ts create mode 100644 src/index.js create mode 100644 src/session-managers/SecureSessionManager.d.ts create mode 100644 src/session-managers/SecureSessionManager.js create mode 100644 src/strategies/SessionStrategy.d.ts create mode 100644 src/strategies/SessionStrategy.js create mode 100644 src/strategies/base.d.ts create mode 100644 src/strategies/base.js create mode 100644 src/strategies/index.d.ts create mode 100644 src/strategies/index.js delete mode 100644 src/test/esm/default-esm-export.mjs delete mode 100644 src/test/esm/named-esm-export.mjs create mode 100644 src/type-extensions.d.ts create mode 100644 src/type-extensions.js rename {src/test => test}/authorize.test.ts (96%) rename {src/test => test}/csrf-fixation.test.ts (100%) rename {src/test => test}/decorators.test.ts (100%) create mode 100644 test/esm/default-esm-export.mjs rename {src/test => test}/esm/esm.test.ts (84%) create mode 100644 test/esm/named-esm-export.mjs rename {src/test => test}/helpers.ts (95%) rename {src/test => test}/independent-strategy-instances.test.ts (99%) rename {src/test => test}/multi-instance.test.ts (99%) rename {src/test => test}/passport.test.ts (99%) rename {src/test => test}/secure-session-manager.test.ts (97%) rename {src/test => test}/secure.key (100%) rename {src/test => test}/session-isolation.test.ts (100%) rename {src/test => test}/session-serialization.test.ts (99%) rename {src/test => test}/session-strategy.test.ts (94%) rename {src/test => test}/strategies-integration.test.ts (100%) rename {src/test => test}/strategy.test.ts (97%) diff --git a/package.json b/package.json index 24ac5ef7..89935b43 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,11 @@ "name": "@fastify/passport", "version": "2.5.0", "description": "Simple, unobtrusive authentication for Fastify.", - "main": "dist/index.js", + "main": "dist/src/index.js", "type": "commonjs", - "types": "dist/index.d.ts", + "types": "dist/src/index.d.ts", "scripts": { - "build": "rimraf ./dist && mkdir dist && tsc --outDir dist && git rev-parse HEAD > BUILD_SHA", + "build": "rimraf ./dist && tsc && git rev-parse HEAD > BUILD_SHA", "lint": "eslint \"*/**/*.{js,ts,tsx}\"", "lint:fix": "prettier --loglevel warn --write \"src/**/*.{ts,tsx}\" && eslint \"*/**/*.{js,ts,tsx}\" --quiet --fix", "prepublishOnly": "npm run build", diff --git a/src/AuthenticationRoute.d.ts b/src/AuthenticationRoute.d.ts new file mode 100644 index 00000000..d52745ee --- /dev/null +++ b/src/AuthenticationRoute.d.ts @@ -0,0 +1,48 @@ +import Authenticator from './Authenticator'; +import { AnyStrategy, Strategy } from './strategies'; +import { FastifyReply, FastifyRequest } from 'fastify'; +type FlashObject = { + type?: string; + message?: string; +}; +type FailureObject = { + challenge?: string | FlashObject; + status?: number; + type?: string; +}; +export interface AuthenticateOptions { + scope?: string | string[]; + failureFlash?: boolean | string | FlashObject; + failureMessage?: boolean | string; + successRedirect?: string; + failureRedirect?: string; + failWithError?: boolean; + successFlash?: boolean | string | FlashObject; + successMessage?: boolean | string; + assignProperty?: string; + successReturnToOrRedirect?: string; + state?: string; + authInfo?: boolean; + session?: boolean; + pauseStream?: boolean; + keepSessionInfo?: boolean; +} +export type SingleStrategyCallback = (request: FastifyRequest, reply: FastifyReply, err: null | Error, user?: unknown, info?: unknown, status?: number) => Promise; +export type MultiStrategyCallback = (request: FastifyRequest, reply: FastifyReply, err: null | Error, user?: unknown, info?: unknown, statuses?: (number | undefined)[]) => Promise; +export type AuthenticateCallback = StrategyOrStrategies extends any[] ? MultiStrategyCallback : SingleStrategyCallback; +export declare class AuthenticationRoute { + readonly authenticator: Authenticator; + readonly callback?: AuthenticateCallback | undefined; + readonly options: AuthenticateOptions; + readonly strategies: (string | Strategy)[]; + readonly isMultiStrategy: boolean; + constructor(authenticator: Authenticator, strategyOrStrategies: StrategyOrStrategies, options?: AuthenticateOptions, callback?: AuthenticateCallback | undefined); + handler: (request: FastifyRequest, reply: FastifyReply) => Promise; + attemptStrategy(failures: FailureObject[], name: string, prototype: AnyStrategy, request: FastifyRequest, reply: FastifyReply): Promise; + onAllFailed(failures: FailureObject[], request: FastifyRequest, reply: FastifyReply): Promise; + applyFlashOrMessage(event: 'success' | 'failure', request: FastifyRequest, result?: FlashObject): void; + toFlashObject(input: string | FlashObject | undefined, type: string): FlashObject | undefined; + private getStrategyName; + private getStrategy; +} +export {}; diff --git a/src/AuthenticationRoute.js b/src/AuthenticationRoute.js new file mode 100644 index 00000000..3c9b9c6b --- /dev/null +++ b/src/AuthenticationRoute.js @@ -0,0 +1,230 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuthenticationRoute = void 0; +const http = require("http"); +const errors_1 = require("./errors"); +const util_1 = require("util"); +const addMessage = (request, message) => { + const existing = request.session.get('messages'); + const messages = existing ? [...existing, message] : [message]; + request.session.set('messages', messages); +}; +const Unhandled = Symbol.for('passport-unhandled'); +class AuthenticationRoute { + constructor(authenticator, strategyOrStrategies, options, callback) { + this.authenticator = authenticator; + this.callback = callback; + this.handler = async (request, reply) => { + if (!request.passport) { + throw new Error('passport.initialize() plugin not in use'); + } + const failures = []; + for (const nameOrInstance of this.strategies) { + try { + return await this.attemptStrategy(failures, this.getStrategyName(nameOrInstance), this.getStrategy(nameOrInstance), request, reply); + } + catch (e) { + if (e == Unhandled) { + continue; + } + else { + throw e; + } + } + } + return this.onAllFailed(failures, request, reply); + }; + this.options = options || {}; + if (Array.isArray(strategyOrStrategies)) { + this.strategies = strategyOrStrategies; + this.isMultiStrategy = false; + } + else { + this.strategies = [strategyOrStrategies]; + this.isMultiStrategy = false; + } + } + attemptStrategy(failures, name, prototype, request, reply) { + const strategy = Object.create(prototype); + return new Promise((resolve, reject) => { + strategy.success = (user, info) => { + request.log.debug({ strategy: name }, 'passport strategy success'); + if (this.callback) { + return resolve(this.callback(request, reply, null, user, info)); + } + info = info || {}; + this.applyFlashOrMessage('success', request, info); + if (this.options.assignProperty) { + request[this.options.assignProperty] = user; + return resolve(); + } + void request + .logIn(user, this.options) + .catch(reject) + .then(() => { + const complete = () => { + if (this.options.successReturnToOrRedirect) { + let url = this.options.successReturnToOrRedirect; + if (request.session && request.session.get('returnTo')) { + url = request.session.get('returnTo'); + request.session.set('returnTo', undefined); + } + void reply.redirect(url); + } + else if (this.options.successRedirect) { + void reply.redirect(this.options.successRedirect); + } + return resolve(); + }; + if (this.options.authInfo !== false) { + void this.authenticator + .transformAuthInfo(info, request) + .catch(reject) + .then((transformedInfo) => { + request.authInfo = transformedInfo; + complete(); + }); + } + else { + complete(); + } + }); + }; + strategy.fail = function (challengeOrStatus, status) { + request.log.trace({ strategy: name }, 'passport strategy failed'); + let challenge; + if (typeof challengeOrStatus === 'number') { + status = challengeOrStatus; + challenge = undefined; + } + else { + challenge = challengeOrStatus; + } + failures.push({ challenge, status: status }); + reject(Unhandled); + }; + strategy.redirect = (url, status) => { + request.log.trace({ strategy: name, url }, 'passport strategy redirecting'); + void reply.status(status || 302); + void reply.redirect(url); + resolve(); + }; + strategy.pass = () => { + request.log.trace({ strategy: name }, 'passport strategy passed'); + resolve(); + }; + const error = (err) => { + request.log.trace({ strategy: name, err }, 'passport strategy errored'); + if (this.callback) { + return resolve(this.callback(request, reply, err)); + } + reject(err); + }; + strategy.error = error; + request.log.trace({ strategy: name }, 'attempting passport strategy authentication'); + try { + const result = strategy.authenticate(request, this.options); + if (util_1.types.isPromise(result)) { + void result.catch(error); + } + } + catch (err) { + error(err); + } + }); + } + async onAllFailed(failures, request, reply) { + var _a; + request.log.trace('all passport strategies failed'); + if (this.callback) { + if (this.isMultiStrategy) { + const challenges = failures.map((f) => f.challenge); + const statuses = failures.map((f) => f.status); + return await this.callback(request, reply, null, false, challenges, statuses); + } + else { + return await this.callback(request, reply, null, false, failures[0].challenge, failures[0].status); + } + } + this.applyFlashOrMessage('failure', request, this.toFlashObject((_a = failures[0]) === null || _a === void 0 ? void 0 : _a.challenge, 'error')); + if (this.options.failureRedirect) { + return reply.redirect(this.options.failureRedirect); + } + const rchallenge = []; + let rstatus; + for (const failure of failures) { + rstatus = rstatus || failure.status; + if (typeof failure.challenge === 'string') { + rchallenge.push(failure.challenge); + } + } + rstatus = rstatus || 401; + void reply.code(rstatus); + if (reply.statusCode === 401 && rchallenge.length) { + void reply.header('WWW-Authenticate', rchallenge); + } + if (this.options.failWithError) { + throw new errors_1.default(http.STATUS_CODES[reply.statusCode], rstatus); + } + void reply.send(http.STATUS_CODES[reply.statusCode]); + } + applyFlashOrMessage(event, request, result) { + var _a; + const flashOption = this.options[`${event}Flash`]; + const level = event == 'success' ? 'success' : 'error'; + if (flashOption) { + let flash; + if (typeof flashOption === 'boolean') { + flash = this.toFlashObject(result, level); + } + else { + flash = this.toFlashObject(flashOption, level); + } + if (flash && flash.type && flash.message) { + request.flash(flash.type, flash.message); + } + } + const messageOption = this.options[`${event}Message`]; + if (messageOption) { + const message = typeof messageOption === 'boolean' ? (_a = this.toFlashObject(result, level)) === null || _a === void 0 ? void 0 : _a.message : messageOption; + if (message) { + addMessage(request, message); + } + } + } + toFlashObject(input, type) { + if (input === undefined) { + return; + } + else if (typeof input == 'string') { + return { type, message: input }; + } + else { + return input; + } + } + getStrategyName(nameOrInstance) { + if (typeof nameOrInstance === 'string') { + return nameOrInstance; + } + else if (nameOrInstance.name) { + return nameOrInstance.name; + } + else { + return nameOrInstance.constructor.name; + } + } + getStrategy(nameOrInstance) { + if (typeof nameOrInstance === 'string') { + const prototype = this.authenticator.strategy(nameOrInstance); + if (!prototype) { + throw new Error(`Unknown authentication strategy ${nameOrInstance}, no strategy with this name has been registered.`); + } + return prototype; + } + else { + return nameOrInstance; + } + } +} +exports.AuthenticationRoute = AuthenticationRoute; diff --git a/src/Authenticator.d.ts b/src/Authenticator.d.ts new file mode 100644 index 00000000..93927854 --- /dev/null +++ b/src/Authenticator.d.ts @@ -0,0 +1,45 @@ +import { FastifyPluginAsync, FastifyRequest, RouteHandlerMethod } from 'fastify'; +import { AuthenticateCallback, AuthenticateOptions } from './AuthenticationRoute'; +import { SecureSessionManager } from './session-managers/SecureSessionManager'; +import { AnyStrategy, Strategy } from './strategies'; +export type SerializeFunction = (user: User, req: FastifyRequest) => Promise; +export type DeserializeFunction = (serialized: SerializedUser, req: FastifyRequest) => Promise; +export type InfoTransformerFunction = (info: any) => Promise; +export interface AuthenticatorOptions { + key?: string; + userProperty?: string; + clearSessionOnLogin?: boolean; + clearSessionIgnoreFields?: string[]; +} +export declare class Authenticator { + key: string; + userProperty: string; + sessionManager: SecureSessionManager; + private strategies; + private serializers; + private deserializers; + private infoTransformers; + private clearSessionOnLogin; + private clearSessionIgnoreFields; + constructor(options?: AuthenticatorOptions); + use(strategy: AnyStrategy): this; + use(name: string, strategy: AnyStrategy): this; + unuse(name: string): this; + initialize(): FastifyPluginAsync; + authenticate(strategy: StrategyOrStrategies, callback?: AuthenticateCallback): RouteHandlerMethod; + authenticate(strategy: StrategyOrStrategies, options?: AuthenticateOptions): RouteHandlerMethod; + authenticate(strategy: StrategyOrStrategies, options?: AuthenticateOptions, callback?: AuthenticateCallback): RouteHandlerMethod; + authorize(strategy: StrategyOrStrategies, callback?: AuthenticateCallback): RouteHandlerMethod; + authorize(strategy: StrategyOrStrategies, options?: AuthenticateOptions): RouteHandlerMethod; + authorize(strategy: StrategyOrStrategies, options?: AuthenticateOptions, callback?: AuthenticateCallback): RouteHandlerMethod; + secureSession(options?: AuthenticateOptions): FastifyPluginAsync; + registerUserSerializer(fn: SerializeFunction): void; + serializeUser(user: User, request: FastifyRequest): Promise; + registerUserDeserializer(fn: DeserializeFunction): void; + deserializeUser(stored: StoredUser, request: FastifyRequest): Promise; + registerAuthInfoTransformer(fn: InfoTransformerFunction): void; + transformAuthInfo(info: any, request: FastifyRequest): Promise; + strategy(name: string): AnyStrategy | undefined; + private runStack; +} +export default Authenticator; diff --git a/src/Authenticator.js b/src/Authenticator.js new file mode 100644 index 00000000..aee5af0a --- /dev/null +++ b/src/Authenticator.js @@ -0,0 +1,128 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Authenticator = void 0; +const fastify_plugin_1 = require("fastify-plugin"); +const AuthenticationRoute_1 = require("./AuthenticationRoute"); +const CreateInitializePlugin_1 = require("./CreateInitializePlugin"); +const SecureSessionManager_1 = require("./session-managers/SecureSessionManager"); +const strategies_1 = require("./strategies"); +class Authenticator { + constructor(options = {}) { + var _a; + this.strategies = {}; + this.serializers = []; + this.deserializers = []; + this.infoTransformers = []; + this.key = options.key || 'passport'; + this.userProperty = options.userProperty || 'user'; + this.use(new strategies_1.SessionStrategy(this.deserializeUser.bind(this))); + this.clearSessionOnLogin = (_a = options.clearSessionOnLogin) !== null && _a !== void 0 ? _a : true; + this.clearSessionIgnoreFields = ['passport', 'session', ...(options.clearSessionIgnoreFields || [])]; + this.sessionManager = new SecureSessionManager_1.SecureSessionManager({ + key: this.key, + clearSessionOnLogin: this.clearSessionOnLogin, + clearSessionIgnoreFields: this.clearSessionIgnoreFields + }, this.serializeUser.bind(this)); + } + use(name, strategy) { + if (!strategy) { + strategy = name; + name = strategy.name; + } + if (!name) { + throw new Error('Authentication strategies must have a name'); + } + this.strategies[name] = strategy; + return this; + } + unuse(name) { + delete this.strategies[name]; + return this; + } + initialize() { + return (0, CreateInitializePlugin_1.CreateInitializePlugin)(this); + } + authenticate(strategyOrStrategies, optionsOrCallback, callback) { + let options; + if (typeof optionsOrCallback == 'function') { + options = {}; + callback = optionsOrCallback; + } + else { + options = optionsOrCallback; + } + return new AuthenticationRoute_1.AuthenticationRoute(this, strategyOrStrategies, options, callback).handler; + } + authorize(strategyOrStrategies, optionsOrCallback, callback) { + let options; + if (typeof optionsOrCallback == 'function') { + options = {}; + callback = optionsOrCallback; + } + else { + options = optionsOrCallback; + } + options || (options = {}); + options.assignProperty = 'account'; + return new AuthenticationRoute_1.AuthenticationRoute(this, strategyOrStrategies, options, callback).handler; + } + secureSession(options) { + return (0, fastify_plugin_1.default)(async (fastify) => { + fastify.addHook('preValidation', new AuthenticationRoute_1.AuthenticationRoute(this, 'session', options).handler); + }); + } + registerUserSerializer(fn) { + this.serializers.push(fn); + } + async serializeUser(user, request) { + const result = await this.runStack(this.serializers, user, request); + if (result) { + return result; + } + else { + throw new Error(`Failed to serialize user into session. Tried ${this.serializers.length} serializers.`); + } + } + registerUserDeserializer(fn) { + this.deserializers.push(fn); + } + async deserializeUser(stored, request) { + const result = await this.runStack(this.deserializers, stored, request); + if (result) { + return result; + } + else if (result === null || result === false) { + return false; + } + else { + throw new Error(`Failed to deserialize user out of session. Tried ${this.deserializers.length} serializers.`); + } + } + registerAuthInfoTransformer(fn) { + this.infoTransformers.push(fn); + } + async transformAuthInfo(info, request) { + const result = await this.runStack(this.infoTransformers, info, request); + return result || info; + } + strategy(name) { + return this.strategies[name]; + } + async runStack(stack, ...args) { + for (const attempt of stack) { + try { + return await attempt(...args); + } + catch (e) { + if (e == 'pass') { + continue; + } + else { + throw e; + } + } + } + } +} +exports.Authenticator = Authenticator; +exports.default = Authenticator; diff --git a/src/CreateInitializePlugin.d.ts b/src/CreateInitializePlugin.d.ts new file mode 100644 index 00000000..a989d22c --- /dev/null +++ b/src/CreateInitializePlugin.d.ts @@ -0,0 +1,2 @@ +import Authenticator from './Authenticator'; +export declare function CreateInitializePlugin(passport: Authenticator): (fastify: import("fastify").FastifyInstance, import("fastify").FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>) => Promise; diff --git a/src/CreateInitializePlugin.js b/src/CreateInitializePlugin.js new file mode 100644 index 00000000..2a5320d4 --- /dev/null +++ b/src/CreateInitializePlugin.js @@ -0,0 +1,23 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CreateInitializePlugin = CreateInitializePlugin; +const fastify_plugin_1 = require("fastify-plugin"); +const decorators_1 = require("./decorators"); +const flash = require("@fastify/flash"); +function CreateInitializePlugin(passport) { + return (0, fastify_plugin_1.default)(async (fastify) => { + void fastify.register(flash); + fastify.decorateRequest('passport', { + getter() { + return passport; + } + }); + fastify.decorateRequest('logIn', decorators_1.logIn); + fastify.decorateRequest('login', decorators_1.logIn); + fastify.decorateRequest('logOut', decorators_1.logOut); + fastify.decorateRequest('logout', decorators_1.logOut); + fastify.decorateRequest('isAuthenticated', decorators_1.isAuthenticated); + fastify.decorateRequest('isUnauthenticated', decorators_1.isUnauthenticated); + fastify.decorateRequest(passport.userProperty, null); + }); +} diff --git a/src/decorators/index.d.ts b/src/decorators/index.d.ts new file mode 100644 index 00000000..33e0537d --- /dev/null +++ b/src/decorators/index.d.ts @@ -0,0 +1,5 @@ +import { logIn } from './login'; +import { logOut } from './logout'; +import { isAuthenticated } from './is-authenticated'; +import { isUnauthenticated } from './is-unauthenticated'; +export { logIn, logOut, isAuthenticated, isUnauthenticated }; diff --git a/src/decorators/index.js b/src/decorators/index.js new file mode 100644 index 00000000..82e36007 --- /dev/null +++ b/src/decorators/index.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isUnauthenticated = exports.isAuthenticated = exports.logOut = exports.logIn = void 0; +const login_1 = require("./login"); +Object.defineProperty(exports, "logIn", { enumerable: true, get: function () { return login_1.logIn; } }); +const logout_1 = require("./logout"); +Object.defineProperty(exports, "logOut", { enumerable: true, get: function () { return logout_1.logOut; } }); +const is_authenticated_1 = require("./is-authenticated"); +Object.defineProperty(exports, "isAuthenticated", { enumerable: true, get: function () { return is_authenticated_1.isAuthenticated; } }); +const is_unauthenticated_1 = require("./is-unauthenticated"); +Object.defineProperty(exports, "isUnauthenticated", { enumerable: true, get: function () { return is_unauthenticated_1.isUnauthenticated; } }); diff --git a/src/decorators/is-authenticated.d.ts b/src/decorators/is-authenticated.d.ts new file mode 100644 index 00000000..4b5b28c6 --- /dev/null +++ b/src/decorators/is-authenticated.d.ts @@ -0,0 +1,2 @@ +import { FastifyRequest } from 'fastify'; +export declare function isAuthenticated(this: FastifyRequest): boolean; diff --git a/src/decorators/is-authenticated.js b/src/decorators/is-authenticated.js new file mode 100644 index 00000000..dde3f9aa --- /dev/null +++ b/src/decorators/is-authenticated.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isAuthenticated = isAuthenticated; +function isAuthenticated() { + const property = this.passport.userProperty; + return this[property] ? true : false; +} diff --git a/src/decorators/is-unauthenticated.d.ts b/src/decorators/is-unauthenticated.d.ts new file mode 100644 index 00000000..02544141 --- /dev/null +++ b/src/decorators/is-unauthenticated.d.ts @@ -0,0 +1,2 @@ +import { FastifyRequest } from 'fastify'; +export declare function isUnauthenticated(this: FastifyRequest): boolean; diff --git a/src/decorators/is-unauthenticated.js b/src/decorators/is-unauthenticated.js new file mode 100644 index 00000000..9e265065 --- /dev/null +++ b/src/decorators/is-unauthenticated.js @@ -0,0 +1,6 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isUnauthenticated = isUnauthenticated; +function isUnauthenticated() { + return !this.isAuthenticated(); +} diff --git a/src/decorators/login.d.ts b/src/decorators/login.d.ts new file mode 100644 index 00000000..2feb45c9 --- /dev/null +++ b/src/decorators/login.d.ts @@ -0,0 +1,7 @@ +import { FastifyRequest } from 'fastify'; +export type DoneCallback = (err?: Error) => void; +export declare function logIn(this: FastifyRequest, user: T): Promise; +export declare function logIn(this: FastifyRequest, user: T, options: { + session?: boolean; + keepSessionInfo?: boolean; +}): Promise; diff --git a/src/decorators/login.js b/src/decorators/login.js new file mode 100644 index 00000000..12661618 --- /dev/null +++ b/src/decorators/login.js @@ -0,0 +1,20 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.logIn = logIn; +async function logIn(user, options = {}) { + if (!this.passport) { + throw new Error('passport.initialize() plugin not in use'); + } + const property = this.passport.userProperty; + const session = options.session === undefined ? true : options.session; + this[property] = user; + if (session) { + try { + await this.passport.sessionManager.logIn(this, user, options); + } + catch (e) { + this[property] = null; + throw e; + } + } +} diff --git a/src/decorators/logout.d.ts b/src/decorators/logout.d.ts new file mode 100644 index 00000000..6c6887fb --- /dev/null +++ b/src/decorators/logout.d.ts @@ -0,0 +1,2 @@ +import { FastifyRequest } from 'fastify'; +export declare function logOut(this: FastifyRequest): Promise; diff --git a/src/decorators/logout.js b/src/decorators/logout.js new file mode 100644 index 00000000..5f6aff97 --- /dev/null +++ b/src/decorators/logout.js @@ -0,0 +1,8 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.logOut = logOut; +async function logOut() { + const property = this.passport.userProperty; + this[property] = null; + await this.passport.sessionManager.logOut(this); +} diff --git a/src/errors.d.ts b/src/errors.d.ts new file mode 100644 index 00000000..be228dfe --- /dev/null +++ b/src/errors.d.ts @@ -0,0 +1,5 @@ +declare class AuthenticationError extends Error { + status: number; + constructor(message: string, status: number); +} +export default AuthenticationError; diff --git a/src/errors.js b/src/errors.js new file mode 100644 index 00000000..a6be021d --- /dev/null +++ b/src/errors.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +class AuthenticationError extends Error { + constructor(message, status) { + super(); + Error.captureStackTrace(this, this.constructor); + this.name = 'AuthenticationError'; + this.message = message; + this.status = status || 401; + } +} +exports.default = AuthenticationError; diff --git a/src/index.d.ts b/src/index.d.ts new file mode 100644 index 00000000..a9011531 --- /dev/null +++ b/src/index.d.ts @@ -0,0 +1,6 @@ +import { Authenticator } from './Authenticator'; +import './type-extensions'; +declare const passport: Authenticator; +export default passport; +export { Strategy } from './strategies'; +export { Authenticator } from './Authenticator'; diff --git a/src/index.js b/src/index.js new file mode 100644 index 00000000..0485dc4e --- /dev/null +++ b/src/index.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Authenticator = exports.Strategy = void 0; +const Authenticator_1 = require("./Authenticator"); +require("./type-extensions"); +const passport = new Authenticator_1.Authenticator(); +module.exports = exports = passport; +exports.default = passport; +var strategies_1 = require("./strategies"); +Object.defineProperty(exports, "Strategy", { enumerable: true, get: function () { return strategies_1.Strategy; } }); +var Authenticator_2 = require("./Authenticator"); +Object.defineProperty(exports, "Authenticator", { enumerable: true, get: function () { return Authenticator_2.Authenticator; } }); diff --git a/src/session-managers/SecureSessionManager.d.ts b/src/session-managers/SecureSessionManager.d.ts new file mode 100644 index 00000000..e01bc8af --- /dev/null +++ b/src/session-managers/SecureSessionManager.d.ts @@ -0,0 +1,18 @@ +import { FastifyRequest } from 'fastify'; +import { AuthenticateOptions } from '../AuthenticationRoute'; +import { SerializeFunction } from '../Authenticator'; +export declare class SecureSessionManager { + key: string; + clearSessionOnLogin: boolean; + clearSessionIgnoreFields: string[]; + serializeUser: SerializeFunction; + constructor(serializeUser: SerializeFunction); + constructor(options: { + key?: string; + clearSessionOnLogin?: boolean; + clearSessionIgnoreFields?: string[]; + }, serializeUser: SerializeFunction); + logIn(request: FastifyRequest, user: any, options?: AuthenticateOptions): Promise; + logOut(request: FastifyRequest): Promise; + getUserFromSession(request: FastifyRequest): any; +} diff --git a/src/session-managers/SecureSessionManager.js b/src/session-managers/SecureSessionManager.js new file mode 100644 index 00000000..a069c34c --- /dev/null +++ b/src/session-managers/SecureSessionManager.js @@ -0,0 +1,59 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SecureSessionManager = void 0; +class SecureSessionManager { + constructor(options, serializeUser) { + var _a; + this.clearSessionIgnoreFields = ['session']; + if (typeof options === 'function') { + this.serializeUser = options; + this.key = 'passport'; + this.clearSessionOnLogin = true; + } + else if (typeof serializeUser === 'function') { + this.serializeUser = serializeUser; + this.key = + (options && typeof options === 'object' && typeof options.key === 'string' && options.key) || 'passport'; + this.clearSessionOnLogin = (_a = options.clearSessionOnLogin) !== null && _a !== void 0 ? _a : true; + this.clearSessionIgnoreFields = [...this.clearSessionIgnoreFields, ...(options.clearSessionIgnoreFields || [])]; + } + else { + throw new Error('SecureSessionManager#constructor must have a valid serializeUser-function passed as a parameter'); + } + } + async logIn(request, user, options) { + const object = await this.serializeUser(user, request); + if (request.session.regenerate) { + if (this.clearSessionOnLogin && object) { + const keepSessionInfoKeys = [...this.clearSessionIgnoreFields]; + if (options === null || options === void 0 ? void 0 : options.keepSessionInfo) { + keepSessionInfoKeys.push(...Object.keys(request.session)); + } + await request.session.regenerate(keepSessionInfoKeys); + } + else { + await request.session.regenerate(); + } + } + else if (this.clearSessionOnLogin && object) { + const currentFields = request.session.data() || {}; + for (const field of Object.keys(currentFields)) { + if ((options === null || options === void 0 ? void 0 : options.keepSessionInfo) || this.clearSessionIgnoreFields.includes(field)) { + continue; + } + request.session.set(field, undefined); + } + } + request.session.set(this.key, object); + } + async logOut(request) { + request.session.set(this.key, undefined); + if (request.session.regenerate) { + await request.session.regenerate(); + } + } + getUserFromSession(request) { + return request.session.get(this.key); + } +} +exports.SecureSessionManager = SecureSessionManager; diff --git a/src/strategies/SessionStrategy.d.ts b/src/strategies/SessionStrategy.d.ts new file mode 100644 index 00000000..effb84fe --- /dev/null +++ b/src/strategies/SessionStrategy.d.ts @@ -0,0 +1,11 @@ +import { Strategy } from './base'; +import { DeserializeFunction } from '../Authenticator'; +import type { FastifyRequest } from 'fastify'; +export declare class SessionStrategy extends Strategy { + private deserializeUser; + constructor(deserializeUser: DeserializeFunction); + constructor(options: any, deserializeUser: DeserializeFunction); + authenticate(request: FastifyRequest, options?: { + pauseStream?: boolean; + }): void; +} diff --git a/src/strategies/SessionStrategy.js b/src/strategies/SessionStrategy.js new file mode 100644 index 00000000..72223774 --- /dev/null +++ b/src/strategies/SessionStrategy.js @@ -0,0 +1,45 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SessionStrategy = void 0; +const base_1 = require("./base"); +class SessionStrategy extends base_1.Strategy { + constructor(options, deserializeUser) { + super('session'); + if (typeof options === 'function') { + this.deserializeUser = options; + } + else if (typeof deserializeUser === 'function') { + this.deserializeUser = deserializeUser; + } + else { + throw new Error('SessionStrategy#constructor must have a valid deserializeUser-function passed as a parameter'); + } + } + authenticate(request, options) { + if (!request.passport) { + return this.error(new Error('passport.initialize() plugin not in use')); + } + options = options || {}; + if (options.pauseStream) { + return this.error(new Error("fastify-passport doesn't support pauseStream option.")); + } + const sessionUser = request.passport.sessionManager.getUserFromSession(request); + if (sessionUser || sessionUser === 0) { + void this.deserializeUser(sessionUser, request) + .catch((err) => this.error(err)) + .then(async (user) => { + if (!user) { + await request.passport.sessionManager.logOut(request); + } + else { + request[request.passport.userProperty] = user; + } + this.pass(); + }); + } + else { + this.pass(); + } + } +} +exports.SessionStrategy = SessionStrategy; diff --git a/src/strategies/base.d.ts b/src/strategies/base.d.ts new file mode 100644 index 00000000..f685fdb0 --- /dev/null +++ b/src/strategies/base.d.ts @@ -0,0 +1,11 @@ +import { FastifyRequest } from 'fastify'; +export declare class Strategy { + name: string; + constructor(name: string); + authenticate(request: FastifyRequest, options?: any): void | Promise; + success: (user: any, info?: any) => void; + fail: ((challenge?: any, status?: number) => void) & ((status?: number) => void); + redirect: (url: string, status?: number) => void; + pass: () => void; + error: (err: Error) => void; +} diff --git a/src/strategies/base.js b/src/strategies/base.js new file mode 100644 index 00000000..50edb3da --- /dev/null +++ b/src/strategies/base.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Strategy = void 0; +class Strategy { + constructor(name) { + this.name = name; + } + authenticate() { + throw new Error('Strategy#authenticate must be overridden by subclass'); + } +} +exports.Strategy = Strategy; diff --git a/src/strategies/index.d.ts b/src/strategies/index.d.ts new file mode 100644 index 00000000..ec3ba69c --- /dev/null +++ b/src/strategies/index.d.ts @@ -0,0 +1,5 @@ +import type { Strategy as ExpressStrategy } from 'passport'; +import type { Strategy } from './base'; +export * from './base'; +export * from './SessionStrategy'; +export type AnyStrategy = Strategy | ExpressStrategy; diff --git a/src/strategies/index.js b/src/strategies/index.js new file mode 100644 index 00000000..c49ee785 --- /dev/null +++ b/src/strategies/index.js @@ -0,0 +1,18 @@ +"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 __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./base"), exports); +__exportStar(require("./SessionStrategy"), exports); diff --git a/src/test/esm/default-esm-export.mjs b/src/test/esm/default-esm-export.mjs deleted file mode 100644 index 699ec09e..00000000 --- a/src/test/esm/default-esm-export.mjs +++ /dev/null @@ -1,3 +0,0 @@ -import passport from '../../../dist/index.js' - -passport.initialize() diff --git a/src/test/esm/named-esm-export.mjs b/src/test/esm/named-esm-export.mjs deleted file mode 100644 index 7bacd302..00000000 --- a/src/test/esm/named-esm-export.mjs +++ /dev/null @@ -1,3 +0,0 @@ -import { Strategy } from '../../../dist/index.js' - -class MS extends Strategy {} diff --git a/src/type-extensions.d.ts b/src/type-extensions.d.ts new file mode 100644 index 00000000..2ee233c9 --- /dev/null +++ b/src/type-extensions.d.ts @@ -0,0 +1,23 @@ +import { flashFactory } from '@fastify/flash/lib/flash'; +import { logIn, logOut, isAuthenticated, isUnauthenticated } from './decorators'; +import Authenticator from './Authenticator'; +declare module 'fastify' { + interface PassportUser { + } + interface FastifyRequest { + flash: ReturnType['request']; + login: typeof logIn; + logIn: typeof logIn; + logout: typeof logOut; + logOut: typeof logOut; + isAuthenticated: typeof isAuthenticated; + isUnauthenticated: typeof isUnauthenticated; + passport: Authenticator; + user?: PassportUser; + authInfo?: Record; + account?: PassportUser; + } + interface FastifyReply { + flash: ReturnType['reply']; + } +} diff --git a/src/type-extensions.js b/src/type-extensions.js new file mode 100644 index 00000000..c8ad2e54 --- /dev/null +++ b/src/type-extensions.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/src/test/authorize.test.ts b/test/authorize.test.ts similarity index 96% rename from src/test/authorize.test.ts rename to test/authorize.test.ts index c8d39dbe..38356a67 100644 --- a/src/test/authorize.test.ts +++ b/test/authorize.test.ts @@ -2,7 +2,7 @@ import { test, describe } from 'node:test' import assert from 'node:assert' import { RouteHandlerMethod } from 'fastify' import { expectType } from 'tsd' -import { Strategy } from '../strategies' +import { Strategy } from '../src/strategies' import { generateTestUser, getConfiguredTestServer } from './helpers' export class TestThirdPartyStrategy extends Strategy { diff --git a/src/test/csrf-fixation.test.ts b/test/csrf-fixation.test.ts similarity index 100% rename from src/test/csrf-fixation.test.ts rename to test/csrf-fixation.test.ts diff --git a/src/test/decorators.test.ts b/test/decorators.test.ts similarity index 100% rename from src/test/decorators.test.ts rename to test/decorators.test.ts diff --git a/test/esm/default-esm-export.mjs b/test/esm/default-esm-export.mjs new file mode 100644 index 00000000..01d3e4a3 --- /dev/null +++ b/test/esm/default-esm-export.mjs @@ -0,0 +1,3 @@ +import passport from '../../dist/src/index.js' + +passport.initialize() diff --git a/src/test/esm/esm.test.ts b/test/esm/esm.test.ts similarity index 84% rename from src/test/esm/esm.test.ts rename to test/esm/esm.test.ts index d48a4808..b88d5ad2 100644 --- a/src/test/esm/esm.test.ts +++ b/test/esm/esm.test.ts @@ -5,12 +5,12 @@ import { join } from 'node:path' describe('Native ESM import', () => { test('should be able to use default export', () => { - const { status } = spawnSync('node', [join(__dirname, '../../../src/test/esm', 'default-esm-export.mjs')]) + const { status } = spawnSync('node', [join(__dirname, '../../../test/esm', 'default-esm-export.mjs')]) assert.strictEqual(status, 0) }) test('should be able to use named export', () => { - const { status } = spawnSync('node', [join(__dirname, '../../../src/test/esm', 'named-esm-export.mjs')]) + const { status } = spawnSync('node', [join(__dirname, '../../../test/esm', 'named-esm-export.mjs')]) assert.strictEqual(status, 0) }) diff --git a/test/esm/named-esm-export.mjs b/test/esm/named-esm-export.mjs new file mode 100644 index 00000000..f0171bd6 --- /dev/null +++ b/test/esm/named-esm-export.mjs @@ -0,0 +1,3 @@ +import { Strategy } from '../../dist/src/index.js' + +class MS extends Strategy {} diff --git a/src/test/helpers.ts b/test/helpers.ts similarity index 95% rename from src/test/helpers.ts rename to test/helpers.ts index 2b610f29..e13e2e64 100644 --- a/src/test/helpers.ts +++ b/test/helpers.ts @@ -3,15 +3,15 @@ import { join } from 'node:path' import fastify, { FastifyInstance } from 'fastify' import fastifySecureSession, { SecureSessionPluginOptions } from '@fastify/secure-session' import fastifyCookie from '@fastify/cookie' -import Authenticator, { AuthenticatorOptions } from '../Authenticator' -import { Strategy } from '../strategies' +import Authenticator, { AuthenticatorOptions } from '../src/Authenticator' +import { Strategy } from '../src/strategies' import { InjectOptions, Response as LightMyRequestResponse } from 'light-my-request' import parseCookies from 'set-cookie-parser' import { IncomingMessage } from 'node:http' import { FastifyRegisterOptions } from 'fastify/types/register' import { fastifySession, FastifySessionOptions } from '@fastify/session' -const SecretKey = fs.readFileSync(join(__dirname, '../../src/test', 'secure.key')) +const SecretKey = fs.readFileSync(join(__dirname, '../../test', 'secure.key')) let counter = 0 export const generateTestUser = () => ({ name: 'test', id: String(counter++) }) diff --git a/src/test/independent-strategy-instances.test.ts b/test/independent-strategy-instances.test.ts similarity index 99% rename from src/test/independent-strategy-instances.test.ts rename to test/independent-strategy-instances.test.ts index 7293915b..2fcddd6e 100644 --- a/src/test/independent-strategy-instances.test.ts +++ b/test/independent-strategy-instances.test.ts @@ -1,6 +1,6 @@ import { test, describe } from 'node:test' import assert from 'node:assert' -import { Strategy } from '../strategies' +import { Strategy } from '../src/strategies' import { TestThirdPartyStrategy } from './authorize.test' import { getConfiguredTestServer, getRegisteredTestServer, TestStrategy } from './helpers' diff --git a/src/test/multi-instance.test.ts b/test/multi-instance.test.ts similarity index 99% rename from src/test/multi-instance.test.ts rename to test/multi-instance.test.ts index 0b64eb8c..57a5e250 100644 --- a/src/test/multi-instance.test.ts +++ b/test/multi-instance.test.ts @@ -1,8 +1,8 @@ import { test, describe, beforeEach } from 'node:test' import assert from 'node:assert' import { FastifyInstance } from 'fastify' -import { Authenticator } from '../Authenticator' -import { Strategy } from '../strategies' +import { Authenticator } from '../src/Authenticator' +import { Strategy } from '../src/strategies' import { getTestServer, TestBrowserSession } from './helpers' let counter: number diff --git a/src/test/passport.test.ts b/test/passport.test.ts similarity index 99% rename from src/test/passport.test.ts rename to test/passport.test.ts index 8893202f..801514aa 100644 --- a/src/test/passport.test.ts +++ b/test/passport.test.ts @@ -2,9 +2,9 @@ import { test, describe } from 'node:test' import assert from 'node:assert' import got from 'got' import { AddressInfo } from 'net' -import { AuthenticateOptions } from '../AuthenticationRoute' -import Authenticator from '../Authenticator' -import { Strategy } from '../strategies' +import { AuthenticateOptions } from '../src/AuthenticationRoute' +import Authenticator from '../src/Authenticator' +import { Strategy } from '../src/strategies' import { getConfiguredTestServer, getRegisteredTestServer, getTestServer, TestStrategy } from './helpers' const testSuite = (sessionPluginName: string) => { diff --git a/src/test/secure-session-manager.test.ts b/test/secure-session-manager.test.ts similarity index 97% rename from src/test/secure-session-manager.test.ts rename to test/secure-session-manager.test.ts index eefeb0d8..2ae210ae 100644 --- a/src/test/secure-session-manager.test.ts +++ b/test/secure-session-manager.test.ts @@ -1,8 +1,8 @@ import { test, describe, mock } from 'node:test' import assert from 'node:assert' import { FastifyRequest } from 'fastify' -import { SerializeFunction } from '../Authenticator' -import { SecureSessionManager } from '../session-managers/SecureSessionManager' +import { SerializeFunction } from '../src/Authenticator' +import { SecureSessionManager } from '../src/session-managers/SecureSessionManager' describe('SecureSessionManager', () => { test('should throw an Error if no parameter was passed', () => { diff --git a/src/test/secure.key b/test/secure.key similarity index 100% rename from src/test/secure.key rename to test/secure.key diff --git a/src/test/session-isolation.test.ts b/test/session-isolation.test.ts similarity index 100% rename from src/test/session-isolation.test.ts rename to test/session-isolation.test.ts diff --git a/src/test/session-serialization.test.ts b/test/session-serialization.test.ts similarity index 99% rename from src/test/session-serialization.test.ts rename to test/session-serialization.test.ts index 7daab9d2..43316541 100644 --- a/src/test/session-serialization.test.ts +++ b/test/session-serialization.test.ts @@ -2,7 +2,7 @@ import { test, describe, mock } from 'node:test' import assert from 'node:assert' import { FastifyInstance } from 'fastify' import { FastifyRequest } from 'fastify/types/request' -import Authenticator from '../Authenticator' +import Authenticator from '../src/Authenticator' import { getTestServer, TestDatabaseStrategy, TestStrategy } from './helpers' const testSuite = (sessionPluginName: string) => { diff --git a/src/test/session-strategy.test.ts b/test/session-strategy.test.ts similarity index 94% rename from src/test/session-strategy.test.ts rename to test/session-strategy.test.ts index 5d6fd6b2..9ba53417 100644 --- a/src/test/session-strategy.test.ts +++ b/test/session-strategy.test.ts @@ -1,7 +1,7 @@ import { test, describe } from 'node:test' import assert from 'node:assert' -import { SerializeFunction } from '../Authenticator' -import { SessionStrategy } from '../strategies/SessionStrategy' +import { SerializeFunction } from '../src/Authenticator' +import { SessionStrategy } from '../src/strategies/SessionStrategy' describe('SessionStrategy', () => { test('should throw an Error if no parameter was passed', () => { diff --git a/src/test/strategies-integration.test.ts b/test/strategies-integration.test.ts similarity index 100% rename from src/test/strategies-integration.test.ts rename to test/strategies-integration.test.ts diff --git a/src/test/strategy.test.ts b/test/strategy.test.ts similarity index 97% rename from src/test/strategy.test.ts rename to test/strategy.test.ts index a91bbef4..e102136a 100644 --- a/src/test/strategy.test.ts +++ b/test/strategy.test.ts @@ -1,8 +1,8 @@ import { test, describe } from 'node:test' import assert from 'node:assert' -import Authenticator from '../Authenticator' +import Authenticator from '../src/Authenticator' import { getConfiguredTestServer, TestStrategy } from './helpers' -import { Strategy } from '../strategies' +import { Strategy } from '../src/strategies' const testSuite = (sessionPluginName: string) => { describe(`${sessionPluginName} tests`, () => { diff --git a/tsconfig.json b/tsconfig.json index 8922df2c..61128164 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,7 +13,7 @@ "noImplicitAny": false, "removeComments": true, "noUnusedLocals": true, - "rootDir": "./src", + "rootDir": ".", "resolveJsonModule": true, "newLine": "lf", "noFallthroughCasesInSwitch": true, @@ -22,5 +22,5 @@ "skipLibCheck": true, "lib": ["ESNext"] }, - "include": ["src"] + "include": ["src", "test"] }