@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 +77 -15
- package/dist/decision.d.ts +18 -0
- package/dist/decision.js +158 -0
- package/dist/decision.js.map +1 -0
- package/dist/guard.d.ts +5 -9
- package/dist/guard.js +24 -175
- package/dist/guard.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/module.d.ts +4 -1
- package/dist/module.js +38 -14
- package/dist/module.js.map +1 -1
- package/dist/passport/index.d.ts +17 -0
- package/dist/passport/index.js +59 -0
- package/dist/passport/index.js.map +1 -0
- package/dist/passport-guard.d.ts +17 -0
- package/dist/passport-guard.js +100 -0
- package/dist/passport-guard.js.map +1 -0
- package/dist/principal-strategy.d.ts +17 -0
- package/dist/principal-strategy.js +84 -0
- package/dist/principal-strategy.js.map +1 -0
- package/dist/strategy.d.ts +9 -3
- package/dist/strategy.js +29 -12
- package/dist/strategy.js.map +1 -1
- package/package.json +16 -3
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: '
|
|
25
|
-
jwtAudience: '
|
|
26
|
-
jwtIssuer: '
|
|
27
|
-
jwtJwksUri: '
|
|
28
|
-
jwtClockToleranceS: '
|
|
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', //
|
|
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
|
|
38
|
-
//
|
|
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
|
|
42
|
-
constructor(private readonly users: UsersRepository) {}
|
|
43
|
-
async resolve(identity: VerifiedSignetIdentity): Promise<PrincipalResolution<
|
|
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
|
-
|
|
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],
|
|
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
|
|
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;
|
package/dist/decision.js
ADDED
|
@@ -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 {
|
|
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
|
|
7
|
-
export declare class SignetBearerGuard extends SignetBearerGuard_base {
|
|
7
|
+
export declare class SignetBearerGuard extends SignetPassportGuard {
|
|
8
8
|
private readonly resolver;
|
|
9
|
-
private readonly
|
|
9
|
+
private readonly decision;
|
|
10
10
|
private readonly signetOptions;
|
|
11
|
-
private readonly reflector;
|
|
12
11
|
private readonly logger;
|
|
13
|
-
constructor(resolver: SignetPrincipalResolver,
|
|
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
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
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,
|
|
82
|
-
super();
|
|
35
|
+
constructor(resolver, decision, signetOptions, reflector) {
|
|
36
|
+
super(reflector);
|
|
83
37
|
this.resolver = resolver;
|
|
84
|
-
this.
|
|
38
|
+
this.decision = decision;
|
|
85
39
|
this.signetOptions = signetOptions;
|
|
86
|
-
this.reflector = reflector;
|
|
87
40
|
}
|
|
88
41
|
async canActivate(context) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
context.getClass(),
|
|
92
|
-
]) === true) {
|
|
42
|
+
const request = await this.authenticate(context);
|
|
43
|
+
if (request === 'public')
|
|
93
44
|
return true;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
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,
|
|
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":";;;;;;;;;;;;;;;AAAA,
|
|
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';
|