@spfn/auth 0.2.0-beta.86 → 0.2.0-beta.87
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 +14 -9
- package/dist/{authenticate-DTA7W4v2.d.ts → authenticate-C7w-pLOR.d.ts} +60 -5
- package/dist/client-proof.d.ts +185 -46
- package/dist/client-proof.js +223 -34
- package/dist/client-proof.js.map +1 -1
- package/dist/index.d.ts +7 -6
- package/dist/server.d.ts +91 -70
- package/dist/server.js +878 -69
- package/dist/server.js.map +1 -1
- package/package.json +4 -4
package/dist/server.js
CHANGED
|
@@ -1379,7 +1379,7 @@ var init_boolean2 = __esm({
|
|
|
1379
1379
|
});
|
|
1380
1380
|
|
|
1381
1381
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/bigint/bigint.mjs
|
|
1382
|
-
function
|
|
1382
|
+
function BigInt2(options) {
|
|
1383
1383
|
return CreateType({ [Kind]: "BigInt", type: "bigint" }, options);
|
|
1384
1384
|
}
|
|
1385
1385
|
var init_bigint = __esm({
|
|
@@ -1441,7 +1441,7 @@ var init_string2 = __esm({
|
|
|
1441
1441
|
// ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/template-literal/syntax.mjs
|
|
1442
1442
|
function* FromUnion(syntax) {
|
|
1443
1443
|
const trim = syntax.trim().replace(/"|'/g, "");
|
|
1444
|
-
return trim === "boolean" ? yield Boolean2() : trim === "number" ? yield Number2() : trim === "bigint" ? yield
|
|
1444
|
+
return trim === "boolean" ? yield Boolean2() : trim === "number" ? yield Number2() : trim === "bigint" ? yield BigInt2() : trim === "string" ? yield String2() : yield (() => {
|
|
1445
1445
|
const literals = trim.split("|").map((literal) => Literal(literal.trim()));
|
|
1446
1446
|
return literals.length === 0 ? Never() : literals.length === 1 ? literals[0] : UnionEvaluated(literals);
|
|
1447
1447
|
})();
|
|
@@ -2457,7 +2457,7 @@ function ConditionalReadonly(T, root) {
|
|
|
2457
2457
|
return root === true ? T : Readonly(T);
|
|
2458
2458
|
}
|
|
2459
2459
|
function FromValue(value, root) {
|
|
2460
|
-
return IsAsyncIterator(value) ? ConditionalReadonly(Any(), root) : IsIterator(value) ? ConditionalReadonly(Any(), root) : IsArray(value) ? Readonly(Tuple(FromArray3(value))) : IsUint8Array(value) ? Uint8Array2() : IsDate(value) ? Date2() : IsObject(value) ? ConditionalReadonly(Object2(FromProperties7(value)), root) : IsFunction(value) ? ConditionalReadonly(Function([], Unknown()), root) : IsUndefined(value) ? Undefined() : IsNull(value) ? Null() : IsSymbol(value) ? Symbol2() : IsBigInt(value) ?
|
|
2460
|
+
return IsAsyncIterator(value) ? ConditionalReadonly(Any(), root) : IsIterator(value) ? ConditionalReadonly(Any(), root) : IsArray(value) ? Readonly(Tuple(FromArray3(value))) : IsUint8Array(value) ? Uint8Array2() : IsDate(value) ? Date2() : IsObject(value) ? ConditionalReadonly(Object2(FromProperties7(value)), root) : IsFunction(value) ? ConditionalReadonly(Function([], Unknown()), root) : IsUndefined(value) ? Undefined() : IsNull(value) ? Null() : IsSymbol(value) ? Symbol2() : IsBigInt(value) ? BigInt2() : IsNumber(value) ? Literal(value) : IsBoolean(value) ? Literal(value) : IsString(value) ? Literal(value) : Object2({});
|
|
2461
2461
|
}
|
|
2462
2462
|
function Const(T, options) {
|
|
2463
2463
|
return CreateType(FromValue(T, true), options);
|
|
@@ -4243,7 +4243,7 @@ __export(type_exports3, {
|
|
|
4243
4243
|
Array: () => Array2,
|
|
4244
4244
|
AsyncIterator: () => AsyncIterator,
|
|
4245
4245
|
Awaited: () => Awaited,
|
|
4246
|
-
BigInt: () =>
|
|
4246
|
+
BigInt: () => BigInt2,
|
|
4247
4247
|
Boolean: () => Boolean2,
|
|
4248
4248
|
Capitalize: () => Capitalize,
|
|
4249
4249
|
Composite: () => Composite,
|
|
@@ -5802,6 +5802,17 @@ var init_keys_repository = __esm({
|
|
|
5802
5802
|
).returning();
|
|
5803
5803
|
return result[0] ?? null;
|
|
5804
5804
|
}
|
|
5805
|
+
/**
|
|
5806
|
+
* Key ID로 공개키 조회 — 활성 여부 무관 (clientProofV1 admission의 revocation 판정용)
|
|
5807
|
+
*
|
|
5808
|
+
* 폐기(isActive=false)·만료(expiresAt 경과)를 SESSION_REVOKED로, 미등록을
|
|
5809
|
+
* PROOF_INVALID로 구분해야 하므로 활성 필터 없이 조회한다. keyId는 UNIQUE.
|
|
5810
|
+
* Read replica 사용
|
|
5811
|
+
*/
|
|
5812
|
+
async findByKeyId(keyId) {
|
|
5813
|
+
const result = await this.readDb.select().from(userPublicKeys).where(eq2(userPublicKeys.keyId, keyId)).limit(1);
|
|
5814
|
+
return result[0] ?? null;
|
|
5815
|
+
}
|
|
5805
5816
|
/**
|
|
5806
5817
|
* Key ID로 활성 공개키 조회 (authenticate용)
|
|
5807
5818
|
* Read replica 사용
|
|
@@ -8158,12 +8169,15 @@ async function verifyCodeService(params) {
|
|
|
8158
8169
|
};
|
|
8159
8170
|
}
|
|
8160
8171
|
|
|
8172
|
+
// src/server/lib/key-policy.ts
|
|
8173
|
+
var KEY_TTL_DAYS = 90;
|
|
8174
|
+
|
|
8161
8175
|
// src/server/services/key.service.ts
|
|
8162
8176
|
init_repositories();
|
|
8163
8177
|
import { InvalidKeyFingerprintError } from "@spfn/auth/errors";
|
|
8164
8178
|
function getKeyExpiryDate() {
|
|
8165
8179
|
const expiresAt = /* @__PURE__ */ new Date();
|
|
8166
|
-
expiresAt.setDate(expiresAt.getDate() +
|
|
8180
|
+
expiresAt.setDate(expiresAt.getDate() + KEY_TTL_DAYS);
|
|
8167
8181
|
return expiresAt;
|
|
8168
8182
|
}
|
|
8169
8183
|
async function registerPublicKeyService(params) {
|
|
@@ -9750,8 +9764,8 @@ function getGithubConfig() {
|
|
|
9750
9764
|
};
|
|
9751
9765
|
}
|
|
9752
9766
|
function getGithubScopes() {
|
|
9753
|
-
const
|
|
9754
|
-
return
|
|
9767
|
+
const configured2 = env3.SPFN_AUTH_GITHUB_SCOPES;
|
|
9768
|
+
return configured2 ? configured2.split(",").map((scope) => scope.trim()).filter(Boolean) : ["read:user", "user:email"];
|
|
9755
9769
|
}
|
|
9756
9770
|
async function requestGithubTokens(params) {
|
|
9757
9771
|
const response = await fetch(GITHUB_TOKEN_URL, {
|
|
@@ -9881,8 +9895,8 @@ function getKakaoConfig() {
|
|
|
9881
9895
|
};
|
|
9882
9896
|
}
|
|
9883
9897
|
function getKakaoScopes() {
|
|
9884
|
-
const
|
|
9885
|
-
return
|
|
9898
|
+
const configured2 = env3.SPFN_AUTH_KAKAO_SCOPES;
|
|
9899
|
+
return configured2 ? configured2.split(",").map((scope) => scope.trim()).filter(Boolean) : ["account_email"];
|
|
9886
9900
|
}
|
|
9887
9901
|
async function requestKakaoTokens(params) {
|
|
9888
9902
|
const response = await fetch(KAKAO_TOKEN_URL, {
|
|
@@ -10685,33 +10699,831 @@ import { EMAIL_PATTERN as EMAIL_PATTERN2, UUID_PATTERN } from "@spfn/auth";
|
|
|
10685
10699
|
|
|
10686
10700
|
// src/server/middleware/authenticate.ts
|
|
10687
10701
|
import { defineMiddleware } from "@spfn/core/route";
|
|
10688
|
-
import { UnauthorizedError } from "@spfn/core/errors";
|
|
10689
|
-
import { verifyClientToken as verifyClientToken2, decodeToken as decodeToken2, authLogger as
|
|
10702
|
+
import { UnauthorizedError as UnauthorizedError2 } from "@spfn/core/errors";
|
|
10703
|
+
import { verifyClientToken as verifyClientToken2, decodeToken as decodeToken2, authLogger as authLogger3, keysRepository as keysRepository3, usersRepository as usersRepository3, userProfilesRepository as userProfilesRepository3 } from "@spfn/auth/server";
|
|
10690
10704
|
import {
|
|
10691
10705
|
InvalidTokenError,
|
|
10692
10706
|
TokenExpiredError,
|
|
10693
|
-
KeyExpiredError
|
|
10694
|
-
AccountDisabledError as AccountDisabledError3,
|
|
10695
|
-
AccountPendingDeletionError as AccountPendingDeletionError3
|
|
10707
|
+
KeyExpiredError
|
|
10696
10708
|
} from "@spfn/auth/errors";
|
|
10709
|
+
|
|
10710
|
+
// src/server/middleware/auth-profiles.ts
|
|
10711
|
+
import {
|
|
10712
|
+
BadRequestError as BadRequestError2,
|
|
10713
|
+
ConflictError as ConflictError2,
|
|
10714
|
+
ServiceUnavailableError,
|
|
10715
|
+
UnauthorizedError
|
|
10716
|
+
} from "@spfn/core/errors";
|
|
10717
|
+
import {
|
|
10718
|
+
authLogger as authLogger2,
|
|
10719
|
+
keysRepository as keysRepository2,
|
|
10720
|
+
usersRepository as usersRepository2,
|
|
10721
|
+
userProfilesRepository as userProfilesRepository2,
|
|
10722
|
+
getPendingDeletionInfo as getPendingDeletionInfo2
|
|
10723
|
+
} from "@spfn/auth/server";
|
|
10724
|
+
import { AccountDisabledError as AccountDisabledError3, AccountPendingDeletionError as AccountPendingDeletionError3 } from "@spfn/auth/errors";
|
|
10725
|
+
|
|
10726
|
+
// src/server/client-proof/canonical-json.ts
|
|
10727
|
+
var CanonicalJsonError = class extends Error {
|
|
10728
|
+
constructor(code) {
|
|
10729
|
+
super(`canonical JSON: ${code}`);
|
|
10730
|
+
this.code = code;
|
|
10731
|
+
this.name = "CanonicalJsonError";
|
|
10732
|
+
}
|
|
10733
|
+
};
|
|
10734
|
+
var INT64_MIN = -(2n ** 63n);
|
|
10735
|
+
var INT64_MAX = 2n ** 63n - 1n;
|
|
10736
|
+
function parseCanonicalJson(bytes) {
|
|
10737
|
+
let text12;
|
|
10738
|
+
try {
|
|
10739
|
+
text12 = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
10740
|
+
} catch {
|
|
10741
|
+
throw new CanonicalJsonError("INVALID_UTF8");
|
|
10742
|
+
}
|
|
10743
|
+
const parser = new Parser(text12);
|
|
10744
|
+
const value = parser.parseValue();
|
|
10745
|
+
parser.skipWhitespace();
|
|
10746
|
+
if (!parser.atEnd()) {
|
|
10747
|
+
throw new CanonicalJsonError("TRAILING_CONTENT");
|
|
10748
|
+
}
|
|
10749
|
+
return value;
|
|
10750
|
+
}
|
|
10751
|
+
function isCanonicalBytes(bytes, value) {
|
|
10752
|
+
const encoded = encodeCanonicalJson(value);
|
|
10753
|
+
if (encoded.length !== bytes.length) {
|
|
10754
|
+
return false;
|
|
10755
|
+
}
|
|
10756
|
+
for (let i = 0; i < encoded.length; i++) {
|
|
10757
|
+
if (encoded[i] !== bytes[i]) {
|
|
10758
|
+
return false;
|
|
10759
|
+
}
|
|
10760
|
+
}
|
|
10761
|
+
return true;
|
|
10762
|
+
}
|
|
10763
|
+
var Parser = class {
|
|
10764
|
+
constructor(text12) {
|
|
10765
|
+
this.text = text12;
|
|
10766
|
+
}
|
|
10767
|
+
pos = 0;
|
|
10768
|
+
atEnd() {
|
|
10769
|
+
return this.pos >= this.text.length;
|
|
10770
|
+
}
|
|
10771
|
+
skipWhitespace() {
|
|
10772
|
+
while (!this.atEnd()) {
|
|
10773
|
+
const c = this.text[this.pos];
|
|
10774
|
+
if (c === " " || c === " " || c === "\n" || c === "\r") {
|
|
10775
|
+
this.pos++;
|
|
10776
|
+
continue;
|
|
10777
|
+
}
|
|
10778
|
+
break;
|
|
10779
|
+
}
|
|
10780
|
+
}
|
|
10781
|
+
parseValue() {
|
|
10782
|
+
this.skipWhitespace();
|
|
10783
|
+
if (this.atEnd()) {
|
|
10784
|
+
throw new CanonicalJsonError("UNEXPECTED_END");
|
|
10785
|
+
}
|
|
10786
|
+
const c = this.text[this.pos];
|
|
10787
|
+
if (c === "{") {
|
|
10788
|
+
return this.parseObject();
|
|
10789
|
+
}
|
|
10790
|
+
if (c === "[") {
|
|
10791
|
+
return this.parseArray();
|
|
10792
|
+
}
|
|
10793
|
+
if (c === '"') {
|
|
10794
|
+
return this.parseString();
|
|
10795
|
+
}
|
|
10796
|
+
if (c === "-" || c >= "0" && c <= "9") {
|
|
10797
|
+
return this.parseNumber();
|
|
10798
|
+
}
|
|
10799
|
+
if (this.text.startsWith("null", this.pos)) {
|
|
10800
|
+
this.pos += 4;
|
|
10801
|
+
return null;
|
|
10802
|
+
}
|
|
10803
|
+
if (this.text.startsWith("true", this.pos)) {
|
|
10804
|
+
this.pos += 4;
|
|
10805
|
+
return true;
|
|
10806
|
+
}
|
|
10807
|
+
if (this.text.startsWith("false", this.pos)) {
|
|
10808
|
+
this.pos += 5;
|
|
10809
|
+
return false;
|
|
10810
|
+
}
|
|
10811
|
+
throw new CanonicalJsonError("INVALID_TOKEN");
|
|
10812
|
+
}
|
|
10813
|
+
parseObject() {
|
|
10814
|
+
this.pos++;
|
|
10815
|
+
const members = /* @__PURE__ */ new Map();
|
|
10816
|
+
this.skipWhitespace();
|
|
10817
|
+
if (this.atEnd()) {
|
|
10818
|
+
throw new CanonicalJsonError("UNEXPECTED_END");
|
|
10819
|
+
}
|
|
10820
|
+
if (this.text[this.pos] === "}") {
|
|
10821
|
+
this.pos++;
|
|
10822
|
+
return members;
|
|
10823
|
+
}
|
|
10824
|
+
for (; ; ) {
|
|
10825
|
+
this.skipWhitespace();
|
|
10826
|
+
if (this.atEnd()) {
|
|
10827
|
+
throw new CanonicalJsonError("UNEXPECTED_END");
|
|
10828
|
+
}
|
|
10829
|
+
if (this.text[this.pos] !== '"') {
|
|
10830
|
+
throw new CanonicalJsonError("INVALID_TOKEN");
|
|
10831
|
+
}
|
|
10832
|
+
const key = this.parseString();
|
|
10833
|
+
if (members.has(key)) {
|
|
10834
|
+
throw new CanonicalJsonError("DUPLICATE_KEY");
|
|
10835
|
+
}
|
|
10836
|
+
this.skipWhitespace();
|
|
10837
|
+
if (this.atEnd()) {
|
|
10838
|
+
throw new CanonicalJsonError("UNEXPECTED_END");
|
|
10839
|
+
}
|
|
10840
|
+
if (this.text[this.pos] !== ":") {
|
|
10841
|
+
throw new CanonicalJsonError("INVALID_TOKEN");
|
|
10842
|
+
}
|
|
10843
|
+
this.pos++;
|
|
10844
|
+
members.set(key, this.parseValue());
|
|
10845
|
+
this.skipWhitespace();
|
|
10846
|
+
if (this.atEnd()) {
|
|
10847
|
+
throw new CanonicalJsonError("UNEXPECTED_END");
|
|
10848
|
+
}
|
|
10849
|
+
const next = this.text[this.pos];
|
|
10850
|
+
if (next === ",") {
|
|
10851
|
+
this.pos++;
|
|
10852
|
+
continue;
|
|
10853
|
+
}
|
|
10854
|
+
if (next === "}") {
|
|
10855
|
+
this.pos++;
|
|
10856
|
+
return members;
|
|
10857
|
+
}
|
|
10858
|
+
throw new CanonicalJsonError("INVALID_TOKEN");
|
|
10859
|
+
}
|
|
10860
|
+
}
|
|
10861
|
+
parseArray() {
|
|
10862
|
+
this.pos++;
|
|
10863
|
+
const items = [];
|
|
10864
|
+
this.skipWhitespace();
|
|
10865
|
+
if (this.atEnd()) {
|
|
10866
|
+
throw new CanonicalJsonError("UNEXPECTED_END");
|
|
10867
|
+
}
|
|
10868
|
+
if (this.text[this.pos] === "]") {
|
|
10869
|
+
this.pos++;
|
|
10870
|
+
return items;
|
|
10871
|
+
}
|
|
10872
|
+
for (; ; ) {
|
|
10873
|
+
items.push(this.parseValue());
|
|
10874
|
+
this.skipWhitespace();
|
|
10875
|
+
if (this.atEnd()) {
|
|
10876
|
+
throw new CanonicalJsonError("UNEXPECTED_END");
|
|
10877
|
+
}
|
|
10878
|
+
const next = this.text[this.pos];
|
|
10879
|
+
if (next === ",") {
|
|
10880
|
+
this.pos++;
|
|
10881
|
+
continue;
|
|
10882
|
+
}
|
|
10883
|
+
if (next === "]") {
|
|
10884
|
+
this.pos++;
|
|
10885
|
+
return items;
|
|
10886
|
+
}
|
|
10887
|
+
throw new CanonicalJsonError("INVALID_TOKEN");
|
|
10888
|
+
}
|
|
10889
|
+
}
|
|
10890
|
+
parseString() {
|
|
10891
|
+
this.pos++;
|
|
10892
|
+
let out = "";
|
|
10893
|
+
for (; ; ) {
|
|
10894
|
+
if (this.atEnd()) {
|
|
10895
|
+
throw new CanonicalJsonError("UNEXPECTED_END");
|
|
10896
|
+
}
|
|
10897
|
+
const c = this.text[this.pos];
|
|
10898
|
+
const code = this.text.charCodeAt(this.pos);
|
|
10899
|
+
if (c === '"') {
|
|
10900
|
+
this.pos++;
|
|
10901
|
+
return out;
|
|
10902
|
+
}
|
|
10903
|
+
if (c === "\\") {
|
|
10904
|
+
out += this.parseEscape();
|
|
10905
|
+
continue;
|
|
10906
|
+
}
|
|
10907
|
+
if (code < 32) {
|
|
10908
|
+
throw new CanonicalJsonError("INVALID_TOKEN");
|
|
10909
|
+
}
|
|
10910
|
+
out += c;
|
|
10911
|
+
this.pos++;
|
|
10912
|
+
}
|
|
10913
|
+
}
|
|
10914
|
+
parseEscape() {
|
|
10915
|
+
this.pos++;
|
|
10916
|
+
if (this.atEnd()) {
|
|
10917
|
+
throw new CanonicalJsonError("UNEXPECTED_END");
|
|
10918
|
+
}
|
|
10919
|
+
const c = this.text[this.pos];
|
|
10920
|
+
this.pos++;
|
|
10921
|
+
switch (c) {
|
|
10922
|
+
case '"':
|
|
10923
|
+
return '"';
|
|
10924
|
+
case "\\":
|
|
10925
|
+
return "\\";
|
|
10926
|
+
case "/":
|
|
10927
|
+
return "/";
|
|
10928
|
+
case "b":
|
|
10929
|
+
return "\b";
|
|
10930
|
+
case "f":
|
|
10931
|
+
return "\f";
|
|
10932
|
+
case "n":
|
|
10933
|
+
return "\n";
|
|
10934
|
+
case "r":
|
|
10935
|
+
return "\r";
|
|
10936
|
+
case "t":
|
|
10937
|
+
return " ";
|
|
10938
|
+
case "u":
|
|
10939
|
+
return this.parseUnicodeEscape();
|
|
10940
|
+
default:
|
|
10941
|
+
throw new CanonicalJsonError("INVALID_ESCAPE");
|
|
10942
|
+
}
|
|
10943
|
+
}
|
|
10944
|
+
parseUnicodeEscape() {
|
|
10945
|
+
const high = this.readHex4();
|
|
10946
|
+
if (high >= 56320 && high <= 57343) {
|
|
10947
|
+
throw new CanonicalJsonError("INVALID_ESCAPE");
|
|
10948
|
+
}
|
|
10949
|
+
if (high < 55296 || high > 56319) {
|
|
10950
|
+
return String.fromCharCode(high);
|
|
10951
|
+
}
|
|
10952
|
+
if (this.text[this.pos] !== "\\" || this.text[this.pos + 1] !== "u") {
|
|
10953
|
+
throw new CanonicalJsonError("INVALID_ESCAPE");
|
|
10954
|
+
}
|
|
10955
|
+
this.pos += 2;
|
|
10956
|
+
const low = this.readHex4();
|
|
10957
|
+
if (low < 56320 || low > 57343) {
|
|
10958
|
+
throw new CanonicalJsonError("INVALID_ESCAPE");
|
|
10959
|
+
}
|
|
10960
|
+
return String.fromCharCode(high, low);
|
|
10961
|
+
}
|
|
10962
|
+
readHex4() {
|
|
10963
|
+
if (this.pos + 4 > this.text.length) {
|
|
10964
|
+
throw new CanonicalJsonError("UNEXPECTED_END");
|
|
10965
|
+
}
|
|
10966
|
+
const hex = this.text.slice(this.pos, this.pos + 4);
|
|
10967
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
10968
|
+
throw new CanonicalJsonError("INVALID_ESCAPE");
|
|
10969
|
+
}
|
|
10970
|
+
this.pos += 4;
|
|
10971
|
+
return parseInt(hex, 16);
|
|
10972
|
+
}
|
|
10973
|
+
parseNumber() {
|
|
10974
|
+
const start = this.pos;
|
|
10975
|
+
if (this.text[this.pos] === "-") {
|
|
10976
|
+
this.pos++;
|
|
10977
|
+
}
|
|
10978
|
+
if (this.atEnd()) {
|
|
10979
|
+
throw new CanonicalJsonError("UNEXPECTED_END");
|
|
10980
|
+
}
|
|
10981
|
+
const first = this.text[this.pos];
|
|
10982
|
+
if (first < "0" || first > "9") {
|
|
10983
|
+
throw new CanonicalJsonError("INVALID_TOKEN");
|
|
10984
|
+
}
|
|
10985
|
+
if (first === "0") {
|
|
10986
|
+
this.pos++;
|
|
10987
|
+
} else {
|
|
10988
|
+
while (!this.atEnd() && this.text[this.pos] >= "0" && this.text[this.pos] <= "9") {
|
|
10989
|
+
this.pos++;
|
|
10990
|
+
}
|
|
10991
|
+
}
|
|
10992
|
+
if (!this.atEnd()) {
|
|
10993
|
+
const next = this.text[this.pos];
|
|
10994
|
+
if (next >= "0" && next <= "9") {
|
|
10995
|
+
throw new CanonicalJsonError("INVALID_TOKEN");
|
|
10996
|
+
}
|
|
10997
|
+
if (next === "." || next === "e" || next === "E") {
|
|
10998
|
+
throw new CanonicalJsonError("NON_INTEGER_NUMBER");
|
|
10999
|
+
}
|
|
11000
|
+
}
|
|
11001
|
+
const value = BigInt(this.text.slice(start, this.pos));
|
|
11002
|
+
if (value < INT64_MIN || value > INT64_MAX) {
|
|
11003
|
+
throw new CanonicalJsonError("INTEGER_OUT_OF_RANGE");
|
|
11004
|
+
}
|
|
11005
|
+
return value;
|
|
11006
|
+
}
|
|
11007
|
+
};
|
|
11008
|
+
function encodeCanonicalJson(value) {
|
|
11009
|
+
return new TextEncoder().encode(encodeToString(value));
|
|
11010
|
+
}
|
|
11011
|
+
function encodeToString(value) {
|
|
11012
|
+
if (value === null) {
|
|
11013
|
+
return "null";
|
|
11014
|
+
}
|
|
11015
|
+
if (typeof value === "boolean") {
|
|
11016
|
+
return value ? "true" : "false";
|
|
11017
|
+
}
|
|
11018
|
+
if (typeof value === "bigint") {
|
|
11019
|
+
return value.toString();
|
|
11020
|
+
}
|
|
11021
|
+
if (typeof value === "string") {
|
|
11022
|
+
return encodeString(value);
|
|
11023
|
+
}
|
|
11024
|
+
if (Array.isArray(value)) {
|
|
11025
|
+
return `[${value.map(encodeToString).join(",")}]`;
|
|
11026
|
+
}
|
|
11027
|
+
const keys = [...value.keys()].sort(compareByCodePoints);
|
|
11028
|
+
const members = keys.map((key) => `${encodeString(key)}:${encodeToString(value.get(key))}`);
|
|
11029
|
+
return `{${members.join(",")}}`;
|
|
11030
|
+
}
|
|
11031
|
+
function compareByCodePoints(a, b) {
|
|
11032
|
+
let i = 0;
|
|
11033
|
+
let j = 0;
|
|
11034
|
+
while (i < a.length && j < b.length) {
|
|
11035
|
+
const ca = a.codePointAt(i);
|
|
11036
|
+
const cb = b.codePointAt(j);
|
|
11037
|
+
if (ca !== cb) {
|
|
11038
|
+
return ca - cb;
|
|
11039
|
+
}
|
|
11040
|
+
i += ca > 65535 ? 2 : 1;
|
|
11041
|
+
j += cb > 65535 ? 2 : 1;
|
|
11042
|
+
}
|
|
11043
|
+
return a.length - i - (b.length - j);
|
|
11044
|
+
}
|
|
11045
|
+
function encodeString(value) {
|
|
11046
|
+
let out = '"';
|
|
11047
|
+
for (const ch of value) {
|
|
11048
|
+
const code = ch.codePointAt(0);
|
|
11049
|
+
if (ch === '"') {
|
|
11050
|
+
out += '\\"';
|
|
11051
|
+
} else if (ch === "\\") {
|
|
11052
|
+
out += "\\\\";
|
|
11053
|
+
} else if (code === 8) {
|
|
11054
|
+
out += "\\b";
|
|
11055
|
+
} else if (code === 12) {
|
|
11056
|
+
out += "\\f";
|
|
11057
|
+
} else if (code === 10) {
|
|
11058
|
+
out += "\\n";
|
|
11059
|
+
} else if (code === 13) {
|
|
11060
|
+
out += "\\r";
|
|
11061
|
+
} else if (code === 9) {
|
|
11062
|
+
out += "\\t";
|
|
11063
|
+
} else if (code < 32) {
|
|
11064
|
+
out += `\\u00${code.toString(16).padStart(2, "0")}`;
|
|
11065
|
+
} else {
|
|
11066
|
+
out += ch;
|
|
11067
|
+
}
|
|
11068
|
+
}
|
|
11069
|
+
return out + '"';
|
|
11070
|
+
}
|
|
11071
|
+
|
|
11072
|
+
// src/server/client-proof/proof.ts
|
|
11073
|
+
import { createHash as createHash4, createPrivateKey, createPublicKey, sign, verify as verify2 } from "crypto";
|
|
11074
|
+
var CLIENT_PROOF_PROFILE = "clientProofV1";
|
|
11075
|
+
var ABSENT_BODY_SHA256 = "0".repeat(64);
|
|
11076
|
+
var DEFAULT_REPLAY_WINDOW_MILLIS = 3e5;
|
|
11077
|
+
var PROOF_INPUT_FIELDS = [
|
|
11078
|
+
"profile",
|
|
11079
|
+
"method",
|
|
11080
|
+
"path",
|
|
11081
|
+
"clientId",
|
|
11082
|
+
"keyId",
|
|
11083
|
+
"nonce",
|
|
11084
|
+
"issuedAtMillis",
|
|
11085
|
+
"bodySha256"
|
|
11086
|
+
];
|
|
11087
|
+
var PROOF_INPUT_SEPARATOR = "\n";
|
|
11088
|
+
var PROOF_SIGNATURE_BYTES = 64;
|
|
11089
|
+
var PROOF_SIGNATURE_HEX_LENGTH = PROOF_SIGNATURE_BYTES * 2;
|
|
11090
|
+
var PROOF_SIGNATURE_PATTERN = /^[0-9a-f]{128}$/;
|
|
11091
|
+
var RAW_SIGNATURE_ENCODING = "ieee-p1363";
|
|
11092
|
+
var ProofInputError = class extends Error {
|
|
11093
|
+
constructor() {
|
|
11094
|
+
super("proof input field contains a C0 control character");
|
|
11095
|
+
this.name = "ProofInputError";
|
|
11096
|
+
}
|
|
11097
|
+
};
|
|
11098
|
+
function canonicalProofInput(input) {
|
|
11099
|
+
const values = {
|
|
11100
|
+
profile: CLIENT_PROOF_PROFILE,
|
|
11101
|
+
method: input.method,
|
|
11102
|
+
path: input.path,
|
|
11103
|
+
clientId: input.clientId,
|
|
11104
|
+
keyId: input.keyId,
|
|
11105
|
+
nonce: input.nonce,
|
|
11106
|
+
issuedAtMillis: input.issuedAtMillis.toString(),
|
|
11107
|
+
bodySha256: input.bodySha256
|
|
11108
|
+
};
|
|
11109
|
+
const fields = PROOF_INPUT_FIELDS.map((name) => values[name]);
|
|
11110
|
+
for (const field of fields) {
|
|
11111
|
+
for (const ch of field) {
|
|
11112
|
+
if (ch.codePointAt(0) < 32) {
|
|
11113
|
+
throw new ProofInputError();
|
|
11114
|
+
}
|
|
11115
|
+
}
|
|
11116
|
+
}
|
|
11117
|
+
return fields.join(PROOF_INPUT_SEPARATOR);
|
|
11118
|
+
}
|
|
11119
|
+
function parseClientProofPublicKey(spkiDerBase64) {
|
|
11120
|
+
const key = createPublicKey({
|
|
11121
|
+
key: Buffer.from(spkiDerBase64, "base64"),
|
|
11122
|
+
format: "der",
|
|
11123
|
+
type: "spki"
|
|
11124
|
+
});
|
|
11125
|
+
if (key.asymmetricKeyType !== "ec" || key.asymmetricKeyDetails?.namedCurve !== "prime256v1") {
|
|
11126
|
+
throw new Error("a clientProofV1 public key must be an ECDSA P-256 key");
|
|
11127
|
+
}
|
|
11128
|
+
return key;
|
|
11129
|
+
}
|
|
11130
|
+
function verifyClientProof(input, presentedProof, publicKey) {
|
|
11131
|
+
const data = Buffer.from(canonicalProofInput(input), "utf8");
|
|
11132
|
+
if (!PROOF_SIGNATURE_PATTERN.test(presentedProof)) {
|
|
11133
|
+
return false;
|
|
11134
|
+
}
|
|
11135
|
+
return verify2(
|
|
11136
|
+
"sha256",
|
|
11137
|
+
data,
|
|
11138
|
+
{ key: publicKey, dsaEncoding: RAW_SIGNATURE_ENCODING },
|
|
11139
|
+
Buffer.from(presentedProof, "hex")
|
|
11140
|
+
);
|
|
11141
|
+
}
|
|
11142
|
+
function sha256Hex(bytes) {
|
|
11143
|
+
return createHash4("sha256").update(bytes).digest("hex");
|
|
11144
|
+
}
|
|
11145
|
+
|
|
11146
|
+
// src/server/client-proof/refusal.ts
|
|
11147
|
+
import { randomBytes } from "crypto";
|
|
11148
|
+
var HTTP_STATUS = {
|
|
11149
|
+
PROOF_INVALID: 401,
|
|
11150
|
+
PROOF_REPLAYED: 401,
|
|
11151
|
+
PROOF_EXPIRED: 401,
|
|
11152
|
+
SESSION_REVOKED: 401,
|
|
11153
|
+
PROFILE_REJECTED: 400,
|
|
11154
|
+
CONTRACT_UNSUPPORTED: 409
|
|
11155
|
+
};
|
|
11156
|
+
var ClientProofRefusal = class _ClientProofRefusal {
|
|
11157
|
+
constructor(code, message) {
|
|
11158
|
+
this.code = code;
|
|
11159
|
+
this.message = message;
|
|
11160
|
+
}
|
|
11161
|
+
get httpStatus() {
|
|
11162
|
+
return HTTP_STATUS[this.code];
|
|
11163
|
+
}
|
|
11164
|
+
/** The canonical bytes of `{"error":{"code":…,"message":…,"requestId":…}}`. */
|
|
11165
|
+
envelopeBytes(requestId) {
|
|
11166
|
+
const error = /* @__PURE__ */ new Map([
|
|
11167
|
+
["code", this.code],
|
|
11168
|
+
["message", this.message],
|
|
11169
|
+
["requestId", requestId]
|
|
11170
|
+
]);
|
|
11171
|
+
return encodeCanonicalJson(/* @__PURE__ */ new Map([["error", error]]));
|
|
11172
|
+
}
|
|
11173
|
+
/** Nothing request-derived reaches a log through this. */
|
|
11174
|
+
toString() {
|
|
11175
|
+
return `ClientProofRefusal(${this.code})`;
|
|
11176
|
+
}
|
|
11177
|
+
// ---- shape: what arrived is not the contract (rule 2) -------------------
|
|
11178
|
+
static unroutable() {
|
|
11179
|
+
return contractViolation("no operation in this contract answers that method and path");
|
|
11180
|
+
}
|
|
11181
|
+
static malformedHeaders() {
|
|
11182
|
+
return contractViolation("the request does not carry the contract header fields exactly once each");
|
|
11183
|
+
}
|
|
11184
|
+
static missingContentType() {
|
|
11185
|
+
return contractViolation("a request that carries a body must declare the contract content type");
|
|
11186
|
+
}
|
|
11187
|
+
static bodyTooLarge() {
|
|
11188
|
+
return contractViolation("the request body exceeds the size this server accepts");
|
|
11189
|
+
}
|
|
11190
|
+
/**
|
|
11191
|
+
* The body parsed but its bytes are not the canonical form of what it
|
|
11192
|
+
* parsed to. Not PROOF_INVALID even though it is discovered next to the
|
|
11193
|
+
* proof: the proof over these bytes verifies perfectly well, and an
|
|
11194
|
+
* auth-family answer would tell the client to re-handshake and send the
|
|
11195
|
+
* same non-canonical bytes again.
|
|
11196
|
+
*/
|
|
11197
|
+
static bodyNotCanonical() {
|
|
11198
|
+
return contractViolation("the request body is not the canonical JSON form of the value it encodes");
|
|
11199
|
+
}
|
|
11200
|
+
static bodyNotTheDeclaredType() {
|
|
11201
|
+
return contractViolation("the request body is not the request type this operation declares");
|
|
11202
|
+
}
|
|
11203
|
+
static sessionHeaderMisplaced() {
|
|
11204
|
+
return contractViolation("the session header is present exactly on the operations that require one");
|
|
11205
|
+
}
|
|
11206
|
+
static unprocessable() {
|
|
11207
|
+
return contractViolation("the request could not be processed");
|
|
11208
|
+
}
|
|
11209
|
+
// ---- the profile allowlist ----------------------------------------------
|
|
11210
|
+
static profileRejected() {
|
|
11211
|
+
return new _ClientProofRefusal("PROFILE_REJECTED", "the named auth profile is not on this contract's allowlist");
|
|
11212
|
+
}
|
|
11213
|
+
// ---- auth: a new session might clear it (rule 1) -------------------------
|
|
11214
|
+
static sessionRevoked() {
|
|
11215
|
+
return new _ClientProofRefusal("SESSION_REVOKED", "the key or session was revoked");
|
|
11216
|
+
}
|
|
11217
|
+
static proofExpired() {
|
|
11218
|
+
return new _ClientProofRefusal("PROOF_EXPIRED", "issuedAtMillis falls outside the replay window");
|
|
11219
|
+
}
|
|
11220
|
+
static proofReplayed() {
|
|
11221
|
+
return new _ClientProofRefusal("PROOF_REPLAYED", "the nonce was already used inside the replay window");
|
|
11222
|
+
}
|
|
11223
|
+
static proofInvalid() {
|
|
11224
|
+
return new _ClientProofRefusal("PROOF_INVALID", "the client proof did not verify");
|
|
11225
|
+
}
|
|
11226
|
+
};
|
|
11227
|
+
function contractViolation(message) {
|
|
11228
|
+
return new ClientProofRefusal("CONTRACT_UNSUPPORTED", message);
|
|
11229
|
+
}
|
|
11230
|
+
|
|
11231
|
+
// src/server/client-proof/admission.ts
|
|
11232
|
+
var CLIENT_PROOF_HEADERS = {
|
|
11233
|
+
profile: "x-spfn-auth-profile",
|
|
11234
|
+
clientId: "x-spfn-client-id",
|
|
11235
|
+
keyId: "x-spfn-key-id",
|
|
11236
|
+
nonce: "x-spfn-nonce",
|
|
11237
|
+
issuedAtMillis: "x-spfn-issued-at",
|
|
11238
|
+
proof: "x-spfn-proof",
|
|
11239
|
+
session: "x-spfn-session"
|
|
11240
|
+
};
|
|
11241
|
+
var CLIENT_PROOF_CONTENT_TYPE = "application/json";
|
|
11242
|
+
var INT64_MIN2 = -(2n ** 63n);
|
|
11243
|
+
var INT64_MAX2 = 2n ** 63n - 1n;
|
|
11244
|
+
function readCredentials(headers) {
|
|
11245
|
+
const profile = headers.get(CLIENT_PROOF_HEADERS.profile);
|
|
11246
|
+
const clientId = headers.get(CLIENT_PROOF_HEADERS.clientId);
|
|
11247
|
+
const keyId = headers.get(CLIENT_PROOF_HEADERS.keyId);
|
|
11248
|
+
const nonce = headers.get(CLIENT_PROOF_HEADERS.nonce);
|
|
11249
|
+
const issuedAtRaw = headers.get(CLIENT_PROOF_HEADERS.issuedAtMillis);
|
|
11250
|
+
const proof = headers.get(CLIENT_PROOF_HEADERS.proof);
|
|
11251
|
+
if (profile === null || clientId === null || keyId === null || nonce === null || issuedAtRaw === null || proof === null) {
|
|
11252
|
+
return null;
|
|
11253
|
+
}
|
|
11254
|
+
const issuedAtMillis = parseInt64(issuedAtRaw);
|
|
11255
|
+
if (issuedAtMillis === null) {
|
|
11256
|
+
return null;
|
|
11257
|
+
}
|
|
11258
|
+
return {
|
|
11259
|
+
profile,
|
|
11260
|
+
clientId,
|
|
11261
|
+
keyId,
|
|
11262
|
+
nonce,
|
|
11263
|
+
issuedAtMillis,
|
|
11264
|
+
proof,
|
|
11265
|
+
sessionId: headers.get(CLIENT_PROOF_HEADERS.session)
|
|
11266
|
+
};
|
|
11267
|
+
}
|
|
11268
|
+
function parseInt64(raw) {
|
|
11269
|
+
if (!/^[+-]?\d{1,19}$/.test(raw)) {
|
|
11270
|
+
return null;
|
|
11271
|
+
}
|
|
11272
|
+
const value = BigInt(raw);
|
|
11273
|
+
if (value < INT64_MIN2 || value > INT64_MAX2) {
|
|
11274
|
+
return null;
|
|
11275
|
+
}
|
|
11276
|
+
return value;
|
|
11277
|
+
}
|
|
11278
|
+
function isRequestContentType(value) {
|
|
11279
|
+
if (value === null) {
|
|
11280
|
+
return false;
|
|
11281
|
+
}
|
|
11282
|
+
return value.split(";")[0].trim().toLowerCase() === CLIENT_PROOF_CONTENT_TYPE;
|
|
11283
|
+
}
|
|
11284
|
+
|
|
11285
|
+
// src/server/client-proof/replay-store.ts
|
|
11286
|
+
import { getCache } from "@spfn/core/cache";
|
|
11287
|
+
function replayLedgerKey(clientId, nonce) {
|
|
11288
|
+
return JSON.stringify([clientId, nonce]);
|
|
11289
|
+
}
|
|
11290
|
+
var MemoryReplayLedger = class {
|
|
11291
|
+
/** replayLedgerKey(...) → the millis it was spent at. */
|
|
11292
|
+
spent = /* @__PURE__ */ new Map();
|
|
11293
|
+
isSpent(clientId, nonce) {
|
|
11294
|
+
return this.spent.has(replayLedgerKey(clientId, nonce));
|
|
11295
|
+
}
|
|
11296
|
+
/** Records the pair at `atMillis`; false when it was already spent. */
|
|
11297
|
+
spend(clientId, nonce, atMillis) {
|
|
11298
|
+
const key = replayLedgerKey(clientId, nonce);
|
|
11299
|
+
if (this.spent.has(key)) {
|
|
11300
|
+
return false;
|
|
11301
|
+
}
|
|
11302
|
+
this.spent.set(key, atMillis);
|
|
11303
|
+
return true;
|
|
11304
|
+
}
|
|
11305
|
+
/** Drops entries older than the window, judged against `nowMillis`. */
|
|
11306
|
+
prune(nowMillis, windowMillis) {
|
|
11307
|
+
for (const [key, spentAtMillis] of this.spent) {
|
|
11308
|
+
if (nowMillis - spentAtMillis > windowMillis) {
|
|
11309
|
+
this.spent.delete(key);
|
|
11310
|
+
}
|
|
11311
|
+
}
|
|
11312
|
+
}
|
|
11313
|
+
get size() {
|
|
11314
|
+
return this.spent.size;
|
|
11315
|
+
}
|
|
11316
|
+
clear() {
|
|
11317
|
+
this.spent.clear();
|
|
11318
|
+
}
|
|
11319
|
+
};
|
|
11320
|
+
var MemoryReplayStore = class {
|
|
11321
|
+
constructor(windowMillis = DEFAULT_REPLAY_WINDOW_MILLIS) {
|
|
11322
|
+
this.windowMillis = windowMillis;
|
|
11323
|
+
}
|
|
11324
|
+
ledger = new MemoryReplayLedger();
|
|
11325
|
+
async isSpent(clientId, nonce) {
|
|
11326
|
+
this.ledger.prune(Date.now(), this.windowMillis);
|
|
11327
|
+
return this.ledger.isSpent(clientId, nonce);
|
|
11328
|
+
}
|
|
11329
|
+
async spend(clientId, nonce) {
|
|
11330
|
+
const now = Date.now();
|
|
11331
|
+
this.ledger.prune(now, this.windowMillis);
|
|
11332
|
+
return this.ledger.spend(clientId, nonce, now);
|
|
11333
|
+
}
|
|
11334
|
+
};
|
|
11335
|
+
var configured = null;
|
|
11336
|
+
function getClientProofReplayStore() {
|
|
11337
|
+
configured ??= new MemoryReplayStore();
|
|
11338
|
+
return configured;
|
|
11339
|
+
}
|
|
11340
|
+
|
|
11341
|
+
// src/server/middleware/auth-profiles.ts
|
|
11342
|
+
function selectAuthProfile(c) {
|
|
11343
|
+
const profile = c.req.header(CLIENT_PROOF_HEADERS.profile);
|
|
11344
|
+
if (profile === void 0) {
|
|
11345
|
+
return null;
|
|
11346
|
+
}
|
|
11347
|
+
if (c.req.header("Authorization") !== void 0) {
|
|
11348
|
+
throw new BadRequestError2({
|
|
11349
|
+
message: "an auth profile and Bearer credentials must not be mixed in one request",
|
|
11350
|
+
details: { code: "PROFILE_REJECTED" }
|
|
11351
|
+
});
|
|
11352
|
+
}
|
|
11353
|
+
const verifier = AUTH_PROFILE_VERIFIERS.get(profile);
|
|
11354
|
+
if (verifier === void 0) {
|
|
11355
|
+
throw refusalError(ClientProofRefusal.profileRejected());
|
|
11356
|
+
}
|
|
11357
|
+
return verifier;
|
|
11358
|
+
}
|
|
11359
|
+
async function resolveAuthenticatedUser(userId) {
|
|
11360
|
+
const [result, locale] = await Promise.all([
|
|
11361
|
+
usersRepository2.findByIdWithRole(userId),
|
|
11362
|
+
userProfilesRepository2.findLocaleByUserId(userId)
|
|
11363
|
+
]);
|
|
11364
|
+
if (!result) {
|
|
11365
|
+
throw new UnauthorizedError({ message: "User not found" });
|
|
11366
|
+
}
|
|
11367
|
+
const { user, role } = result;
|
|
11368
|
+
if (user.status !== "active") {
|
|
11369
|
+
if (user.status === "pending_deletion") {
|
|
11370
|
+
const pending = await getPendingDeletionInfo2(user.id);
|
|
11371
|
+
throw new AccountPendingDeletionError3({ purgeScheduledAt: pending?.purgeScheduledAt.toISOString() });
|
|
11372
|
+
}
|
|
11373
|
+
throw new AccountDisabledError3({ status: user.status });
|
|
11374
|
+
}
|
|
11375
|
+
return { user, role: role?.name ?? null, locale };
|
|
11376
|
+
}
|
|
11377
|
+
function refusalError(refusal) {
|
|
11378
|
+
const data = { message: refusal.message, details: { code: refusal.code } };
|
|
11379
|
+
if (refusal.code === "PROFILE_REJECTED") {
|
|
11380
|
+
return new BadRequestError2(data);
|
|
11381
|
+
}
|
|
11382
|
+
if (refusal.code === "CONTRACT_UNSUPPORTED") {
|
|
11383
|
+
return new ConflictError2(data);
|
|
11384
|
+
}
|
|
11385
|
+
return new UnauthorizedError(data);
|
|
11386
|
+
}
|
|
11387
|
+
async function failClosed(operation) {
|
|
11388
|
+
try {
|
|
11389
|
+
return await operation();
|
|
11390
|
+
} catch (err) {
|
|
11391
|
+
authLogger2.middleware.error("client-proof replay store unavailable \u2014 refusing (fail-closed)", err);
|
|
11392
|
+
throw new ServiceUnavailableError({ message: "Authentication is temporarily unavailable" });
|
|
11393
|
+
}
|
|
11394
|
+
}
|
|
11395
|
+
async function verifyClientProofProfile(c) {
|
|
11396
|
+
const headers = c.req.raw.headers;
|
|
11397
|
+
const credentials = readCredentials(headers);
|
|
11398
|
+
if (credentials === null) {
|
|
11399
|
+
throw refusalError(ClientProofRefusal.malformedHeaders());
|
|
11400
|
+
}
|
|
11401
|
+
if (credentials.profile !== CLIENT_PROOF_PROFILE) {
|
|
11402
|
+
throw refusalError(ClientProofRefusal.profileRejected());
|
|
11403
|
+
}
|
|
11404
|
+
if (credentials.sessionId !== null) {
|
|
11405
|
+
throw refusalError(ClientProofRefusal.sessionHeaderMisplaced());
|
|
11406
|
+
}
|
|
11407
|
+
const body = new Uint8Array(await c.req.arrayBuffer());
|
|
11408
|
+
const proofInput = {
|
|
11409
|
+
method: c.req.method,
|
|
11410
|
+
path: c.req.path,
|
|
11411
|
+
clientId: credentials.clientId,
|
|
11412
|
+
keyId: credentials.keyId,
|
|
11413
|
+
nonce: credentials.nonce,
|
|
11414
|
+
issuedAtMillis: credentials.issuedAtMillis,
|
|
11415
|
+
bodySha256: admitBodySha256(headers, body)
|
|
11416
|
+
};
|
|
11417
|
+
const keyRecord = await keysRepository2.findByKeyId(credentials.keyId);
|
|
11418
|
+
if (keyRecord !== null) {
|
|
11419
|
+
if (!keyRecord.isActive) {
|
|
11420
|
+
throw refusalError(ClientProofRefusal.sessionRevoked());
|
|
11421
|
+
}
|
|
11422
|
+
if (keyRecord.expiresAt && /* @__PURE__ */ new Date() > keyRecord.expiresAt) {
|
|
11423
|
+
throw refusalError(ClientProofRefusal.sessionRevoked());
|
|
11424
|
+
}
|
|
11425
|
+
}
|
|
11426
|
+
const age = Date.now() - Number(credentials.issuedAtMillis);
|
|
11427
|
+
if (age < 0 || age > DEFAULT_REPLAY_WINDOW_MILLIS) {
|
|
11428
|
+
throw refusalError(ClientProofRefusal.proofExpired());
|
|
11429
|
+
}
|
|
11430
|
+
const replayStore = getClientProofReplayStore();
|
|
11431
|
+
if (await failClosed(() => replayStore.isSpent(credentials.clientId, credentials.nonce))) {
|
|
11432
|
+
throw refusalError(ClientProofRefusal.proofReplayed());
|
|
11433
|
+
}
|
|
11434
|
+
if (keyRecord === null) {
|
|
11435
|
+
throw refusalError(ClientProofRefusal.proofInvalid());
|
|
11436
|
+
}
|
|
11437
|
+
if (credentials.clientId !== String(keyRecord.userId)) {
|
|
11438
|
+
throw refusalError(ClientProofRefusal.proofInvalid());
|
|
11439
|
+
}
|
|
11440
|
+
verifyProofOrThrow(proofInput, credentials, keyRecord.publicKey);
|
|
11441
|
+
if (!await failClosed(() => replayStore.spend(credentials.clientId, credentials.nonce))) {
|
|
11442
|
+
throw refusalError(ClientProofRefusal.proofReplayed());
|
|
11443
|
+
}
|
|
11444
|
+
const { user, role, locale } = await resolveAuthenticatedUser(keyRecord.userId);
|
|
11445
|
+
keysRepository2.updateLastUsedById(keyRecord.id).catch((err) => authLogger2.middleware.error("Failed to update lastUsedAt", err));
|
|
11446
|
+
authLogger2.middleware.info("API access", {
|
|
11447
|
+
userId: user.id,
|
|
11448
|
+
email: user.email,
|
|
11449
|
+
keyId: credentials.keyId,
|
|
11450
|
+
scheme: CLIENT_PROOF_PROFILE,
|
|
11451
|
+
method: c.req.method,
|
|
11452
|
+
path: c.req.path,
|
|
11453
|
+
ip: c.req.header("x-forwarded-for") || c.req.header("x-real-ip"),
|
|
11454
|
+
userAgent: c.req.header("user-agent")
|
|
11455
|
+
});
|
|
11456
|
+
return {
|
|
11457
|
+
user,
|
|
11458
|
+
userId: String(user.id),
|
|
11459
|
+
keyId: credentials.keyId,
|
|
11460
|
+
role,
|
|
11461
|
+
locale,
|
|
11462
|
+
scheme: "clientProofV1"
|
|
11463
|
+
};
|
|
11464
|
+
}
|
|
11465
|
+
function admitBodySha256(headers, body) {
|
|
11466
|
+
if (body.length === 0) {
|
|
11467
|
+
return ABSENT_BODY_SHA256;
|
|
11468
|
+
}
|
|
11469
|
+
if (!isRequestContentType(headers.get("content-type"))) {
|
|
11470
|
+
throw refusalError(ClientProofRefusal.missingContentType());
|
|
11471
|
+
}
|
|
11472
|
+
let value;
|
|
11473
|
+
try {
|
|
11474
|
+
value = parseCanonicalJson(body);
|
|
11475
|
+
} catch {
|
|
11476
|
+
throw refusalError(ClientProofRefusal.bodyNotCanonical());
|
|
11477
|
+
}
|
|
11478
|
+
if (!isCanonicalBytes(body, value)) {
|
|
11479
|
+
throw refusalError(ClientProofRefusal.bodyNotCanonical());
|
|
11480
|
+
}
|
|
11481
|
+
return sha256Hex(body);
|
|
11482
|
+
}
|
|
11483
|
+
function verifyProofOrThrow(proofInput, credentials, publicKeySpkiDerBase64) {
|
|
11484
|
+
let verified;
|
|
11485
|
+
try {
|
|
11486
|
+
const publicKey = parseClientProofPublicKey(publicKeySpkiDerBase64);
|
|
11487
|
+
verified = verifyClientProof(proofInput, credentials.proof, publicKey);
|
|
11488
|
+
} catch (err) {
|
|
11489
|
+
if (err instanceof ProofInputError) {
|
|
11490
|
+
throw refusalError(ClientProofRefusal.unprocessable());
|
|
11491
|
+
}
|
|
11492
|
+
throw refusalError(ClientProofRefusal.proofInvalid());
|
|
11493
|
+
}
|
|
11494
|
+
if (!verified) {
|
|
11495
|
+
throw refusalError(ClientProofRefusal.proofInvalid());
|
|
11496
|
+
}
|
|
11497
|
+
}
|
|
11498
|
+
var AUTH_PROFILE_VERIFIERS = /* @__PURE__ */ new Map([
|
|
11499
|
+
[CLIENT_PROOF_PROFILE, { verify: verifyClientProofProfile }]
|
|
11500
|
+
]);
|
|
11501
|
+
|
|
11502
|
+
// src/server/middleware/authenticate.ts
|
|
10697
11503
|
var authenticate = defineMiddleware("auth", async (c, next) => {
|
|
11504
|
+
const profileVerifier = selectAuthProfile(c);
|
|
11505
|
+
if (profileVerifier !== null) {
|
|
11506
|
+
c.set("auth", await profileVerifier.verify(c));
|
|
11507
|
+
await next();
|
|
11508
|
+
return;
|
|
11509
|
+
}
|
|
10698
11510
|
const authHeader = c.req.header("Authorization");
|
|
10699
11511
|
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
10700
|
-
|
|
11512
|
+
authLogger3.middleware.error("Missing or invalid authorization header. If using Next.js API routes, ensure you have imported '@spfn/auth/nextjs/api' in your API route handler (e.g., src/app/api/actions/[[...path]]/route.ts) to enable automatic authentication header forwarding from client to backend.", {
|
|
10701
11513
|
headers: c.req.header(),
|
|
10702
11514
|
path: c.req.path
|
|
10703
11515
|
});
|
|
10704
|
-
throw new
|
|
11516
|
+
throw new UnauthorizedError2({ message: "Authentication header missing or invalid: Bearer {token}" });
|
|
10705
11517
|
}
|
|
10706
11518
|
const token = authHeader.substring(7);
|
|
10707
11519
|
const decoded = decodeToken2(token);
|
|
10708
11520
|
if (!decoded || !decoded.keyId) {
|
|
10709
|
-
throw new
|
|
11521
|
+
throw new UnauthorizedError2({ message: "Invalid token: missing keyId" });
|
|
10710
11522
|
}
|
|
10711
11523
|
const keyId = decoded.keyId;
|
|
10712
|
-
const keyRecord = await
|
|
11524
|
+
const keyRecord = await keysRepository3.findActiveByKeyId(keyId);
|
|
10713
11525
|
if (!keyRecord) {
|
|
10714
|
-
throw new
|
|
11526
|
+
throw new UnauthorizedError2({ message: "Invalid or revoked key" });
|
|
10715
11527
|
}
|
|
10716
11528
|
if (keyRecord.expiresAt && /* @__PURE__ */ new Date() > keyRecord.expiresAt) {
|
|
10717
11529
|
throw new KeyExpiredError();
|
|
@@ -10732,34 +11544,21 @@ var authenticate = defineMiddleware("auth", async (c, next) => {
|
|
|
10732
11544
|
throw new InvalidTokenError({ message: "Invalid token signature" });
|
|
10733
11545
|
}
|
|
10734
11546
|
}
|
|
10735
|
-
throw new
|
|
10736
|
-
}
|
|
10737
|
-
const [result, locale] = await Promise.all([
|
|
10738
|
-
usersRepository2.findByIdWithRole(keyRecord.userId),
|
|
10739
|
-
userProfilesRepository2.findLocaleByUserId(keyRecord.userId)
|
|
10740
|
-
]);
|
|
10741
|
-
if (!result) {
|
|
10742
|
-
throw new UnauthorizedError({ message: "User not found" });
|
|
10743
|
-
}
|
|
10744
|
-
const { user, role } = result;
|
|
10745
|
-
if (user.status !== "active") {
|
|
10746
|
-
if (user.status === "pending_deletion") {
|
|
10747
|
-
const pending = await getPendingDeletionInfo2(user.id);
|
|
10748
|
-
throw new AccountPendingDeletionError3({ purgeScheduledAt: pending?.purgeScheduledAt.toISOString() });
|
|
10749
|
-
}
|
|
10750
|
-
throw new AccountDisabledError3({ status: user.status });
|
|
11547
|
+
throw new UnauthorizedError2({ message: "Authentication failed" });
|
|
10751
11548
|
}
|
|
10752
|
-
|
|
11549
|
+
const { user, role, locale } = await resolveAuthenticatedUser(keyRecord.userId);
|
|
11550
|
+
keysRepository3.updateLastUsedById(keyRecord.id).catch((err) => authLogger3.middleware.error("Failed to update lastUsedAt", err));
|
|
10753
11551
|
c.set("auth", {
|
|
10754
11552
|
user,
|
|
10755
11553
|
userId: String(user.id),
|
|
10756
11554
|
keyId,
|
|
10757
|
-
role
|
|
10758
|
-
locale
|
|
11555
|
+
role,
|
|
11556
|
+
locale,
|
|
11557
|
+
scheme: "bearer"
|
|
10759
11558
|
});
|
|
10760
11559
|
const method = c.req.method;
|
|
10761
11560
|
const path = c.req.path;
|
|
10762
|
-
|
|
11561
|
+
authLogger3.middleware.info("API access", {
|
|
10763
11562
|
userId: user.id,
|
|
10764
11563
|
email: user.email,
|
|
10765
11564
|
keyId,
|
|
@@ -10771,6 +11570,12 @@ var authenticate = defineMiddleware("auth", async (c, next) => {
|
|
|
10771
11570
|
await next();
|
|
10772
11571
|
});
|
|
10773
11572
|
var optionalAuth = defineMiddleware("optionalAuth", async (c, next) => {
|
|
11573
|
+
const profileVerifier = selectAuthProfile(c);
|
|
11574
|
+
if (profileVerifier !== null) {
|
|
11575
|
+
c.set("auth", await profileVerifier.verify(c));
|
|
11576
|
+
await next();
|
|
11577
|
+
return;
|
|
11578
|
+
}
|
|
10774
11579
|
const authHeader = c.req.header("Authorization");
|
|
10775
11580
|
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
10776
11581
|
await next();
|
|
@@ -10784,7 +11589,7 @@ var optionalAuth = defineMiddleware("optionalAuth", async (c, next) => {
|
|
|
10784
11589
|
return;
|
|
10785
11590
|
}
|
|
10786
11591
|
const keyId = decoded.keyId;
|
|
10787
|
-
const keyRecord = await
|
|
11592
|
+
const keyRecord = await keysRepository3.findActiveByKeyId(keyId);
|
|
10788
11593
|
if (!keyRecord) {
|
|
10789
11594
|
await next();
|
|
10790
11595
|
return;
|
|
@@ -10799,21 +11604,22 @@ var optionalAuth = defineMiddleware("optionalAuth", async (c, next) => {
|
|
|
10799
11604
|
keyRecord.algorithm
|
|
10800
11605
|
);
|
|
10801
11606
|
const [result, locale] = await Promise.all([
|
|
10802
|
-
|
|
10803
|
-
|
|
11607
|
+
usersRepository3.findByIdWithRole(keyRecord.userId),
|
|
11608
|
+
userProfilesRepository3.findLocaleByUserId(keyRecord.userId)
|
|
10804
11609
|
]);
|
|
10805
11610
|
if (!result || result.user.status !== "active") {
|
|
10806
11611
|
await next();
|
|
10807
11612
|
return;
|
|
10808
11613
|
}
|
|
10809
11614
|
const { user, role } = result;
|
|
10810
|
-
|
|
11615
|
+
keysRepository3.updateLastUsedById(keyRecord.id).catch((err) => authLogger3.middleware.error("Failed to update lastUsedAt", err));
|
|
10811
11616
|
c.set("auth", {
|
|
10812
11617
|
user,
|
|
10813
11618
|
userId: String(user.id),
|
|
10814
11619
|
keyId,
|
|
10815
11620
|
role: role?.name ?? null,
|
|
10816
|
-
locale
|
|
11621
|
+
locale,
|
|
11622
|
+
scheme: "bearer"
|
|
10817
11623
|
});
|
|
10818
11624
|
} catch {
|
|
10819
11625
|
}
|
|
@@ -10824,13 +11630,13 @@ var optionalAuth = defineMiddleware("optionalAuth", async (c, next) => {
|
|
|
10824
11630
|
import { defineMiddleware as defineMiddleware2 } from "@spfn/core/route";
|
|
10825
11631
|
import { ForbiddenError as ForbiddenError2 } from "@spfn/core/errors";
|
|
10826
11632
|
import { InsufficientPermissionsError } from "@spfn/auth/errors";
|
|
10827
|
-
import { getAuth as getAuth2, hasAllPermissions as hasAllPermissions2, hasAnyPermission as hasAnyPermission2, authLogger as
|
|
11633
|
+
import { getAuth as getAuth2, hasAllPermissions as hasAllPermissions2, hasAnyPermission as hasAnyPermission2, authLogger as authLogger4 } from "@spfn/auth/server";
|
|
10828
11634
|
var requirePermissions = defineMiddleware2(
|
|
10829
11635
|
"permission",
|
|
10830
11636
|
(...permissionNames) => async (c, next) => {
|
|
10831
11637
|
const auth = getAuth2(c);
|
|
10832
11638
|
if (!auth) {
|
|
10833
|
-
|
|
11639
|
+
authLogger4.middleware.warn("Permission check failed: not authenticated", {
|
|
10834
11640
|
permissions: permissionNames,
|
|
10835
11641
|
path: c.req.path
|
|
10836
11642
|
});
|
|
@@ -10839,14 +11645,14 @@ var requirePermissions = defineMiddleware2(
|
|
|
10839
11645
|
const { userId } = auth;
|
|
10840
11646
|
const allowed = await hasAllPermissions2(userId, permissionNames);
|
|
10841
11647
|
if (!allowed) {
|
|
10842
|
-
|
|
11648
|
+
authLogger4.middleware.warn("Permission check failed", {
|
|
10843
11649
|
userId,
|
|
10844
11650
|
requiredPermissions: permissionNames,
|
|
10845
11651
|
path: c.req.path
|
|
10846
11652
|
});
|
|
10847
11653
|
throw new InsufficientPermissionsError({ requiredPermissions: permissionNames });
|
|
10848
11654
|
}
|
|
10849
|
-
|
|
11655
|
+
authLogger4.middleware.debug("Permission check passed", {
|
|
10850
11656
|
userId,
|
|
10851
11657
|
permissions: permissionNames
|
|
10852
11658
|
});
|
|
@@ -10858,7 +11664,7 @@ var requireAnyPermission = defineMiddleware2(
|
|
|
10858
11664
|
(...permissionNames) => async (c, next) => {
|
|
10859
11665
|
const auth = getAuth2(c);
|
|
10860
11666
|
if (!auth) {
|
|
10861
|
-
|
|
11667
|
+
authLogger4.middleware.warn("Any permission check failed: not authenticated", {
|
|
10862
11668
|
permissions: permissionNames,
|
|
10863
11669
|
path: c.req.path
|
|
10864
11670
|
});
|
|
@@ -10867,14 +11673,14 @@ var requireAnyPermission = defineMiddleware2(
|
|
|
10867
11673
|
const { userId } = auth;
|
|
10868
11674
|
const allowed = await hasAnyPermission2(userId, permissionNames);
|
|
10869
11675
|
if (!allowed) {
|
|
10870
|
-
|
|
11676
|
+
authLogger4.middleware.warn("Any permission check failed", {
|
|
10871
11677
|
userId,
|
|
10872
11678
|
requiredAnyOf: permissionNames,
|
|
10873
11679
|
path: c.req.path
|
|
10874
11680
|
});
|
|
10875
11681
|
throw new InsufficientPermissionsError({ requiredPermissions: permissionNames });
|
|
10876
11682
|
}
|
|
10877
|
-
|
|
11683
|
+
authLogger4.middleware.debug("Any permission check passed", {
|
|
10878
11684
|
userId,
|
|
10879
11685
|
permissions: permissionNames
|
|
10880
11686
|
});
|
|
@@ -10884,7 +11690,7 @@ var requireAnyPermission = defineMiddleware2(
|
|
|
10884
11690
|
|
|
10885
11691
|
// src/server/middleware/require-role.ts
|
|
10886
11692
|
import { defineMiddleware as defineMiddleware3 } from "@spfn/core/route";
|
|
10887
|
-
import { getAuth as getAuth3, authLogger as
|
|
11693
|
+
import { getAuth as getAuth3, authLogger as authLogger5 } from "@spfn/auth/server";
|
|
10888
11694
|
import { ForbiddenError as ForbiddenError3 } from "@spfn/core/errors";
|
|
10889
11695
|
import { InsufficientRoleError } from "@spfn/auth/errors";
|
|
10890
11696
|
var requireRole = defineMiddleware3(
|
|
@@ -10892,7 +11698,7 @@ var requireRole = defineMiddleware3(
|
|
|
10892
11698
|
(...roleNames) => async (c, next) => {
|
|
10893
11699
|
const auth = getAuth3(c);
|
|
10894
11700
|
if (!auth) {
|
|
10895
|
-
|
|
11701
|
+
authLogger5.middleware.warn("Role check failed: not authenticated", {
|
|
10896
11702
|
roles: roleNames,
|
|
10897
11703
|
path: c.req.path
|
|
10898
11704
|
});
|
|
@@ -10900,7 +11706,7 @@ var requireRole = defineMiddleware3(
|
|
|
10900
11706
|
}
|
|
10901
11707
|
const { userId, role: userRole } = auth;
|
|
10902
11708
|
if (!userRole || !roleNames.includes(userRole)) {
|
|
10903
|
-
|
|
11709
|
+
authLogger5.middleware.warn("Role check failed", {
|
|
10904
11710
|
userId,
|
|
10905
11711
|
userRole,
|
|
10906
11712
|
requiredRoles: roleNames,
|
|
@@ -10908,7 +11714,7 @@ var requireRole = defineMiddleware3(
|
|
|
10908
11714
|
});
|
|
10909
11715
|
throw new InsufficientRoleError({ requiredRoles: roleNames });
|
|
10910
11716
|
}
|
|
10911
|
-
|
|
11717
|
+
authLogger5.middleware.debug("Role check passed", {
|
|
10912
11718
|
userId,
|
|
10913
11719
|
userRole,
|
|
10914
11720
|
roles: roleNames
|
|
@@ -10919,7 +11725,7 @@ var requireRole = defineMiddleware3(
|
|
|
10919
11725
|
|
|
10920
11726
|
// src/server/middleware/role-guard.ts
|
|
10921
11727
|
import { defineMiddleware as defineMiddleware4 } from "@spfn/core/route";
|
|
10922
|
-
import { getAuth as getAuth4, authLogger as
|
|
11728
|
+
import { getAuth as getAuth4, authLogger as authLogger6 } from "@spfn/auth/server";
|
|
10923
11729
|
import { ForbiddenError as ForbiddenError4 } from "@spfn/core/errors";
|
|
10924
11730
|
import { InsufficientRoleError as InsufficientRoleError2 } from "@spfn/auth/errors";
|
|
10925
11731
|
var roleGuard = defineMiddleware4(
|
|
@@ -10931,7 +11737,7 @@ var roleGuard = defineMiddleware4(
|
|
|
10931
11737
|
}
|
|
10932
11738
|
const auth = getAuth4(c);
|
|
10933
11739
|
if (!auth) {
|
|
10934
|
-
|
|
11740
|
+
authLogger6.middleware.warn("Role guard failed: not authenticated", {
|
|
10935
11741
|
path: c.req.path
|
|
10936
11742
|
});
|
|
10937
11743
|
throw new ForbiddenError4({ message: "Authentication required" });
|
|
@@ -10939,7 +11745,7 @@ var roleGuard = defineMiddleware4(
|
|
|
10939
11745
|
const { userId, role: userRole } = auth;
|
|
10940
11746
|
if (deny && deny.length > 0) {
|
|
10941
11747
|
if (userRole && deny.includes(userRole)) {
|
|
10942
|
-
|
|
11748
|
+
authLogger6.middleware.warn("Role guard denied", {
|
|
10943
11749
|
userId,
|
|
10944
11750
|
userRole,
|
|
10945
11751
|
deniedRoles: deny,
|
|
@@ -10950,7 +11756,7 @@ var roleGuard = defineMiddleware4(
|
|
|
10950
11756
|
}
|
|
10951
11757
|
if (allow && allow.length > 0) {
|
|
10952
11758
|
if (!userRole || !allow.includes(userRole)) {
|
|
10953
|
-
|
|
11759
|
+
authLogger6.middleware.warn("Role guard failed: role not allowed", {
|
|
10954
11760
|
userId,
|
|
10955
11761
|
userRole,
|
|
10956
11762
|
allowedRoles: allow,
|
|
@@ -10959,7 +11765,7 @@ var roleGuard = defineMiddleware4(
|
|
|
10959
11765
|
throw new InsufficientRoleError2({ requiredRoles: allow });
|
|
10960
11766
|
}
|
|
10961
11767
|
}
|
|
10962
|
-
|
|
11768
|
+
authLogger6.middleware.debug("Role guard passed", {
|
|
10963
11769
|
userId,
|
|
10964
11770
|
userRole,
|
|
10965
11771
|
allow,
|
|
@@ -10971,27 +11777,27 @@ var roleGuard = defineMiddleware4(
|
|
|
10971
11777
|
|
|
10972
11778
|
// src/server/middleware/one-time-token-auth.ts
|
|
10973
11779
|
import { defineMiddleware as defineMiddleware5 } from "@spfn/core/route";
|
|
10974
|
-
import { UnauthorizedError as
|
|
10975
|
-
import { usersRepository as
|
|
11780
|
+
import { UnauthorizedError as UnauthorizedError3 } from "@spfn/core/errors";
|
|
11781
|
+
import { usersRepository as usersRepository4, userProfilesRepository as userProfilesRepository4 } from "@spfn/auth/server";
|
|
10976
11782
|
var oneTimeTokenAuth = defineMiddleware5("oneTimeTokenAuth", async (c, next) => {
|
|
10977
11783
|
const token = c.req.query("token") ?? extractOTTHeader(c.req.header("Authorization"));
|
|
10978
11784
|
if (!token) {
|
|
10979
|
-
throw new
|
|
11785
|
+
throw new UnauthorizedError3({ message: "One-time token required: ?token=xxx or Authorization: OTT xxx" });
|
|
10980
11786
|
}
|
|
10981
11787
|
const userId = await verifyOneTimeTokenService(token);
|
|
10982
11788
|
if (!userId) {
|
|
10983
|
-
throw new
|
|
11789
|
+
throw new UnauthorizedError3({ message: "Invalid or expired one-time token" });
|
|
10984
11790
|
}
|
|
10985
11791
|
const [result, locale] = await Promise.all([
|
|
10986
|
-
|
|
10987
|
-
|
|
11792
|
+
usersRepository4.findByIdWithRole(Number(userId)),
|
|
11793
|
+
userProfilesRepository4.findLocaleByUserId(Number(userId))
|
|
10988
11794
|
]);
|
|
10989
11795
|
if (!result) {
|
|
10990
|
-
throw new
|
|
11796
|
+
throw new UnauthorizedError3({ message: "User not found" });
|
|
10991
11797
|
}
|
|
10992
11798
|
const { user, role } = result;
|
|
10993
11799
|
if (user.status !== "active") {
|
|
10994
|
-
throw new
|
|
11800
|
+
throw new UnauthorizedError3({ message: "Account is not active" });
|
|
10995
11801
|
}
|
|
10996
11802
|
c.set("auth", {
|
|
10997
11803
|
user,
|
|
@@ -10999,7 +11805,8 @@ var oneTimeTokenAuth = defineMiddleware5("oneTimeTokenAuth", async (c, next) =>
|
|
|
10999
11805
|
keyId: "",
|
|
11000
11806
|
// No key involved in OTT auth
|
|
11001
11807
|
role: role?.name ?? null,
|
|
11002
|
-
locale
|
|
11808
|
+
locale,
|
|
11809
|
+
scheme: "oneTimeToken"
|
|
11003
11810
|
});
|
|
11004
11811
|
await next();
|
|
11005
11812
|
}, { skips: ["auth"] });
|
|
@@ -12476,6 +13283,7 @@ export {
|
|
|
12476
13283
|
requirePermissions,
|
|
12477
13284
|
requireRole,
|
|
12478
13285
|
resendInvitation,
|
|
13286
|
+
resolveAuthenticatedUser,
|
|
12479
13287
|
revokeKeyService,
|
|
12480
13288
|
roleGuard,
|
|
12481
13289
|
rolePermissions,
|
|
@@ -12485,6 +13293,7 @@ export {
|
|
|
12485
13293
|
rotateKeyService,
|
|
12486
13294
|
runBeforeRegister,
|
|
12487
13295
|
sealSession,
|
|
13296
|
+
selectAuthProfile,
|
|
12488
13297
|
sendVerificationCodeService,
|
|
12489
13298
|
setRolePermissions,
|
|
12490
13299
|
shouldRefreshSession,
|