@marxbiotech/signet-integration 0.1.1 → 0.2.0

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/README.md CHANGED
@@ -10,6 +10,12 @@ stored, is yours.
10
10
 
11
11
  ## Wiring
12
12
 
13
+ Two ways in. Both verify the same way, classify the resolver's outcome the same
14
+ way (401 / 403 / 503 / 500) and write the same decision log line; pick by where
15
+ you want the principal to land.
16
+
17
+ ### A. Your own Passport strategy (`request.user` is your principal)
18
+
13
19
  ```ts
14
20
  // your-signet.options.ts -- your values, one object
15
21
  export const MY_OPTIONS = {
@@ -21,26 +27,25 @@ export const MY_OPTIONS = {
21
27
  },
22
28
  developmentProfile: { canonicalResource: 'http://localhost/api/mcp', host: 'localhost', namespace: 'development' },
23
29
  env: {
24
- deploymentNamespace: 'MY_DEPLOYMENT_NAMESPACE',
25
- jwtAudience: 'MY_JWT_AUDIENCE',
26
- jwtIssuer: 'MY_JWT_ISSUER',
27
- jwtJwksUri: 'MY_JWT_JWKS_URI',
28
- jwtClockToleranceS: 'MY_JWT_CLOCK_TOLERANCE_S',
30
+ deploymentNamespace: 'SIGNET_DEPLOYMENT_NAMESPACE',
31
+ jwtAudience: 'SIGNET_JWT_AUDIENCE',
32
+ jwtIssuer: 'SIGNET_JWT_ISSUER',
33
+ jwtJwksUri: 'SIGNET_JWT_JWKS_URI',
34
+ jwtClockToleranceS: 'SIGNET_JWT_CLOCK_TOLERANCE_S',
29
35
  signetEnabled: 'SIGNET_AUTH_ENABLED', // omit = the Bearer channel is always on
30
36
  legacyApiKeyEnabled: 'LEGACY_API_KEY_ENABLED', // omit = no legacy channel
31
37
  },
32
38
  realm: 'myservice',
33
- requestPrincipalKey: 'user', // where the guard attaches your principal
39
+ requestPrincipalKey: 'user', // route B attaches here; route A leaves it to Passport
34
40
  scopesSupported: ['myservice:access'], // RFC 9728 scopes_supported; must include the admission scope
35
41
  } as const satisfies SignetIntegrationOptions<'production' | 'staging'>;
36
42
 
37
- // your resolver -- Passport's verify callback, as a Nest provider.
38
- // Annotate the return type: a class method gets no contextual typing from
39
- // `implements`, so without it `ok: true` widens to `boolean` and fails to compile.
43
+ // your strategy: Passport's verify callback, with the identity already proven.
44
+ // The package's dependencies are property-injected; the constructor is yours.
40
45
  @Injectable()
41
- export class MyResolver implements SignetPrincipalResolver<MyPrincipal> {
42
- constructor(private readonly users: UsersRepository) {}
43
- async resolve(identity: VerifiedSignetIdentity): Promise<PrincipalResolution<MyPrincipal>> {
46
+ export class MySignetStrategy extends SignetPrincipalStrategy<MyUser> {
47
+ constructor(private readonly users: UsersRepository) { super(); }
48
+ async resolve(identity: VerifiedSignetIdentity): Promise<PrincipalResolution<MyUser>> {
44
49
  const user = await this.users.findBySignetSubject(identity.subject); // or identity.claims.erp_user_id
45
50
  if (!user) return { ok: false, reason: 'unknown_subject' }; // → 403, reason only logged
46
51
  return { ok: true, principal: user, logFields: { userId: user.id } }; // logFields render on the decision line
@@ -48,7 +53,33 @@ export class MyResolver implements SignetPrincipalResolver<MyPrincipal> {
48
53
  }
49
54
  }
50
55
 
51
- // your auth module
56
+ @Module({
57
+ imports: [UsersModule, SignetIntegrationModule.forPassport({ options: MY_OPTIONS })],
58
+ providers: [
59
+ MySignetStrategy, // registers itself with Passport
60
+ { provide: APP_GUARD, useClass: SignetPassportGuard }, // AuthGuard('signet-jwt') that honours @Public()
61
+ { provide: APP_FILTER, useClass: BearerChallengeFilter }, // RFC 6750 challenges on 401/403
62
+ ],
63
+ controllers: [createProtectedResourceController(MY_OPTIONS), ...yourControllers],
64
+ })
65
+ export class ApiModule {}
66
+ ```
67
+
68
+ `SignetPassportGuard` is `AuthGuard('signet-jwt')` plus `@Public()` and a
69
+ uniform 401; any `AuthGuard('signet-jwt')` of your own works too. Handlers read
70
+ the principal from `request.user` as they always did.
71
+
72
+ ### B. Module-wired resolver (principal under `requestPrincipalKey`)
73
+
74
+ The same options and the same `resolve`, as a provider the module binds:
75
+
76
+ ```ts
77
+ @Injectable()
78
+ export class MyResolver implements SignetPrincipalResolver<MyUser> {
79
+ constructor(private readonly users: UsersRepository) {}
80
+ async resolve(identity: VerifiedSignetIdentity): Promise<PrincipalResolution<MyUser>> { /* as above */ }
81
+ }
82
+
52
83
  @Module({
53
84
  imports: [
54
85
  UsersModule,
@@ -58,7 +89,7 @@ export class MyResolver implements SignetPrincipalResolver<MyPrincipal> {
58
89
  imports: [UsersModule], // whatever the resolver's constructor needs
59
90
  }),
60
91
  ],
61
- exports: [SignetIntegrationModule], // so the process module below can inject the package's providers
92
+ exports: [SignetIntegrationModule],
62
93
  })
63
94
  export class AuthModule {}
64
95
 
@@ -74,6 +105,19 @@ export class AuthModule {}
74
105
  export class ApiModule {}
75
106
  ```
