@xtaskjs/security 1.0.0 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,24 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
+
6
+ ## 1.0.3 (2026-03-14)
7
+
8
+ **Note:** Version bump only for package @xtaskjs/security
9
+
10
+
11
+
12
+
13
+
14
+ ## 1.0.2 (2026-03-14)
15
+
16
+ **Note:** Version bump only for package @xtaskjs/security
17
+
18
+
19
+
20
+
21
+
22
+ ## 1.0.1 (2026-03-14)
23
+
24
+ **Note:** Version bump only for package @xtaskjs/security
@@ -0,0 +1,6 @@
1
+ import { RouteExecutionContext } from "@xtaskjs/common";
2
+ import { SecurityAuthenticationResult } from "./types";
3
+ export declare class SecurityAuthenticationService {
4
+ authenticate(context: RouteExecutionContext, strategyNames?: string | string[]): Promise<SecurityAuthenticationResult>;
5
+ authenticateRequest(request: any, response?: any, strategyNames?: string | string[]): Promise<SecurityAuthenticationResult>;
6
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SecurityAuthenticationService = void 0;
4
+ const core_1 = require("@xtaskjs/core");
5
+ const lifecycle_1 = require("./lifecycle");
6
+ class SecurityAuthenticationService {
7
+ async authenticate(context, strategyNames) {
8
+ const result = await (0, lifecycle_1.getSecurityLifecycleManager)().authenticateContext(context, strategyNames);
9
+ if (!result.success) {
10
+ throw new core_1.UnauthorizedError(result.challenge?.message || "Unauthorized", {
11
+ message: result.challenge?.message || "Unauthorized",
12
+ });
13
+ }
14
+ return result;
15
+ }
16
+ async authenticateRequest(request, response, strategyNames) {
17
+ return (0, lifecycle_1.getSecurityLifecycleManager)().authenticateRequest(request, response, strategyNames);
18
+ }
19
+ }
20
+ exports.SecurityAuthenticationService = SecurityAuthenticationService;
@@ -0,0 +1,6 @@
1
+ import { RouteAuthenticationContext, RouteExecutionContext } from "@xtaskjs/common";
2
+ import { SecurityRoleMatchingMode } from "./types";
3
+ export declare class SecurityAuthorizationService {
4
+ getRoles(source: RouteExecutionContext | RouteAuthenticationContext | any): string[];
5
+ isAuthorized(source: RouteExecutionContext | RouteAuthenticationContext | any, requiredRoles: string[], mode?: SecurityRoleMatchingMode): boolean;
6
+ }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SecurityAuthorizationService = void 0;
4
+ const normalizeRoles = (input) => {
5
+ if (Array.isArray(input)) {
6
+ return input
7
+ .map((value) => String(value || "").trim())
8
+ .filter((value) => value.length > 0);
9
+ }
10
+ if (typeof input === "string") {
11
+ return input
12
+ .split(/[\s,]+/)
13
+ .map((value) => value.trim())
14
+ .filter((value) => value.length > 0);
15
+ }
16
+ return [];
17
+ };
18
+ class SecurityAuthorizationService {
19
+ getRoles(source) {
20
+ if (!source) {
21
+ return [];
22
+ }
23
+ if (Array.isArray(source.roles)) {
24
+ return normalizeRoles(source.roles);
25
+ }
26
+ if (source.auth && Array.isArray(source.auth.roles)) {
27
+ return normalizeRoles(source.auth.roles);
28
+ }
29
+ if (Array.isArray(source.user?.roles) || typeof source.user?.roles === "string") {
30
+ return normalizeRoles(source.user.roles);
31
+ }
32
+ if (Array.isArray(source.claims?.roles) || typeof source.claims?.roles === "string") {
33
+ return normalizeRoles(source.claims.roles);
34
+ }
35
+ if (typeof source.claims?.scope === "string") {
36
+ return normalizeRoles(source.claims.scope);
37
+ }
38
+ return [];
39
+ }
40
+ isAuthorized(source, requiredRoles, mode = "any") {
41
+ if (requiredRoles.length === 0) {
42
+ return true;
43
+ }
44
+ const grantedRoles = new Set(this.getRoles(source));
45
+ if (mode === "all") {
46
+ return requiredRoles.every((role) => grantedRoles.has(role));
47
+ }
48
+ return requiredRoles.some((role) => grantedRoles.has(role));
49
+ }
50
+ }
51
+ exports.SecurityAuthorizationService = SecurityAuthorizationService;
@@ -0,0 +1,8 @@
1
+ import { JweSecurityStrategyOptions, JwtSecurityStrategyOptions, RegisteredJweSecurityStrategyOptions, RegisteredJwtSecurityStrategyOptions, RegisteredSecurityStrategyOptions } from "./types";
2
+ export declare const registerJwtStrategy: (options: JwtSecurityStrategyOptions) => RegisteredJwtSecurityStrategyOptions;
3
+ export declare const registerJweStrategy: (options: JweSecurityStrategyOptions) => RegisteredJweSecurityStrategyOptions;
4
+ export declare const getRegisteredSecurityStrategies: () => RegisteredSecurityStrategyOptions[];
5
+ export declare const clearRegisteredSecurityStrategies: () => void;
6
+ export declare const getDefaultSecurityStrategyName: () => string | undefined;
7
+ export declare const JwtSecurityStrategy: (options: JwtSecurityStrategyOptions) => ClassDecorator;
8
+ export declare const JweSecurityStrategy: (options: JweSecurityStrategyOptions) => ClassDecorator;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JweSecurityStrategy = exports.JwtSecurityStrategy = exports.getDefaultSecurityStrategyName = exports.clearRegisteredSecurityStrategies = exports.getRegisteredSecurityStrategies = exports.registerJweStrategy = exports.registerJwtStrategy = void 0;
4
+ const DEFAULT_STRATEGY_NAME = "default";
5
+ const registeredStrategies = new Map();
6
+ const resolveStrategyName = (kind, requestedName) => {
7
+ if (typeof requestedName === "string" && requestedName.trim().length > 0) {
8
+ return requestedName.trim();
9
+ }
10
+ if (!registeredStrategies.has(DEFAULT_STRATEGY_NAME)) {
11
+ return DEFAULT_STRATEGY_NAME;
12
+ }
13
+ return `${kind}-${registeredStrategies.size + 1}`;
14
+ };
15
+ const registerJwtStrategy = (options) => {
16
+ const name = resolveStrategyName("jwt", options.name);
17
+ const definition = {
18
+ ...options,
19
+ kind: "jwt",
20
+ name,
21
+ };
22
+ registeredStrategies.set(name, definition);
23
+ return definition;
24
+ };
25
+ exports.registerJwtStrategy = registerJwtStrategy;
26
+ const registerJweStrategy = (options) => {
27
+ const name = resolveStrategyName("jwe", options.name);
28
+ const definition = {
29
+ ...options,
30
+ kind: "jwe",
31
+ name,
32
+ };
33
+ registeredStrategies.set(name, definition);
34
+ return definition;
35
+ };
36
+ exports.registerJweStrategy = registerJweStrategy;
37
+ const getRegisteredSecurityStrategies = () => {
38
+ return Array.from(registeredStrategies.values());
39
+ };
40
+ exports.getRegisteredSecurityStrategies = getRegisteredSecurityStrategies;
41
+ const clearRegisteredSecurityStrategies = () => {
42
+ registeredStrategies.clear();
43
+ };
44
+ exports.clearRegisteredSecurityStrategies = clearRegisteredSecurityStrategies;
45
+ const getDefaultSecurityStrategyName = () => {
46
+ const explicitDefault = Array.from(registeredStrategies.values()).find((definition) => definition.default === true);
47
+ if (explicitDefault) {
48
+ return explicitDefault.name;
49
+ }
50
+ if (registeredStrategies.size === 1) {
51
+ return Array.from(registeredStrategies.keys())[0];
52
+ }
53
+ if (registeredStrategies.has(DEFAULT_STRATEGY_NAME)) {
54
+ return DEFAULT_STRATEGY_NAME;
55
+ }
56
+ return undefined;
57
+ };
58
+ exports.getDefaultSecurityStrategyName = getDefaultSecurityStrategyName;
59
+ const JwtSecurityStrategy = (options) => {
60
+ return () => {
61
+ (0, exports.registerJwtStrategy)(options);
62
+ };
63
+ };
64
+ exports.JwtSecurityStrategy = JwtSecurityStrategy;
65
+ const JweSecurityStrategy = (options) => {
66
+ return () => {
67
+ (0, exports.registerJweStrategy)(options);
68
+ };
69
+ };
70
+ exports.JweSecurityStrategy = JweSecurityStrategy;
@@ -0,0 +1,5 @@
1
+ import { AuthenticatedOptions, RolesOptions } from "./types";
2
+ export declare const Authenticated: (value?: string | string[] | AuthenticatedOptions) => MethodDecorator & ClassDecorator;
3
+ export declare const Auth: (value?: string | string[] | AuthenticatedOptions) => MethodDecorator & ClassDecorator;
4
+ export declare const Roles: (value: RolesOptions | string, ...roles: string[]) => MethodDecorator & ClassDecorator;
5
+ export declare const AllowAnonymous: () => MethodDecorator & ClassDecorator;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AllowAnonymous = exports.Roles = exports.Auth = exports.Authenticated = void 0;
4
+ const common_1 = require("@xtaskjs/common");
5
+ const guards_1 = require("./guards");
6
+ const metadata_1 = require("./metadata");
7
+ const applyDecorator = (decorator, target, propertyKey, descriptor) => {
8
+ if (propertyKey === undefined) {
9
+ decorator(target);
10
+ return;
11
+ }
12
+ decorator(target, propertyKey, descriptor);
13
+ };
14
+ const normalizeAuthenticatedOptions = (value) => {
15
+ if (!value) {
16
+ return {};
17
+ }
18
+ if (typeof value === "string" || Array.isArray(value)) {
19
+ return { strategies: value };
20
+ }
21
+ return value;
22
+ };
23
+ const Authenticated = (value) => {
24
+ const options = normalizeAuthenticatedOptions(value);
25
+ const strategies = (0, metadata_1.normalizeStrategies)(options.strategies);
26
+ const guardDecorator = (0, common_1.UseGuards)(guards_1.authenticationGuard);
27
+ return (target, propertyKey, descriptor) => {
28
+ (0, metadata_1.setAuthenticatedMetadata)(target, propertyKey, strategies);
29
+ applyDecorator(guardDecorator, target, propertyKey, descriptor);
30
+ };
31
+ };
32
+ exports.Authenticated = Authenticated;
33
+ exports.Auth = exports.Authenticated;
34
+ const normalizeRolesOptions = (value, roles) => {
35
+ if (typeof value === "string") {
36
+ return { roles: [value, ...roles] };
37
+ }
38
+ return value;
39
+ };
40
+ const Roles = (value, ...roles) => {
41
+ const options = normalizeRolesOptions(value, roles);
42
+ const guardDecorator = (0, common_1.UseGuards)(guards_1.authorizationGuard);
43
+ return (target, propertyKey, descriptor) => {
44
+ (0, metadata_1.setRolesMetadata)(target, propertyKey, options);
45
+ applyDecorator(guardDecorator, target, propertyKey, descriptor);
46
+ };
47
+ };
48
+ exports.Roles = Roles;
49
+ const AllowAnonymous = () => {
50
+ return (target, propertyKey) => {
51
+ (0, metadata_1.setAllowAnonymousMetadata)(target, propertyKey);
52
+ };
53
+ };
54
+ exports.AllowAnonymous = AllowAnonymous;
@@ -0,0 +1,3 @@
1
+ import { GuardLike } from "@xtaskjs/common";
2
+ export declare const authenticationGuard: GuardLike;
3
+ export declare const authorizationGuard: GuardLike;
package/dist/guards.js ADDED
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.authorizationGuard = exports.authenticationGuard = void 0;
4
+ const core_1 = require("@xtaskjs/core");
5
+ const authentication_1 = require("./authentication");
6
+ const authorization_1 = require("./authorization");
7
+ const metadata_1 = require("./metadata");
8
+ const AUTHENTICATION_GUARD_STATE_KEY = "xtask:security:authentication-checked";
9
+ const AUTHORIZATION_GUARD_STATE_KEY = "xtask:security:authorization-checked";
10
+ const authenticationService = new authentication_1.SecurityAuthenticationService();
11
+ const authorizationService = new authorization_1.SecurityAuthorizationService();
12
+ exports.authenticationGuard = {
13
+ async canActivate(context) {
14
+ if (context.state[AUTHENTICATION_GUARD_STATE_KEY]) {
15
+ return true;
16
+ }
17
+ const metadata = (0, metadata_1.resolveSecurityMetadata)(context.controller?.constructor, context.handler);
18
+ if (metadata.allowAnonymous) {
19
+ context.state[AUTHENTICATION_GUARD_STATE_KEY] = true;
20
+ return true;
21
+ }
22
+ await authenticationService.authenticate(context, metadata.strategies);
23
+ context.state[AUTHENTICATION_GUARD_STATE_KEY] = true;
24
+ return true;
25
+ },
26
+ };
27
+ exports.authorizationGuard = {
28
+ async canActivate(context) {
29
+ if (context.state[AUTHORIZATION_GUARD_STATE_KEY]) {
30
+ return true;
31
+ }
32
+ const metadata = (0, metadata_1.resolveSecurityMetadata)(context.controller?.constructor, context.handler);
33
+ if (metadata.allowAnonymous || metadata.roles.length === 0) {
34
+ context.state[AUTHORIZATION_GUARD_STATE_KEY] = true;
35
+ return true;
36
+ }
37
+ if (!context.auth.isAuthenticated) {
38
+ await exports.authenticationGuard.canActivate(context);
39
+ }
40
+ if (!authorizationService.isAuthorized(context.auth, metadata.roles, metadata.roleMode)) {
41
+ throw new core_1.ForbiddenError("Forbidden", {
42
+ message: "Forbidden",
43
+ requiredRoles: metadata.roles,
44
+ actualRoles: context.auth.roles,
45
+ });
46
+ }
47
+ context.state[AUTHORIZATION_GUARD_STATE_KEY] = true;
48
+ return true;
49
+ },
50
+ };
@@ -1,5 +1,4 @@
1
1
  import "reflect-metadata";
2
-
3
2
  export * from "./types";
4
3
  export * from "./tokens";
5
4
  export * from "./configuration";
@@ -7,4 +6,4 @@ export * from "./authentication";
7
6
  export * from "./authorization";
8
7
  export * from "./decorators";
9
8
  export * from "./guards";
10
- export * from "./lifecycle";
9
+ export * from "./lifecycle";
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ require("reflect-metadata");
18
+ __exportStar(require("./types"), exports);
19
+ __exportStar(require("./tokens"), exports);
20
+ __exportStar(require("./configuration"), exports);
21
+ __exportStar(require("./authentication"), exports);
22
+ __exportStar(require("./authorization"), exports);
23
+ __exportStar(require("./decorators"), exports);
24
+ __exportStar(require("./guards"), exports);
25
+ __exportStar(require("./lifecycle"), exports);
@@ -0,0 +1,25 @@
1
+ import { RouteExecutionContext } from "@xtaskjs/common";
2
+ import { Container } from "@xtaskjs/core";
3
+ import { SecurityAuthenticationResult } from "./types";
4
+ export declare class SecurityLifecycleManager {
5
+ private passportInstance;
6
+ private readonly strategyDefinitions;
7
+ private container?;
8
+ initialize(container?: Container): Promise<void>;
9
+ destroy(): Promise<void>;
10
+ getPassport(): any;
11
+ authenticateContext(context: RouteExecutionContext, strategyNames?: string | string[]): Promise<SecurityAuthenticationResult>;
12
+ authenticateRequest(request: any, _response?: any, strategyNames?: string | string[]): Promise<SecurityAuthenticationResult>;
13
+ private resolveStrategyNames;
14
+ private registerJwtStrategy;
15
+ private runPassportStrategy;
16
+ private registerContainerBindings;
17
+ }
18
+ export declare const initializeSecurityIntegration: (container?: Container) => Promise<void>;
19
+ export declare const shutdownSecurityIntegration: () => Promise<void>;
20
+ export declare const getSecurityLifecycleManager: () => SecurityLifecycleManager;
21
+ export declare const resetSecurityIntegration: () => Promise<void>;
22
+ export declare const InjectAuthenticationService: () => ParameterDecorator & PropertyDecorator;
23
+ export declare const InjectAuthorizationService: () => ParameterDecorator & PropertyDecorator;
24
+ export declare const InjectPassport: () => ParameterDecorator & PropertyDecorator;
25
+ export declare const InjectSecurityLifecycleManager: () => ParameterDecorator & PropertyDecorator;