@hyper.authio/server 0.8.0 → 0.8.1
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 +19 -4
- package/dist/index.cjs +28 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -5
- package/dist/index.d.ts +19 -5
- package/dist/index.js +29 -11
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -301,13 +301,26 @@ declare class AuthioBearerError extends AuthioError {
|
|
|
301
301
|
get wwwAuthenticate(): string;
|
|
302
302
|
}
|
|
303
303
|
/**
|
|
304
|
-
*
|
|
304
|
+
* A rule refused a caller whose token is valid. A refusal asking for a stronger or more recent sign-in
|
|
305
|
+
* (`insufficient_user_authentication`) answers `401`, as RFC 9470 §3 has it, naming the protected resource metadata
|
|
306
|
+
* (`resource_metadata`, RFC 9728 §5.1) when the API publishes it; every other refusal answers `403`, with
|
|
307
|
+
* `wwwAuthenticate` when it is about scopes (`insufficient_scope`, RFC 6750 §3.1).
|
|
305
308
|
*/
|
|
306
309
|
declare class AuthioForbiddenError extends AuthioError {
|
|
307
|
-
|
|
310
|
+
/** `401` for a step-up (`insufficient_user_authentication`), `403` otherwise. */
|
|
311
|
+
readonly status: 401 | 403;
|
|
308
312
|
readonly failure: AuthioRuleFailure;
|
|
309
|
-
|
|
310
|
-
|
|
313
|
+
/** The API's protected resource metadata URL (RFC 9728), sent as `resource_metadata` with a step-up `401`. */
|
|
314
|
+
readonly resourceMetadata: string | undefined;
|
|
315
|
+
constructor(failure: AuthioRuleFailure, options?: {
|
|
316
|
+
resourceMetadata?: string;
|
|
317
|
+
});
|
|
318
|
+
/** The same refusal, pointing at the API's protected resource metadata. */
|
|
319
|
+
withResourceMetadata(resourceMetadata: string | undefined): AuthioForbiddenError;
|
|
320
|
+
/**
|
|
321
|
+
* The `WWW-Authenticate` value, or `undefined` when the refusal is not about scopes or the sign-in. A step-up `401`
|
|
322
|
+
* carries `resource_metadata` when `resourceMetadata` is set.
|
|
323
|
+
*/
|
|
311
324
|
get wwwAuthenticate(): string | undefined;
|
|
312
325
|
}
|
|
313
326
|
/**
|
|
@@ -316,7 +329,8 @@ declare class AuthioForbiddenError extends AuthioError {
|
|
|
316
329
|
*/
|
|
317
330
|
declare function toForbiddenChallenge(failure: AuthioRuleFailure): string | undefined;
|
|
318
331
|
/**
|
|
319
|
-
* Problem details (RFC 9457) for a `401` or `403
|
|
332
|
+
* Problem details (RFC 9457) for a `401` or `403`, with the refusal's `status`: a step-up refusal is `401`
|
|
333
|
+
* Unauthorized and carries `error`, `acr_values` and `max_age`.
|
|
320
334
|
*/
|
|
321
335
|
declare function toProblemDetails(error: AuthioBearerError | AuthioForbiddenError): Record<string, unknown>;
|
|
322
336
|
|
package/dist/index.d.ts
CHANGED
|
@@ -301,13 +301,26 @@ declare class AuthioBearerError extends AuthioError {
|
|
|
301
301
|
get wwwAuthenticate(): string;
|
|
302
302
|
}
|
|
303
303
|
/**
|
|
304
|
-
*
|
|
304
|
+
* A rule refused a caller whose token is valid. A refusal asking for a stronger or more recent sign-in
|
|
305
|
+
* (`insufficient_user_authentication`) answers `401`, as RFC 9470 §3 has it, naming the protected resource metadata
|
|
306
|
+
* (`resource_metadata`, RFC 9728 §5.1) when the API publishes it; every other refusal answers `403`, with
|
|
307
|
+
* `wwwAuthenticate` when it is about scopes (`insufficient_scope`, RFC 6750 §3.1).
|
|
305
308
|
*/
|
|
306
309
|
declare class AuthioForbiddenError extends AuthioError {
|
|
307
|
-
|
|
310
|
+
/** `401` for a step-up (`insufficient_user_authentication`), `403` otherwise. */
|
|
311
|
+
readonly status: 401 | 403;
|
|
308
312
|
readonly failure: AuthioRuleFailure;
|
|
309
|
-
|
|
310
|
-
|
|
313
|
+
/** The API's protected resource metadata URL (RFC 9728), sent as `resource_metadata` with a step-up `401`. */
|
|
314
|
+
readonly resourceMetadata: string | undefined;
|
|
315
|
+
constructor(failure: AuthioRuleFailure, options?: {
|
|
316
|
+
resourceMetadata?: string;
|
|
317
|
+
});
|
|
318
|
+
/** The same refusal, pointing at the API's protected resource metadata. */
|
|
319
|
+
withResourceMetadata(resourceMetadata: string | undefined): AuthioForbiddenError;
|
|
320
|
+
/**
|
|
321
|
+
* The `WWW-Authenticate` value, or `undefined` when the refusal is not about scopes or the sign-in. A step-up `401`
|
|
322
|
+
* carries `resource_metadata` when `resourceMetadata` is set.
|
|
323
|
+
*/
|
|
311
324
|
get wwwAuthenticate(): string | undefined;
|
|
312
325
|
}
|
|
313
326
|
/**
|
|
@@ -316,7 +329,8 @@ declare class AuthioForbiddenError extends AuthioError {
|
|
|
316
329
|
*/
|
|
317
330
|
declare function toForbiddenChallenge(failure: AuthioRuleFailure): string | undefined;
|
|
318
331
|
/**
|
|
319
|
-
* Problem details (RFC 9457) for a `401` or `403
|
|
332
|
+
* Problem details (RFC 9457) for a `401` or `403`, with the refusal's `status`: a step-up refusal is `401`
|
|
333
|
+
* Unauthorized and carries `error`, `acr_values` and `max_age`.
|
|
320
334
|
*/
|
|
321
335
|
declare function toProblemDetails(error: AuthioBearerError | AuthioForbiddenError): Record<string, unknown>;
|
|
322
336
|
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { systemClock, AuthioClaimTypes, parseResourceAccess, AuthioError, AuthioDefaults, resolveIssuer, AuthioConfigurationError, resolveConnection, createAuthioDiscoveryClient, seconds, AuthioHmacAlgorithms, createAuthioSigningKeys, encryptionKeyFromFile, createAuthioTokenDecrypter, AuthioAsymmetricAlgorithms, requireHmacSecretLength, createAuthioClient, toScopeString, AuthioWebhookHeaders, verifyWebhookSignature, tryParseWebhookEvent, toClientCredentialsToken, isAuthioTokenEncrypted, AuthioTokenValidationError, withClientSecret, verifyAuthioJwt, AuthioTokenTypes } from '@hyper.authio/client';
|
|
1
|
+
import { systemClock, AuthioClaimTypes, parseResourceAccess, AuthioError, AuthioDefaults, resolveIssuer, AuthioConfigurationError, resolveConnection, createAuthioDiscoveryClient, seconds, AuthioHmacAlgorithms, createAuthioSigningKeys, encryptionKeyFromFile, createAuthioTokenDecrypter, AuthioAsymmetricAlgorithms, requireHmacSecretLength, createAuthioClient, toScopeString, parseAuthioBearerChallenge, AuthioOAuthErrors, AuthioWebhookHeaders, verifyWebhookSignature, tryParseWebhookEvent, toClientCredentialsToken, isAuthioTokenEncrypted, AuthioTokenValidationError, withClientSecret, verifyAuthioJwt, AuthioTokenTypes } from '@hyper.authio/client';
|
|
2
2
|
import { decodeProtectedHeader } from 'jose';
|
|
3
3
|
|
|
4
4
|
// src/user.ts
|
|
@@ -469,16 +469,30 @@ var AuthioBearerError = class _AuthioBearerError extends AuthioError {
|
|
|
469
469
|
return parameters.length === 0 ? "Bearer" : `Bearer ${parameters.join(", ")}`;
|
|
470
470
|
}
|
|
471
471
|
};
|
|
472
|
-
var AuthioForbiddenError = class extends AuthioError {
|
|
473
|
-
|
|
472
|
+
var AuthioForbiddenError = class _AuthioForbiddenError extends AuthioError {
|
|
473
|
+
/** `401` for a step-up (`insufficient_user_authentication`), `403` otherwise. */
|
|
474
|
+
status;
|
|
474
475
|
failure;
|
|
475
|
-
|
|
476
|
+
/** The API's protected resource metadata URL (RFC 9728), sent as `resource_metadata` with a step-up `401`. */
|
|
477
|
+
resourceMetadata;
|
|
478
|
+
constructor(failure, options = {}) {
|
|
476
479
|
super(failure.message);
|
|
477
480
|
this.failure = failure;
|
|
481
|
+
this.status = failure.error === "insufficient_user_authentication" ? 401 : 403;
|
|
482
|
+
this.resourceMetadata = options.resourceMetadata;
|
|
478
483
|
}
|
|
479
|
-
/** The
|
|
484
|
+
/** The same refusal, pointing at the API's protected resource metadata. */
|
|
485
|
+
withResourceMetadata(resourceMetadata) {
|
|
486
|
+
if (!resourceMetadata || resourceMetadata === this.resourceMetadata) return this;
|
|
487
|
+
return new _AuthioForbiddenError(this.failure, { resourceMetadata });
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* The `WWW-Authenticate` value, or `undefined` when the refusal is not about scopes or the sign-in. A step-up `401`
|
|
491
|
+
* carries `resource_metadata` when `resourceMetadata` is set.
|
|
492
|
+
*/
|
|
480
493
|
get wwwAuthenticate() {
|
|
481
|
-
|
|
494
|
+
const challenge = toForbiddenChallenge(this.failure);
|
|
495
|
+
return challenge && this.status === 401 && this.resourceMetadata ? `${challenge}, resource_metadata="${escapeQuoted(this.resourceMetadata)}"` : challenge;
|
|
482
496
|
}
|
|
483
497
|
};
|
|
484
498
|
function toForbiddenChallenge(failure) {
|
|
@@ -492,15 +506,15 @@ function toForbiddenChallenge(failure) {
|
|
|
492
506
|
function toProblemDetails(error) {
|
|
493
507
|
if (error instanceof AuthioBearerError)
|
|
494
508
|
return {
|
|
495
|
-
type:
|
|
509
|
+
type: unauthorizedType,
|
|
496
510
|
title: "Unauthorized",
|
|
497
511
|
status: 401,
|
|
498
512
|
detail: "A valid Authio access token is required."
|
|
499
513
|
};
|
|
500
514
|
return {
|
|
501
|
-
type: "https://tools.ietf.org/html/rfc9110#section-15.5.4",
|
|
502
|
-
title: "Forbidden",
|
|
503
|
-
status:
|
|
515
|
+
type: error.status === 401 ? unauthorizedType : "https://tools.ietf.org/html/rfc9110#section-15.5.4",
|
|
516
|
+
title: error.status === 401 ? "Unauthorized" : "Forbidden",
|
|
517
|
+
status: error.status,
|
|
504
518
|
detail: error.failure.message || "The caller is not allowed to access this resource.",
|
|
505
519
|
...error.failure.error ? { error: error.failure.error } : {},
|
|
506
520
|
...error.failure.scope ? { scope: error.failure.scope } : {},
|
|
@@ -508,6 +522,7 @@ function toProblemDetails(error) {
|
|
|
508
522
|
...error.failure.maxAge !== void 0 ? { max_age: error.failure.maxAge } : {}
|
|
509
523
|
};
|
|
510
524
|
}
|
|
525
|
+
var unauthorizedType = "https://tools.ietf.org/html/rfc9110#section-15.5.2";
|
|
511
526
|
function toHeaderText(text) {
|
|
512
527
|
return [...text].map((character) => character >= " " && character <= "~" && character !== '"' && character !== "\\" ? character : "?").join("");
|
|
513
528
|
}
|
|
@@ -903,13 +918,16 @@ function createOnBehalfOf(options) {
|
|
|
903
918
|
};
|
|
904
919
|
const token = await getToken(subject, request.signal);
|
|
905
920
|
const response = await attempt(token);
|
|
906
|
-
if (response.status !== 401) return response;
|
|
921
|
+
if (response.status !== 401 || isStepUpChallenge(response)) return response;
|
|
907
922
|
await invalidate(subject, token.accessToken);
|
|
908
923
|
return attempt(await getToken(subject, request.signal));
|
|
909
924
|
};
|
|
910
925
|
}
|
|
911
926
|
};
|
|
912
927
|
}
|
|
928
|
+
function isStepUpChallenge(response) {
|
|
929
|
+
return parseAuthioBearerChallenge(response.headers.get("www-authenticate"))?.error === AuthioOAuthErrors.InsufficientUserAuthentication;
|
|
930
|
+
}
|
|
913
931
|
function tokenOf(subject) {
|
|
914
932
|
if (!subject) return void 0;
|
|
915
933
|
return typeof subject === "string" ? subject : subject.token;
|