76
107
 
108
+ Here Passport's `request.user` stays the verified identity and the principal
109
+ goes under `options.requestPrincipalKey` (`principal`, say), which is what a
110
+ consumer with several credential channels and its own dispatcher wants.
111
+
112
+ ### Entry points
113
+
114
+ - `@marxbiotech/signet-integration` -- everything.
115
+ - `@marxbiotech/signet-integration/passport` -- everything except
116
+ `createProtectedResourceController`, whose file needs `@nestjs/swagger` and
117
+ `@nestjs/throttler` (both optional peers). Import from here if you serve the
118
+ metadata document some other way.
119
+ - `@marxbiotech/signet-integration/testing` -- see below.
120
+
77
121
  Requirements: `ConfigModule` must be global (the strategy, filter and profile
78
122
  service inject `ConfigService`); validate the env variables named in `options.env`
79
123
  in your own config validation (Joi or otherwise), the package only re-checks the
@@ -108,4 +152,22 @@ issues), `startJwksServer()`, `FIXTURE_OPTIONS` and `FIXTURE_DEVELOPMENT_PROFILE
108
152
  - Verifier failures (bad signature, wrong audience, unreachable JWKS) are one uniform 401; the reason is only logged.
109
153
  - `iat` is not required and no `maxTokenAge` is applied; `client_id` is required (RFC 9068 §2.2, Signet issues it always).
110
154
  - JWKS options are fixed: 3 s timeout, 30 s cooldown, 10 min cache.
111
- - The guard's decision log line is `metric reason <your logFields> clientId environment`; the fixed keys are reserved.
155
+ - The decision log line is `metric reason <your logFields> clientId environment`, written under your strategy's or guard's class name; the fixed keys are reserved.
156
+
157
+ ## Development and release
158
+
159
+ ```sh
160
+ pnpm install
161
+ pnpm lint && pnpm typecheck && pnpm test && pnpm build
162
+ ```
163
+
164
+ Releases are cut by tag. Write the `CHANGELOG.md` entry, bump `version` in
165
+ `package.json` on `main`, then push the matching tag:
166
+
167
+ ```sh
168
+ git tag v0.2.0 && git push origin v0.2.0
169
+ ```
170
+
171
+ `.github/workflows/publish.yml` refuses a tag that does not equal
172
+ `v<package.json version>`, runs lint, tests and build, and publishes with npm
173
+ trusted publishing (OIDC). Nothing is published from a developer machine.
@@ -0,0 +1,18 @@
1
+ import { Logger } from '@nestjs/common';
2
+ import { SignetDeploymentProfileService } from './deployment-profile';
3
+ import { type VerifiedSignetIdentity } from './jwt-verifier';
4
+ import { type SignetIntegrationOptions } from './options';
5
+ import { type SignetPrincipalResolver } from './principal-resolver';
6
+ export interface DecisionContext<P> {
7
+ readonly attach?: (principal: P) => void;
8
+ readonly logger?: Logger;
9
+ readonly source?: string;
10
+ }
11
+ export declare class SignetDecision {
12
+ private readonly options;
13
+ private readonly profiles;
14
+ private readonly logger;
15
+ constructor(options: SignetIntegrationOptions, profiles: SignetDeploymentProfileService);
16
+ decide<P>(identity: VerifiedSignetIdentity, resolver: SignetPrincipalResolver<P>, context?: DecisionContext<P>): Promise<P>;
17
+ }
18
+ export declare function logFailure(logger: Logger, message: string, metric: 'auth_store_error' | 'auth_store_unavailable' | 'resolver_contract_error' | 'signet_identity_missing', errorName: string, stack?: string): void;
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ var SignetDecision_1;
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.SignetDecision = void 0;
17
+ exports.logFailure = logFailure;
18
+ const common_1 = require("@nestjs/common");
19
+ const bearer_challenge_1 = require("./bearer-challenge");
20
+ const deployment_profile_1 = require("./deployment-profile");
21
+ const log_sanitise_1 = require("./log-sanitise");
22
+ const options_1 = require("./options");
23
+ const principal_resolver_1 = require("./principal-resolver");
24
+ // The decision line's own keys. A resolver's `logFields` may not reuse them:
25
+ // a second `metric=` or `clientId=` on one line is whichever copy the log
26
+ // parser keeps, and operators alert on these.
27
+ const RESERVED_LOG_KEYS = new Set([
28
+ 'metric',
29
+ 'reason',
30
+ 'clientId',
31
+ 'environment',
32
+ ]);
33
+ // The layer between a verified identity and a request that may proceed: the
34
+ // admission scope, the consumer's resolver, and the one rendering of the
35
+ // outcome as an HTTP status and a log line. Both the package's Bearer guard
36
+ // and a consumer's own Passport strategy go through here, so the
37
+ // classification cannot drift between them:
38
+ //
39
+ // token lacks the admission scope → 403 (service scope), with an
40
+ // insufficient_scope challenge
41
+ // resolver says { ok: false } → 403, bare
42
+ // resolver throws PrincipalStoreUnavailableError → 503, fail closed
43
+ // resolver throws an HttpException → its own status
44
+ // resolver throws anything else → 500, the error under its own name
45
+ // resolver logFields reuses a fixed key → 500, resolver_contract_error
46
+ //
47
+ // None of these are 401: the token was valid. The 403 messages distinguish
48
+ // "get a token with the right scope" from "ask an administrator" without
49
+ // naming what the caller does not hold. The 500 is deliberately NOT a 503:
50
+ // a bug in the resolver must not send the operator to check the database.
51
+ //
52
+ // The resolver is a parameter, not something the caller runs first: a
53
+ // consumer that wrapped its own call in try/catch could turn a store outage
54
+ // into a 401, which is the silent failure this layer exists to prevent.
55
+ let SignetDecision = SignetDecision_1 = class SignetDecision {
56
+ options;
57
+ profiles;
58
+ logger = new common_1.Logger(SignetDecision_1.name);
59
+ constructor(options, profiles) {
60
+ this.options = options;
61
+ this.profiles = profiles;
62
+ }
63
+ async decide(identity, resolver, context = {}) {
64
+ const logger = context.logger ?? this.logger;
65
+ const { admissionScope } = this.options;
66
+ if (!identity.scopes.includes(admissionScope)) {
67
+ logger.warn(`signet token lacks service scope: metric=signet_scope_missing source=${(0, log_sanitise_1.sanitiseLogToken)(context.source ?? 'none')}`);
68
+ // A 403 a client CAN fix by re-authorising (RFC 6750 §3.1); the
69
+ // resolver's refusals below stay bare, because a wider OAuth scope
70
+ // would not supply a missing local authorization.
71
+ throw new bearer_challenge_1.InsufficientScopeException(admissionScope);
72
+ }
73
+ const environment = this.profiles.profile.environment;
74
+ let resolution;
75
+ try {
76
+ resolution = await resolver.resolve(identity);
77
+ }
78
+ catch (error) {
79
+ // An HttpException is a decision already taken below us; it is neither
80
+ // the store nor a bug, and it must reach the caller with its own status.
81
+ if (error instanceof common_1.HttpException)
82
+ throw error;
83
+ if (error instanceof principal_resolver_1.PrincipalStoreUnavailableError) {
84
+ // The line names what actually failed (the cause's class), not the
85
+ // wrapper; the driver's message travels in the stack, never on the
86
+ // key=value line.
87
+ logFailure(logger, 'authorization store unavailable', 'auth_store_unavailable', error.causeName, error.causeStack);
88
+ throw new common_1.ServiceUnavailableException('authorization store unavailable');
89
+ }
90
+ const errorName = error instanceof Error && error.name ? error.name : typeof error;
91
+ const stack = error instanceof Error ? error.stack : undefined;
92
+ // A bug: rethrow unchanged so Nest answers 500 and the error keeps its
93
+ // own name in the exception filter.
94
+ logFailure(logger, 'authorization load failed', 'auth_store_error', errorName, stack);
95
+ throw error;
96
+ }
97
+ if (!resolution.ok) {
98
+ // The reason is logged for the operator and NOT returned.
99
+ logDecision(logger, 'refused', identity.clientId, environment, resolution);
100
+ throw new common_1.ForbiddenException(this.options.principalRefusalMessage ??
101
+ 'this identity holds no authorization in this service');
102
+ }
103
+ context.attach?.(resolution.principal);
104
+ logDecision(logger, 'authorized', identity.clientId, environment, resolution);
105
+ return resolution.principal;
106
+ }
107
+ };
108
+ exports.SignetDecision = SignetDecision;
109
+ exports.SignetDecision = SignetDecision = SignetDecision_1 = __decorate([
110
+ (0, common_1.Injectable)(),
111
+ __param(0, (0, common_1.Inject)(options_1.SIGNET_INTEGRATION_OPTIONS)),
112
+ __metadata("design:paramtypes", [Object, deployment_profile_1.SignetDeploymentProfileService])
113
+ ], SignetDecision);
114
+ // The one rendering of the authorization DECISION: the line that lets a
115
+ // request through and the line that refuses one, so their field set and its
116
+ // order cannot drift apart -- metric, reason, the resolver's logFields in the
117
+ // order given, clientId, environment.
118
+ //
119
+ // What these lines disclose about the caller: the client id (software, not
120
+ // a person; a registration identifier, not a credential) and whatever the
121
+ // resolver chose to put in `logFields`. No `sub`, no issuer.
122
+ //
123
+ // Levels: a refusal is a `warn`, an authorization a `log` (info). One line
124
+ // per authorized request is the price of having any record of which client
125
+ // acted; it is paid deliberately, because the alternative is no record.
126
+ function logDecision(logger, outcome, clientId, environment, resolution) {
127
+ const consumerFields = Object.entries(resolution.logFields ?? {});
128
+ for (const [key] of consumerFields) {
129
+ if (RESERVED_LOG_KEYS.has(key)) {
130
+ // A programming error in the resolver, not a decision: one alertable
131
+ // line, then a 500 under its own name rather than a forged log line.
132
+ logFailure(logger, 'resolver logFields invalid', 'resolver_contract_error', 'Error');
133
+ throw new Error(`resolver logFields must not use the reserved key ${key}`);
134
+ }
135
+ }
136
+ const pairs = [
137
+ ['reason', resolution.ok ? 'none' : resolution.reason],
138
+ ...consumerFields,
139
+ ['clientId', clientId],
140
+ ['environment', environment],
141
+ ];
142
+ const rendered = pairs
143
+ .map(([key, value]) => `${(0, log_sanitise_1.sanitiseLogToken)(key)}=${(0, log_sanitise_1.sanitiseLogToken)(value)}`)
144
+ .join(' ');
145
+ if (outcome === 'refused') {
146
+ logger.warn(`signet principal refused: metric=signet_principal_refused ${rendered}`);
147
+ return;
148
+ }
149
+ logger.log(`signet principal authorized: metric=signet_principal_authorized ${rendered}`);
150
+ }
151
+ // The one rendering of the failure lines. Only the error's class name goes
152
+ // into the key=value line: the driver's message can carry the DSN or the
153
+ // SQL, so it travels in the stack as the SECOND argument, where a structured
154
+ // logger renders it after the filterable fields.
155
+ function logFailure(logger, message, metric, errorName, stack) {
156
+ logger.error(`${message}: metric=${metric} errorName=${(0, log_sanitise_1.sanitiseLogToken)(errorName)}`, stack);
157
+ }
158
+ //# sourceMappingURL=decision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decision.js","sourceRoot":"","sources":["../src/decision.ts"],"names":[],"mappings":"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"}
package/dist/guard.d.ts CHANGED
@@ -1,18 +1,14 @@
1
1
  import { type ExecutionContext } from '@nestjs/common';
2
2
  import { Reflector } from '@nestjs/core';
3
- import { SignetDeploymentProfileService } from './deployment-profile';
3
+ import { SignetDecision } from './decision';
4
4
  import { type SignetIntegrationOptions } from './options';
5
+ import { SignetPassportGuard } from './passport-guard';
5
6
  import { type SignetPrincipalResolver } from './principal-resolver';
6
- declare const SignetBearerGuard_base: import("@nestjs/passport").Type<import("@nestjs/passport").IAuthGuard>;
7
- export declare class SignetBearerGuard extends SignetBearerGuard_base {
7
+ export declare class SignetBearerGuard extends SignetPassportGuard {
8
8
  private readonly resolver;
9
- private readonly profiles;
9
+ private readonly decision;
10
10
  private readonly signetOptions;
11
- private readonly reflector;
12
11
  private readonly logger;
13
- constructor(resolver: SignetPrincipalResolver, profiles: SignetDeploymentProfileService, signetOptions: SignetIntegrationOptions, reflector: Reflector);
12
+ constructor(resolver: SignetPrincipalResolver, decision: SignetDecision, signetOptions: SignetIntegrationOptions, reflector: Reflector);
14
13
  canActivate(context: ExecutionContext): Promise<boolean>;
15
- private logDecision;
16
- private logFailure;
17
14
  }
18
- export {};
package/dist/guard.js CHANGED
@@ -15,200 +15,49 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.SignetBearerGuard = void 0;
16
16
  const common_1 = require("@nestjs/common");
17
17
  const core_1 = require("@nestjs/core");
18
- const passport_1 = require("@nestjs/passport");
19
- const bearer_challenge_1 = require("./bearer-challenge");
20
- const deployment_profile_1 = require("./deployment-profile");
21
- const log_sanitise_1 = require("./log-sanitise");
18
+ const decision_1 = require("./decision");
22
19
  const options_1 = require("./options");
20
+ const passport_guard_1 = require("./passport-guard");
23
21
  const principal_resolver_1 = require("./principal-resolver");
24
- const public_decorator_1 = require("./public.decorator");
25
- const strategy_1 = require("./strategy");
26
- // The decision line's own keys. A resolver's `logFields` may not reuse them:
27
- // a second `metric=` or `clientId=` on one line is whichever copy the log
28
- // parser keeps, and operators alert on these.
29
- const RESERVED_LOG_KEYS = new Set([
30
- 'metric',
31
- 'reason',
32
- 'clientId',
33
- 'environment',
34
- ]);
35
- // Nest's OPTIONAL_DEPS_METADATA. `@nestjs/common` does not re-export it.
36
- const OPTIONAL_PARAMTYPES = 'optional:paramtypes';
37
- // AuthGuard's mixin marks its constructor parameter 0 `@Optional()`.
38
- // `Reflect.getMetadata` walks the constructor prototype chain, so a subclass
39
- // that never writes its own list treats ITS parameter 0 — the principal
40
- // resolver — as optional. `@Optional()` on a later parameter copies that
41
- // inherited list onto this constructor before appending its own index.
42
- // Parameter decorators run before this one, so the copy is already own
43
- // metadata by the time this drops index 0 (and writes a list when absent).
44
- function requirePrincipalResolver() {
45
- return (target) => {
46
- const own = Reflect.getOwnMetadata(OPTIONAL_PARAMTYPES, target);
47
- const optionalIndexes = Array.isArray(own)
48
- ? own.filter((index) => index !== 0)
49
- : [];
50
- Reflect.defineMetadata(OPTIONAL_PARAMTYPES, optionalIndexes, target);
51
- };
52
- }
53
- // The Bearer channel's guard: Passport dispatches SignetJwtStrategy, and a
54
- // false from it is a uniform 401. A route marked `@Public()` (on the handler
55
- // or its class) is let through first, so the guard can be an APP_GUARD on
56
- // its own; a consumer with a credential dispatcher of its own decides
57
- // `@Public()` there and never hands a public route to this guard. What
58
- // follows is the layer on top:
59
- //
60
- // token lacks the admission scope → 403 (service scope), with an
61
- // insufficient_scope challenge
62
- // resolver says { ok: false } → 403, bare
63
- // resolver throws PrincipalStoreUnavailableError → 503, fail closed
64
- // resolver throws an HttpException → its own status
65
- // resolver throws anything else → 500, the error under its own name
66
- // resolver logFields reuses a fixed key → 500, resolver_contract_error
67
- // Passport passed but attached no user → 500, programming error
68
- //
69
- // None of these are 401: the token was valid. The 403 messages distinguish
70
- // "get a token with the right scope" from "ask an administrator" without
71
- // naming what the caller does not hold. The 500 is deliberately NOT a 503:
72
- // a bug in the resolver must not send the operator to check the database.
73
- let SignetBearerGuard = class SignetBearerGuard extends (0, passport_1.AuthGuard)(strategy_1.SIGNET_JWT_STRATEGY) {
22
+ // The Bearer channel's guard for the module-wired resolver: SignetJwtStrategy
23
+ // leaves the verified identity on request.user, the resolver bound to
24
+ // SIGNET_PRINCIPAL_RESOLVER turns it into the consumer's principal, and the
25
+ // principal goes under options.requestPrincipalKey (which may also be `user`,
26
+ // in which case the identity is overwritten by the principal). The
27
+ // classification of the resolver's outcome is SignetDecision's.
28
+ let SignetBearerGuard = class SignetBearerGuard extends passport_guard_1.SignetPassportGuard {
74
29
  resolver;
75
- profiles;
30
+ decision;
76
31
  signetOptions;
77
- reflector;
78
32
  // The subclass's name when a consumer subclasses this guard, so its log
79
33
  // `context` keeps the name the consumer's operators filter on.
80
34
  logger = new common_1.Logger(this.constructor.name);
81
- constructor(resolver, profiles, signetOptions, reflector) {
82
- super();
35
+ constructor(resolver, decision, signetOptions, reflector) {
36
+ super(reflector);
83
37
  this.resolver = resolver;
84
- this.profiles = profiles;
38
+ this.decision = decision;
85
39
  this.signetOptions = signetOptions;
86
- this.reflector = reflector;
87
40
  }
88
41
  async canActivate(context) {
89
- if (this.reflector.getAllAndOverride(public_decorator_1.IS_PUBLIC_ROUTE, [
90
- context.getHandler(),
91
- context.getClass(),
92
- ]) === true) {
42
+ const request = await this.authenticate(context);
43
+ if (request === 'public')
93
44
  return true;
94
- }
95
- // Typed unknown: the base declares boolean | Promise<boolean> |
96
- // Observable<boolean>, and anything other than a literal true is treated
97
- // as "did not pass".
98
- let passed;
99
- try {
100
- passed = await super.canActivate(context);
101
- }
102
- catch (error) {
103
- // Passport's own UnauthorizedException carries a message; the contract
104
- // is one indistinguishable 401. Anything else is a programming error
105
- // and must not be dressed up as an authentication failure.
106
- if (error instanceof common_1.UnauthorizedException) {
107
- throw new common_1.UnauthorizedException();
108
- }
109
- throw error;
110
- }
111
- const request = context.switchToHttp().getRequest();
112
- const identity = request.user;
113
- if (passed !== true || identity === undefined) {
114
- this.logFailure('signet strategy did not pass cleanly', 'signet_identity_missing', 'none');
115
- throw new common_1.InternalServerErrorException('signet strategy did not pass cleanly');
116
- }
117
- const { admissionScope } = this.signetOptions;
118
- if (!identity.scopes.includes(admissionScope)) {
119
- this.logger.warn(`signet token lacks service scope: metric=signet_scope_missing source=${(0, log_sanitise_1.sanitiseLogToken)(request.ip ?? 'none')}`);
120
- // A 403 a client CAN fix by re-authorising (RFC 6750 §3.1); the
121
- // resolver's refusals below stay bare, because a wider OAuth scope
122
- // would not supply a missing local authorization.
123
- throw new bearer_challenge_1.InsufficientScopeException(admissionScope);
124
- }
125
- const environment = this.profiles.profile.environment;
126
- let resolution;
127
- try {
128
- resolution = await this.resolver.resolve(identity);
129
- }
130
- catch (error) {
131
- // An HttpException is a decision already taken below us; it is neither
132
- // the store nor a bug, and it must reach the caller with its own status.
133
- if (error instanceof common_1.HttpException)
134
- throw error;
135
- if (error instanceof principal_resolver_1.PrincipalStoreUnavailableError) {
136
- // The line names what actually failed (the cause's class), not the
137
- // wrapper; the driver's message travels in the stack, never on the
138
- // key=value line.
139
- this.logFailure('authorization store unavailable', 'auth_store_unavailable', error.causeName, error.causeStack);
140
- throw new common_1.ServiceUnavailableException('authorization store unavailable');
141
- }
142
- const errorName = error instanceof Error && error.name ? error.name : typeof error;
143
- const stack = error instanceof Error ? error.stack : undefined;
144
- // A bug: rethrow unchanged so Nest answers 500 and the error keeps its
145
- // own name in the exception filter.
146
- this.logFailure('authorization load failed', 'auth_store_error', errorName, stack);
147
- throw error;
148
- }
149
- if (!resolution.ok) {
150
- // The reason is logged for the operator and NOT returned.
151
- this.logDecision('refused', identity.clientId, environment, resolution);
152
- throw new common_1.ForbiddenException(this.signetOptions.principalRefusalMessage ??
153
- 'this identity holds no authorization in this service');
154
- }
155
- // Attached before the decision line: an assignment that throws must not
156
- // leave an "authorized" line behind.
157
- request[this.signetOptions.requestPrincipalKey] = resolution.principal;
158
- this.logDecision('authorized', identity.clientId, environment, resolution);
45
+ await this.decision.decide(request.user, this.resolver, {
46
+ attach: (principal) => {
47
+ request[this.signetOptions.requestPrincipalKey] = principal;
48
+ },
49
+ logger: this.logger,
50
+ source: request.ip,
51
+ });
159
52
  return true;
160
53
  }
161
- // The one rendering of this guard's authorization DECISION: the line that
162
- // lets a request through and the line that refuses one, so their field set
163
- // and its order cannot drift apart -- metric, reason, the resolver's
164
- // logFields in the order given, clientId, environment.
165
- //
166
- // What these lines disclose about the caller: the client id (software, not
167
- // a person; a registration identifier, not a credential) and whatever the
168
- // resolver chose to put in `logFields`. No `sub`, no issuer.
169
- //
170
- // Levels: a refusal is a `warn`, an authorization a `log` (info). One line
171
- // per authorized request is the price of having any record of which client
172
- // acted; it is paid deliberately, because the alternative is no record.
173
- logDecision(outcome, clientId, environment, resolution) {
174
- const consumerFields = Object.entries(resolution.logFields ?? {});
175
- for (const [key] of consumerFields) {
176
- if (RESERVED_LOG_KEYS.has(key)) {
177
- // A programming error in the resolver, not a decision: one alertable
178
- // line, then a 500 under its own name rather than a forged log line.
179
- this.logFailure('resolver logFields invalid', 'resolver_contract_error', 'Error');
180
- throw new Error(`resolver logFields must not use the reserved key ${key}`);
181
- }
182
- }
183
- const pairs = [
184
- ['reason', resolution.ok ? 'none' : resolution.reason],
185
- ...consumerFields,
186
- ['clientId', clientId],
187
- ['environment', environment],
188
- ];
189
- const rendered = pairs
190
- .map(([key, value]) => `${(0, log_sanitise_1.sanitiseLogToken)(key)}=${(0, log_sanitise_1.sanitiseLogToken)(value)}`)
191
- .join(' ');
192
- if (outcome === 'refused') {
193
- this.logger.warn(`signet principal refused: metric=signet_principal_refused ${rendered}`);
194
- return;
195
- }
196
- this.logger.log(`signet principal authorized: metric=signet_principal_authorized ${rendered}`);
197
- }
198
- // The one rendering of this guard's failure lines. Only the error's class
199
- // name goes into the key=value line: the driver's message can carry the
200
- // DSN or the SQL, so it travels in the stack as the SECOND argument, where
201
- // a structured logger renders it after the filterable fields.
202
- logFailure(message, metric, errorName, stack) {
203
- this.logger.error(`${message}: metric=${metric} errorName=${(0, log_sanitise_1.sanitiseLogToken)(errorName)}`, stack);
204
- }
205
54
  };
206
55
  exports.SignetBearerGuard = SignetBearerGuard;
207
56
  exports.SignetBearerGuard = SignetBearerGuard = __decorate([
208
57
  (0, common_1.Injectable)(),
209
- requirePrincipalResolver(),
58
+ (0, passport_guard_1.ownConstructorParametersRequired)(),
210
59
  __param(0, (0, common_1.Inject)(principal_resolver_1.SIGNET_PRINCIPAL_RESOLVER)),
211
60
  __param(2, (0, common_1.Inject)(options_1.SIGNET_INTEGRATION_OPTIONS)),
212
- __metadata("design:paramtypes", [Object, deployment_profile_1.SignetDeploymentProfileService, Object, core_1.Reflector])
61
+ __metadata("design:paramtypes", [Object, decision_1.SignetDecision, Object, core_1.Reflector])
213
62
  ], SignetBearerGuard);
214
63
  //# sourceMappingURL=guard.js.map
package/dist/guard.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"guard.js","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"guard.js","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAKwB;AACxB,uCAAyC;AAEzC,yCAA4C;AAE5C,uCAGmB;AACnB,qDAG0B;AAC1B,6DAG8B;AAE9B,8EAA8E;AAC9E,sEAAsE;AACtE,4EAA4E;AAC5E,8EAA8E;AAC9E,mEAAmE;AACnE,gEAAgE;AAGzD,IAAM,iBAAiB,GAAvB,MAAM,iBAAkB,SAAQ,oCAAmB;IAOrC;IACA;IAKA;IAZnB,wEAAwE;IACxE,+DAA+D;IAC9C,MAAM,GAAG,IAAI,eAAM,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAE5D,YAEmB,QAAiC,EACjC,QAAwB,EAKxB,aAAuC,EACxD,SAAoB;QAEpB,KAAK,CAAC,SAAS,CAAC,CAAC;QATA,aAAQ,GAAR,QAAQ,CAAyB;QACjC,aAAQ,GAAR,QAAQ,CAAgB;QAKxB,kBAAa,GAAb,aAAa,CAA0B;IAI1D,CAAC;IAEQ,KAAK,CAAC,WAAW,CAAC,OAAyB;QAClD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,YAAY,CAAyB,OAAO,CAAC,CAAC;QACzE,IAAI,OAAO,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACtC,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;YACtD,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE;gBACpB,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,mBAAmB,CAAC,GAAG,SAAS,CAAC;YAC9D,CAAC;YACD,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,MAAM,EAAE,OAAO,CAAC,EAAE;SACnB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AA/BY,8CAAiB;4BAAjB,iBAAiB;IAF7B,IAAA,mBAAU,GAAE;IACZ,IAAA,iDAAgC,GAAE;IAO9B,WAAA,IAAA,eAAM,EAAC,8CAAyB,CAAC,CAAA;IAMjC,WAAA,IAAA,eAAM,EAAC,oCAA0B,CAAC,CAAA;6CAJR,yBAAc,UAM9B,gBAAS;GAdX,iBAAiB,CA+B7B"}
package/dist/index.d.ts CHANGED
@@ -1,15 +1,18 @@
1
1
  export { bearerChallenge, carriesScopeChallenge, type ChallengeDecision, challengeFor, insufficientScopeChallenge, InsufficientScopeException, isResourceRequest, protectedResourceMetadataPath, protectedResourceMetadataUrl, type ScopeChallengeCarrier, } from './bearer-challenge';
2
2
  export { BearerChallengeFilter } from './bearer-challenge.filter';
3
3
  export { type ChannelFlags, type JwtSettings, readChannelFlags, readSignetAuthConfig, type SignetAuthConfig, type SignetEnvOptions, } from './config';
4
+ export { type DecisionContext, SignetDecision } from './decision';
4
5
  export { type DeploymentProfile, type DeploymentProfileInput, type DeploymentProfileResolution, resolveDeploymentProfile, SignetDeploymentProfileService, } from './deployment-profile';
5
6
  export { SignetBearerGuard } from './guard';
6
7
  export { type JwtRejection, JwtRejectionEnum, JwtVerifier, REMOTE_JWKS_OPTIONS, type VerifiedSignetIdentity, type VerifyResult, } from './jwt-verifier';
7
- export { type ResolverProvider, SignetIntegrationModule, type SignetIntegrationModuleOptions, } from './module';
8
+ export { type ResolverProvider, SignetIntegrationModule, type SignetIntegrationModuleOptions, type SignetPassportModuleOptions, } from './module';
8
9
  export { DEVELOPMENT_ENVIRONMENT, SIGNET_INTEGRATION_OPTIONS, type SignetDeployedProfile, type SignetDevelopmentProfile, type SignetEnvNames, type SignetIntegrationOptions, } from './options';
10
+ export { type AuthenticatedRequest, SignetPassportGuard, } from './passport-guard';
9
11
  export { createSignetPrincipalDecorator } from './principal.decorator';
10
12
  export { type PrincipalResolution, PrincipalStoreUnavailableError, SIGNET_PRINCIPAL_RESOLVER, type SignetPrincipalResolver, } from './principal-resolver';
13
+ export { SignetPrincipalStrategy } from './principal-strategy';
11
14
  export { createProtectedResourceController, type ProtectedResourceMetadata, } from './protected-resource.controller';
12
15
  export { IS_PUBLIC_ROUTE, Public } from './public.decorator';
13
16
  export { createScopeVocabulary, type ScopeVocabulary, type ScopeVocabularyInput, type TokenScopeRead, } from './scope-vocabulary';
14
- export { bearerToken, SIGNET_JWT_STRATEGY, SignetJwtStrategy, } from './strategy';
17
+ export { bearerToken, SIGNET_JWT_STRATEGY, SignetBearerVerification, SignetJwtStrategy, } from './strategy';
15
18
  export { validateSignetIntegrationOptions } from './validate-options';