4 Commits

Author SHA1 Message Date
jkunz 2ad751ecba v1.13.0
Docker (tags) / security (push) Failing after 0s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2025-12-15 19:45:57 +00:00
jkunz a24b0d8be7 feat(oidc): feat(oidc): add OIDC provider (OidcManager, endpoints, and interfaces) 2025-12-15 19:45:57 +00:00
jkunz 02c700e44d v1.12.1
Docker (tags) / security (push) Failing after 0s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2025-12-15 19:17:12 +00:00
jkunz e9f1b5dac9 fix(dependencies): fix(deps): bump @uptime.link/webwidget to ^1.2.6 2025-12-15 19:17:12 +00:00
10 changed files with 1185 additions and 255 deletions
+14
View File
@@ -1,5 +1,19 @@
# Changelog
## 2025-12-15 - 1.13.0 - feat(oidc)
feat(oidc): add OIDC provider (OidcManager, endpoints, and interfaces)
- Add OidcManager class implementing OpenID Connect / OAuth2 server functionality (authorization codes, access/refresh tokens, user consents, PKCE support, JWKS, ID token generation, token revocation, cleanup task).
- Expose OIDC endpoints on the website server: /.well-known/openid-configuration, /.well-known/jwks.json, /oauth/authorize, /oauth/token, /oauth/userinfo (GET/POST), and /oauth/revoke.
- Integrate OidcManager into Reception: add oidcManager property and instantiate it from ts/index.ts so routes can reference it.
- Add TypeScript interfaces for OIDC data structures (ts_interfaces/data/loint-reception.oidc.ts) and export them from the data index.
## 2025-12-15 - 1.12.1 - fix(dependencies)
fix(deps): bump @uptime.link/webwidget to ^1.2.6
- Updated dependency @uptime.link/webwidget from ^1.2.5 to ^1.2.6 in package.json
- No other files changed; this is a dependency patch update
## 2025-12-15 - 1.12.0 - feat(interfaces)
Add JWT public-key and blocklist request interfaces, publish ordering files, and update dependencies
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@idp.global/idp.global",
"version": "1.12.0",
"version": "1.13.0",
"description": "An identity provider software managing user authentications, registrations, and sessions.",
"main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts",
@@ -50,7 +50,7 @@
"@push.rocks/webstore": "^2.0.20",
"@serve.zone/platformclient": "^1.1.2",
"@tsclass/tsclass": "^9.3.0",
"@uptime.link/webwidget": "^1.2.5"
"@uptime.link/webwidget": "^1.2.6"
},
"devDependencies": {
"@git.zone/tsbuild": "^4.0.2",
+171 -250
View File
@@ -111,8 +111,8 @@ importers:
specifier: ^9.3.0
version: 9.3.0
'@uptime.link/webwidget':
specifier: ^1.2.5
version: 1.2.5(@tiptap/pm@2.27.1)
specifier: ^1.2.6
version: 1.2.6(@tiptap/pm@2.27.1)
devDependencies:
'@git.zone/tsbuild':
specifier: ^4.0.2
@@ -131,7 +131,7 @@ importers:
version: 5.0.2
'@types/node':
specifier: ^24.10.1
version: 24.10.1
version: 24.10.4
packages:
@@ -332,9 +332,6 @@ packages:
'@cfworker/json-schema@4.1.1':
resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==}
'@cloudflare/workers-types@4.20251202.0':
resolution: {integrity: sha512-Q7m1Ivu2fbKalOPm00KLpu6GfRaq4TlrPknqugvZgp/gDH96OYKINO4x7jvCIBvCz/aK9vVoOj8tlbSQBervVA==}
'@cloudflare/workers-types@4.20251213.0':
resolution: {integrity: sha512-PJAGdKfU7hs39C2YOFNLTdrfdqG6rbaVj5UuI306zS+TPokiskRLEgUXKqS6avN9Uu9Nyuf2a0hqoumLQCnJlQ==}
@@ -350,15 +347,9 @@ packages:
'@consent.software/webclient@1.1.0':
resolution: {integrity: sha512-VX7e8ygZwgU8WEzn22fdvvEytLYl4kfp/u40GusaBU4iFtjrCY2hxDy9Z1FTKicpGcRxf3t13lM0Jaugq7Jj/w==}
'@design.estate/dees-catalog@2.0.7':
resolution: {integrity: sha512-rshv71LqA2PXaEEf6C1/hv6Yu2ovRuWaZhdnUznCDpjdYgxBq7PHkiHCNvg/m6wJ9Ue/03HcuuPqtj2bksgAag==}
'@design.estate/dees-catalog@3.3.1':
resolution: {integrity: sha512-QNIjAElIMm04Jz7VZTY/F1NjBIXCEYJ0VGK/wHE8ppzsSpSw93uLV47GFyAoEnwFp665IJpfS4HzRh7epwIArA==}
'@design.estate/dees-comms@1.0.27':
resolution: {integrity: sha512-GvzTUwkV442LD60T08iqSoqvhA02Mou5lFvvqBPc4yBUiU7cZISqBx+76xvMgMIEI9Dx9JfTl4/2nW8MoVAanw==}
'@design.estate/dees-comms@1.0.30':
resolution: {integrity: sha512-KchMlklJfKAjQiJiR0xmofXtQ27VgZtBIxcMwPE9d+h3jJRv+lPZxzBQVOM0eyM0uS44S5vJMZ11IeV4uDXSHg==}
@@ -368,9 +359,6 @@ packages:
'@design.estate/dees-element@2.1.3':
resolution: {integrity: sha512-TjXWxVcdSPaT1IOk31ckfxvAZnJLuTxhFGsNCKoh63/UE2FVf6slp8//UFvN+ADigiA9ZsY0azkY99XbJCwDDA==}
'@design.estate/dees-wcctools@1.2.1':
resolution: {integrity: sha512-ESFas1MPPwDUcXRssyHRsc63XPTBJSTBA+5RhYXDZx8mbV6HxEKiJR8Oz1Mv7DBdW+ZSuUTD/fA6Aa/fCxGYTQ==}
'@design.estate/dees-wcctools@2.0.1':
resolution: {integrity: sha512-1DaQtvoMmD+uH9cjSrL4szk7h0nbBlT/ZBmz+qvWCOqzZXE3wPOAdgASZ73NeQlehLx4KGbfJTCG15DSB0W3LQ==}
@@ -681,71 +669,71 @@ packages:
'@module-federation/webpack-bundler-runtime@0.21.6':
resolution: {integrity: sha512-7zIp3LrcWbhGuFDTUMLJ2FJvcwjlddqhWGxi/MW3ur1a+HaO8v5tF2nl+vElKmbG1DFLU/52l3PElVcWf/YcsQ==}
'@mongodb-js/saslprep@1.3.2':
resolution: {integrity: sha512-QgA5AySqB27cGTXBFmnpifAi7HxoGUeezwo6p9dI03MuDB6Pp33zgclqVb6oVK3j6I9Vesg0+oojW2XxB59SGg==}
'@mongodb-js/saslprep@1.4.0':
resolution: {integrity: sha512-ZHzx7Z3rdlWL1mECydvpryWN/ETXJiCxdgQKTAH+djzIPe77HdnSizKBDi1TVDXZjXyOj2IqEG/vPw71ULF06w==}
'@napi-rs/canvas-android-arm64@0.1.83':
resolution: {integrity: sha512-TbKM2fh9zXjqFIU8bgMfzG7rkrIYdLKMafgPhFoPwKrpWk1glGbWP7LEu8Y/WrMDqTGFdRqUmuX89yQEzZbkiw==}
'@napi-rs/canvas-android-arm64@0.1.84':
resolution: {integrity: sha512-pdvuqvj3qtwVryqgpAGornJLV6Ezpk39V6wT4JCnRVGy8I3Tk1au8qOalFGrx/r0Ig87hWslysPpHBxVpBMIww==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
'@napi-rs/canvas-darwin-arm64@0.1.83':
resolution: {integrity: sha512-gp8IDVUloPUmkepHly4xRUOfUJSFNvA4jR7ZRF5nk3YcGzegSFGeICiT4PnYyPgSKEhYAFe1Y2XNy0Mp6Tu8mQ==}
'@napi-rs/canvas-darwin-arm64@0.1.84':
resolution: {integrity: sha512-A8IND3Hnv0R6abc6qCcCaOCujTLMmGxtucMTZ5vbQUrEN/scxi378MyTLtyWg+MRr6bwQJ6v/orqMS9datIcww==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@napi-rs/canvas-darwin-x64@0.1.83':
resolution: {integrity: sha512-r4ZJxiP9OgUbdGZhPDEXD3hQ0aIPcVaywtcTXvamYxTU/SWKAbKVhFNTtpRe1J30oQ25gWyxTkUKSBgUkNzdnw==}
'@napi-rs/canvas-darwin-x64@0.1.84':
resolution: {integrity: sha512-AUW45lJhYWwnA74LaNeqhvqYKK/2hNnBBBl03KRdqeCD4tKneUSrxUqIv8d22CBweOvrAASyKN3W87WO2zEr/A==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@napi-rs/canvas-linux-arm-gnueabihf@0.1.83':
resolution: {integrity: sha512-Uc6aSB05qH1r+9GUDxIE6F5ZF7L0nTFyyzq8ublWUZhw8fEGK8iy931ff1ByGFT04+xHJad1kBcL4R1ZEV8z7Q==}
'@napi-rs/canvas-linux-arm-gnueabihf@0.1.84':
resolution: {integrity: sha512-8zs5ZqOrdgs4FioTxSBrkl/wHZB56bJNBqaIsfPL4ZkEQCinOkrFF7xIcXiHiKp93J3wUtbIzeVrhTIaWwqk+A==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
'@napi-rs/canvas-linux-arm64-gnu@0.1.83':
resolution: {integrity: sha512-eEeaJA7V5KOFq7W0GtoRVbd3ak8UZpK+XLkCgUiFGtlunNw+ZZW9Cr/92MXflGe7o3SqqMUg+f975LPxO/vsOQ==}
'@napi-rs/canvas-linux-arm64-gnu@0.1.84':
resolution: {integrity: sha512-i204vtowOglJUpbAFWU5mqsJgH0lVpNk/Ml4mQtB4Lndd86oF+Otr6Mr5KQnZHqYGhlSIKiU2SYnUbhO28zGQA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@napi-rs/canvas-linux-arm64-musl@0.1.83':
resolution: {integrity: sha512-cAvonp5XpbatVGegF9lMQNchs3z5RH6EtamRVnQvtoRtwbzOMcdzwuLBqDBQxQF79MFbuZNkWj3YRJjZCjHVzw==}
'@napi-rs/canvas-linux-arm64-musl@0.1.84':
resolution: {integrity: sha512-VyZq0EEw+OILnWk7G3ZgLLPaz1ERaPP++jLjeyLMbFOF+Tr4zHzWKiKDsEV/cT7btLPZbVoR3VX+T9/QubnURQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@napi-rs/canvas-linux-riscv64-gnu@0.1.83':
resolution: {integrity: sha512-WFUPQ9qZy31vmLxIJ3MfmHw+R2g/mLCgk8zmh7maJW8snV3vLPA7pZfIS65Dc61EVDp1vaBskwQ2RqPPzwkaew==}
'@napi-rs/canvas-linux-riscv64-gnu@0.1.84':
resolution: {integrity: sha512-PSMTh8DiThvLRsbtc/a065I/ceZk17EXAATv9uNvHgkgo7wdEfTh2C3aveNkBMGByVO3tvnvD5v/YFtZL07cIg==}
engines: {node: '>= 10'}
cpu: [riscv64]
os: [linux]
'@napi-rs/canvas-linux-x64-gnu@0.1.83':
resolution: {integrity: sha512-X9YwIjsuy50WwOyYeNhEHjKHO8rrfH9M4U8vNqLuGmqsZdKua/GrUhdQGdjq7lTgdY3g4+Ta5jF8MzAa7UAs/g==}
'@napi-rs/canvas-linux-x64-gnu@0.1.84':
resolution: {integrity: sha512-N1GY3noO1oqgEo3rYQIwY44kfM11vA0lDbN0orTOHfCSUZTUyiYCY0nZ197QMahZBm1aR/vYgsWpV74MMMDuNA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@napi-rs/canvas-linux-x64-musl@0.1.83':
resolution: {integrity: sha512-Vv2pLWQS8EnlSM1bstJ7vVhKA+mL4+my4sKUIn/bgIxB5O90dqiDhQjUDLP+5xn9ZMestRWDt3tdQEkGAmzq/A==}
'@napi-rs/canvas-linux-x64-musl@0.1.84':
resolution: {integrity: sha512-vUZmua6ADqTWyHyei81aXIt9wp0yjeNwTH0KdhdeoBb6azHmFR8uKTukZMXfLCC3bnsW0t4lW7K78KNMknmtjg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@napi-rs/canvas-win32-x64-msvc@0.1.83':
resolution: {integrity: sha512-K1TtjbScfRNYhq8dengLLufXGbtEtWdUXPV505uLFPovyGHzDUGXLFP/zUJzj6xWXwgUjHNLgEPIt7mye0zr6Q==}
'@napi-rs/canvas-win32-x64-msvc@0.1.84':
resolution: {integrity: sha512-YSs8ncurc1xzegUMNnQUTYrdrAuaXdPMOa+iYYyAxydOtg0ppV386hyYMsy00Yip1NlTgLCseRG4sHSnjQx6og==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
'@napi-rs/canvas@0.1.83':
resolution: {integrity: sha512-f9GVB9VNc9vn/nroc9epXRNkVpvNPZh69+qzLJIm9DfruxFqX0/jsXG46OGWAJgkO4mN0HvFHjRROMXKVmPszg==}
'@napi-rs/canvas@0.1.84':
resolution: {integrity: sha512-88FTNFs4uuiFKP0tUrPsEXhpe9dg7za9ILZJE08pGdUveMIDeana1zwfVkqRHJDPJFAmGY3dXmJ99dzsy57YnA==}
engines: {node: '>= 10'}
'@napi-rs/wasm-runtime@1.0.7':
@@ -880,8 +868,8 @@ packages:
'@push.rocks/smarterror@2.0.1':
resolution: {integrity: sha512-iCcH1D8tlDJgMFsaJ6lhdOTKhbU0KoprNv9MRP9o7691QOx4JEDXiHtr/lNtxVo8BUtdb9CF6kazaknO9KuORA==}
'@push.rocks/smartexit@1.0.23':
resolution: {integrity: sha512-WmwKYcwbHBByoABhHHB+PAjr5475AtD/xBh1mDcqPrFsOOUOZq3BBUdpq25wI3ccu/SZB5IwaimiVzadls6HkA==}
'@push.rocks/smartexit@1.1.0':
resolution: {integrity: sha512-GD8VLIbxQuwvhPXwK4eH162XAYSj+M3wGKWGNO3i1iY4bj8P3BARcgsWx6/ntN3aCo5ygWtrevrfD5iecYY2Ng==}
'@push.rocks/smartfeed@1.4.0':
resolution: {integrity: sha512-bvj/3cGQI6TbbjbqrgC1uufcqprd/VthefuIsS8KHiHyCqYD5Z6RTjrbQY9WOCsmub/dcuMavfXQZqe9g2+OrQ==}
@@ -1188,60 +1176,60 @@ packages:
'@rolldown/pluginutils@1.0.0-beta.52':
resolution: {integrity: sha512-/L0htLJZbaZFL1g9OHOblTxbCYIGefErJjtYOwgl9ZqNx27P3L0SDfjhhHIss32gu5NWgnxuT2a2Hnnv6QGHKA==}
'@rspack/binding-darwin-arm64@1.6.6':
resolution: {integrity: sha512-vGVDP0rlWa2w/gLba/sncVfkCah0HmhdmK5vGj/7sSX0iViwQneA2xjxDHyCNSQrvfq9GJmj4Kmdq/9tGh0KuA==}
'@rspack/binding-darwin-arm64@1.6.7':
resolution: {integrity: sha512-QiIAP8JTAtht0j8/xZZEQTJRB9e+KrOm9c7JJm73CewVg55rDWRrwopiVfBNlTu1coem1ztUHJYdQhg2uXfqww==}
cpu: [arm64]
os: [darwin]
'@rspack/binding-darwin-x64@1.6.6':
resolution: {integrity: sha512-IcdEG2kOmbPPO70Zl7gDnowDjK7d7C1hWew2vU7dPltr2t1JalRIMnS051lhiur0ULkSxV3cW1zXqv0Oi8AnOg==}
'@rspack/binding-darwin-x64@1.6.7':
resolution: {integrity: sha512-DpQRxxTXkMMNPmBXeJBaAB8HmWKxH2IfvHv7vU+kBhJ3xdPtXU4/xBv1W3biluoNRG11gc1WLIgjzeGgaLCxmw==}
cpu: [x64]
os: [darwin]
'@rspack/binding-linux-arm64-gnu@1.6.6':
resolution: {integrity: sha512-rIguCCtlTcwoFlwheDiUgdImk27spuCRn43zGJogARpM/ZYRFKIuSwFDGUtJT2g0TSLUAHUhWAUqC36NwvrbMQ==}
'@rspack/binding-linux-arm64-gnu@1.6.7':
resolution: {integrity: sha512-211/XoBiooGGgUo/NxNpsrzGUXtH1d7g/4+UTtjYtfc8QHwu7ZMHcsqg0wss53fXzn/yyxd0DZ56vBHq52BiFw==}
cpu: [arm64]
os: [linux]
'@rspack/binding-linux-arm64-musl@1.6.6':
resolution: {integrity: sha512-x6X6Gr0fUw6qrJGxZt3Rb6oIX+jd9pdcyp0VbtofcLaqGVQbzustYsYnuLATPOys0q4J/4kWnmEhkjLJHwkhpQ==}
'@rspack/binding-linux-arm64-musl@1.6.7':
resolution: {integrity: sha512-0WnqAWz3WPDsXGvOOA++or7cHpoidVsH3FlqNaAfRu6ni6n7ig/s0/jKUB+C5FtXOgmGjAGkZHfFgNHsvZ0FWw==}
cpu: [arm64]
os: [linux]
'@rspack/binding-linux-x64-gnu@1.6.6':
resolution: {integrity: sha512-gSlVdASszWHosQKn+nzYOInBijdQboUnmNMGgW9/PijVg3433IvQjzviUuJFno8CMGgrACV9yw+ZFDuK0J57VA==}
'@rspack/binding-linux-x64-gnu@1.6.7':
resolution: {integrity: sha512-iMrE0Q4IuYpkE0MjpaOVaUDYbQFiCRI9D3EPoXzlXJj4kJSdNheODpHTBVRlWt8Xp7UAoWuIFXCvKFKcSMm3aQ==}
cpu: [x64]
os: [linux]
'@rspack/binding-linux-x64-musl@1.6.6':
resolution: {integrity: sha512-TZaqVkh7memsTK/hxkOBrbpdzbmBUMea1YnYt++7QjMgco1kWFvAQ+YhAWtIaOaEg8s6C07Lt0Zp8izM2Dja0g==}
'@rspack/binding-linux-x64-musl@1.6.7':
resolution: {integrity: sha512-e7gKFxpdEQwYGk7lTC/hukTgNtaoAstBXehnZNk4k3kuU6+86WDrkn18Cd949iNqfIPtIG/wIsFNGbkHsH69hQ==}
cpu: [x64]
os: [linux]
'@rspack/binding-wasm32-wasi@1.6.6':
resolution: {integrity: sha512-W4mWdlLnYrbUaktyHOGNfATblxMTbgF7CBfDw8PhbDtjd2l8e/TnaHgIDkwITHXAOMEF/QEKfo9FtusbcQJNKw==}
'@rspack/binding-wasm32-wasi@1.6.7':
resolution: {integrity: sha512-yx88EFdE9RP3hh7VhjjW6uc6wGU0KcpOcZp8T8E/a+X8L98fX0aVrtM1IDbndhmdluIMqGbfJNap2+QqOCY9Mw==}
cpu: [wasm32]
'@rspack/binding-win32-arm64-msvc@1.6.6':
resolution: {integrity: sha512-cw5OgxqoDwjoZlk0L3vGEwcjPZsOVFYLwr2ssiC05rsTbhBwxj8coLpAJdvUvbf6C2TTmCB7iPe2sPq1KWD37g==}
'@rspack/binding-win32-arm64-msvc@1.6.7':
resolution: {integrity: sha512-vgxVYpFK8P5ulSXQQA+EbX78R/SUU+WIf0JIY+LoUoP89gZOsise/lKAJMAybzpeTJ1t0ndLchFznDYnzq+l4Q==}
cpu: [arm64]
os: [win32]
'@rspack/binding-win32-ia32-msvc@1.6.6':
resolution: {integrity: sha512-M4ruR+VZ59iy+mPjy6FQPT27cOgeytf3wFBrt7e0suKeNLYGxrNyI9YhgpCTY++SMJsAMgRLGDHoI3ZgWulw1Q==}
'@rspack/binding-win32-ia32-msvc@1.6.7':
resolution: {integrity: sha512-bV5RTW0Va0UQKJm9HWLt7fWNBPaBBBxCJOA2pJT3nGGm6CCXKnZSyEiVbFUk4jI/uiwBfqenlLkzaGoMRbeDhA==}
cpu: [ia32]
os: [win32]
'@rspack/binding-win32-x64-msvc@1.6.6':
resolution: {integrity: sha512-q5QTvdhPUh+CA93cQG5zWKRIHMIWPzw+ftFDEwBw52zYdvNAoLniqD8o5Mi8CT0pndhulXgR5aw0Sjd3eMah+A==}
'@rspack/binding-win32-x64-msvc@1.6.7':
resolution: {integrity: sha512-8xlbuJQtYktlBjZupOHlO8FeZqSIhsV3ih7xBSiOYar6LI6uQzA7XiO3I5kaPSDirBMMMKv1Z4rKCxWx10a3TQ==}
cpu: [x64]
os: [win32]
'@rspack/binding@1.6.6':
resolution: {integrity: sha512-noiV+qhyBTVpvG2M4bnOwKk2Ynl6G47Wf7wpCjPCFr87qr3txNwTTnhkEJEU59yj+VvIhbRD2rf5+9TLoT0Wxg==}
'@rspack/binding@1.6.7':
resolution: {integrity: sha512-7ICabuBN3gHc6PPN52+m1kruz3ogiJjg1C0gSWdLRk18m/4jlcM2aAy6wfXjgODJdB0Yh2ro/lIpBbj+AYWUGA==}
'@rspack/core@1.6.6':
resolution: {integrity: sha512-2mR+2YBydlgZ7Q0Rpd6bCC3MBnV9TS0x857K0zIhbDj4BQOqaWVy1n7fx/B3MrS8TR0QCuzKfyDAjNz+XTyJVQ==}
'@rspack/core@1.6.7':
resolution: {integrity: sha512-tkd4nSzTf+pDa9OAE4INi/JEa93HNszjWy5C9+trf4ZCXLLHsHxHQFbzoreuz4Vv2PlCWajgvAdiPMV1vGIkuw==}
engines: {node: '>=18.12.0'}
peerDependencies:
'@swc/helpers': '>=0.5.1'
@@ -1760,8 +1748,8 @@ packages:
'@types/node@22.19.3':
resolution: {integrity: sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==}
'@types/node@24.10.1':
resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==}
'@types/node@24.10.4':
resolution: {integrity: sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==}
'@types/qs@6.14.0':
resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==}
@@ -1826,8 +1814,8 @@ packages:
'@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
'@uptime.link/webwidget@1.2.5':
resolution: {integrity: sha512-uyVQ87JG0gz7M2MjMWJaTnFbigBTNhcdKpqP9V3kkQyzxod3HV963vidrdWTgKbULtDyPTjtaoS5gSqn67BJhQ==}
'@uptime.link/webwidget@1.2.6':
resolution: {integrity: sha512-rpr3lIQ69OwfYJSBhBYOP2rx4yyowpdpLbqUvkiBVx93SEc/9gwM8Sy9vcBztod9e9j5Nwac/82Ygjx7pRfykQ==}
'@webcontainer/api@1.2.0':
resolution: {integrity: sha512-tzoKBd4lLdhHy5GHFpUkl+ndoSba8JqmB7x0ZQFnWfjbcbQOvKQfxA8MEMUYhgqjWHnbrWdAfnBEHz5f5lYG5A==}
@@ -1888,8 +1876,8 @@ packages:
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
asn1js@3.0.6:
resolution: {integrity: sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==}
asn1js@3.0.7:
resolution: {integrity: sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==}
engines: {node: '>=12.0.0'}
async-mutex@0.5.0:
@@ -2304,8 +2292,8 @@ packages:
resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==}
hasBin: true
fast-xml-parser@5.3.2:
resolution: {integrity: sha512-n8v8b6p4Z1sMgqRmqLJm3awW4NX7NkaKPfb3uJIBTSH7Pdvufi3PQ3/lJLQrvxcMYl7JI2jnDO90siPEpD8JBA==}
fast-xml-parser@5.3.3:
resolution: {integrity: sha512-2O3dkPAAC6JavuMm8+4+pgTk+5hoAs+CjZ+sWcQLkX9+/tHRuTkQh/Oaifr8qDmZ8iEHb771Ea6G8CdwkrgvYA==}
hasBin: true
fault@2.0.1:
@@ -2514,8 +2502,8 @@ packages:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
engines: {node: '>=0.10.0'}
iconv-lite@0.7.0:
resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==}
iconv-lite@0.7.1:
resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==}
engines: {node: '>=0.10.0'}
ieee754@1.2.1:
@@ -2629,8 +2617,8 @@ packages:
resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
engines: {node: '>=0.10.0'}
lenis@1.3.15:
resolution: {integrity: sha512-zSYOFs0ydafX70uygFoipaHHQouPeE4DpZZhdOUyLJxVf2ZVvBCBBaolDDaQztTRsa6+stBlxq2GmFGJPAVryQ==}
lenis@1.3.16:
resolution: {integrity: sha512-KULpna+5TgRCDMSPx4SzJwtsjlz7EeoFCp4IDCXrlM73rLBAj34Egcl7GCLz/6+hXFOYt3DTBeTtJvTu45dJNA==}
peerDependencies:
'@nuxt/kit': '>=3.0.0'
react: '>=17.0.0'
@@ -2736,9 +2724,6 @@ packages:
resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==}
engines: {node: 20 || >=22}
lucide@0.555.0:
resolution: {integrity: sha512-R7BkO2/XRpMADcMIRn1UOZOvirxr2Z6s/R82k0EUK71ZXXrlRbvkVwTAIf+9DRApeyH+zNMIGfiUdmrOhoAygQ==}
lucide@0.560.0:
resolution: {integrity: sha512-w7++Pwdz0NxxMtC4ugLmsy66Ar95HnDIMjzJZdHl0kQKIHto3icgI+lbOZMlovZ1Mo4RGITWGhYn1ro7hcY/UA==}
@@ -2973,12 +2958,12 @@ packages:
resolution: {integrity: sha512-irhhjRVLE20hbkRl4zpAYLnDMM+zIZnp0IDB9akAFFUZp/3XdOfwwddc7y6cNvF2WCEtfTYRwYbIfYa2kVY0og==}
engines: {node: '>=20.19.0'}
mongodb-memory-server-core@10.4.1:
resolution: {integrity: sha512-YJdrEyF9hk64nfeoVDMP6IfTzK+gLZhrQqYyP6JJMsqo2LK5eF7JRZ4YPQDmt1re/JhItpiU+ypiZbIG1OsW5Q==}
mongodb-memory-server-core@10.4.2:
resolution: {integrity: sha512-/w7SWH+f/bpzmQlYu0lRWF33GLOo0GwZrflP1gkDhc6PCuLo5T1mnME/W+mrFmsowLGzthdJnezGBpOnIYNALw==}
engines: {node: '>=16.20.1'}
mongodb-memory-server@10.4.1:
resolution: {integrity: sha512-XpCyV1e7QQ1lW28rgtXP4ZlX8ZfD/8z1ZGNxz2y3JrosLgDrNnYWvPjlgFj3JjboYUtlh1jF2Ez/rwsQA6cl0w==}
mongodb-memory-server@10.4.2:
resolution: {integrity: sha512-r2swgOhmhGfx80TJIALb7t4hlrRtdl+uqc0qrGe+nqmjZQn3prd0SOioxxuLb5LjtmQrx3CPQqJf+PmH5hg+5A==}
engines: {node: '>=16.20.1'}
mongodb@6.21.0:
@@ -3539,9 +3524,6 @@ packages:
strnum@1.1.2:
resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==}
strnum@2.1.1:
resolution: {integrity: sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==}
strnum@2.1.2:
resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==}
@@ -3835,8 +3817,8 @@ snapshots:
'@api.global/typedrequest': 3.2.5
'@api.global/typedrequest-interfaces': 3.0.19
'@api.global/typedsocket': 3.1.1(@push.rocks/smartserve@1.4.0)
'@cloudflare/workers-types': 4.20251202.0
'@design.estate/dees-comms': 1.0.27
'@cloudflare/workers-types': 4.20251213.0
'@design.estate/dees-comms': 1.0.30
'@push.rocks/lik': 6.2.2
'@push.rocks/smartchok': 1.2.0
'@push.rocks/smartdelay': 3.0.5
@@ -4442,8 +4424,6 @@ snapshots:
'@cfworker/json-schema@4.1.1': {}
'@cloudflare/workers-types@4.20251202.0': {}
'@cloudflare/workers-types@4.20251213.0': {}
'@configvault.io/interfaces@1.0.17':
@@ -4473,42 +4453,6 @@ snapshots:
'@push.rocks/smarttime': 4.1.1
'@push.rocks/webstore': 2.0.20
'@design.estate/dees-catalog@2.0.7(@tiptap/pm@2.27.1)':
dependencies:
'@design.estate/dees-domtools': 2.3.6
'@design.estate/dees-element': 2.1.3
'@design.estate/dees-wcctools': 1.2.1
'@fortawesome/fontawesome-svg-core': 7.1.0
'@fortawesome/free-brands-svg-icons': 7.1.0
'@fortawesome/free-regular-svg-icons': 7.1.0
'@fortawesome/free-solid-svg-icons': 7.1.0
'@push.rocks/smarti18n': 1.0.4
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartstring': 4.1.0
'@tiptap/core': 2.27.1(@tiptap/pm@2.27.1)
'@tiptap/extension-link': 2.27.1(@tiptap/core@2.27.1(@tiptap/pm@2.27.1))(@tiptap/pm@2.27.1)
'@tiptap/extension-text-align': 2.27.1(@tiptap/core@2.27.1(@tiptap/pm@2.27.1))
'@tiptap/extension-typography': 2.27.1(@tiptap/core@2.27.1(@tiptap/pm@2.27.1))
'@tiptap/extension-underline': 2.27.1(@tiptap/core@2.27.1(@tiptap/pm@2.27.1))
'@tiptap/starter-kit': 2.27.1
'@tsclass/tsclass': 9.3.0
'@webcontainer/api': 1.2.0
apexcharts: 5.3.6
highlight.js: 11.11.1
ibantools: 4.5.1
lit: 3.3.1
lucide: 0.555.0
monaco-editor: 0.52.2
pdfjs-dist: 4.10.38
xterm: 5.3.0
xterm-addon-fit: 0.8.0(xterm@5.3.0)
transitivePeerDependencies:
- '@nuxt/kit'
- '@tiptap/pm'
- react
- supports-color
- vue
'@design.estate/dees-catalog@3.3.1(@tiptap/pm@2.27.1)':
dependencies:
'@design.estate/dees-domtools': 2.3.6
@@ -4545,13 +4489,6 @@ snapshots:
- supports-color
- vue
'@design.estate/dees-comms@1.0.27':
dependencies:
'@api.global/typedrequest': 3.2.5
'@api.global/typedrequest-interfaces': 3.0.19
'@push.rocks/smartdelay': 3.0.5
broadcast-channel: 7.2.0
'@design.estate/dees-comms@1.0.30':
dependencies:
'@api.global/typedrequest': 3.2.5
@@ -4562,7 +4499,7 @@ snapshots:
'@design.estate/dees-domtools@2.3.6':
dependencies:
'@api.global/typedrequest': 3.2.5
'@design.estate/dees-comms': 1.0.27
'@design.estate/dees-comms': 1.0.30
'@push.rocks/lik': 6.2.2
'@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartjson': 5.2.0
@@ -4576,7 +4513,7 @@ snapshots:
'@push.rocks/webrequest': 3.0.37
'@push.rocks/websetup': 3.0.19
'@push.rocks/webstore': 2.0.20
lenis: 1.3.15
lenis: 1.3.16
lit: 3.3.1
sweet-scroll: 4.0.0
transitivePeerDependencies:
@@ -4597,18 +4534,6 @@ snapshots:
- supports-color
- vue
'@design.estate/dees-wcctools@1.2.1':
dependencies:
'@design.estate/dees-domtools': 2.3.6
'@design.estate/dees-element': 2.1.3
'@push.rocks/smartdelay': 3.0.5
lit: 3.3.1
transitivePeerDependencies:
- '@nuxt/kit'
- react
- supports-color
- vue
'@design.estate/dees-wcctools@2.0.1':
dependencies:
'@design.estate/dees-domtools': 2.3.6
@@ -4765,7 +4690,7 @@ snapshots:
'@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartspawn': 3.0.3
'@rspack/core': 1.6.6
'@rspack/core': 1.6.7
'@types/html-minifier': 4.0.6
esbuild: 0.27.1
html-minifier: 4.0.0
@@ -4976,52 +4901,52 @@ snapshots:
'@module-federation/runtime': 0.21.6
'@module-federation/sdk': 0.21.6
'@mongodb-js/saslprep@1.3.2':
'@mongodb-js/saslprep@1.4.0':
dependencies:
sparse-bitfield: 3.0.3
'@napi-rs/canvas-android-arm64@0.1.83':
'@napi-rs/canvas-android-arm64@0.1.84':
optional: true
'@napi-rs/canvas-darwin-arm64@0.1.83':
'@napi-rs/canvas-darwin-arm64@0.1.84':
optional: true
'@napi-rs/canvas-darwin-x64@0.1.83':
'@napi-rs/canvas-darwin-x64@0.1.84':
optional: true
'@napi-rs/canvas-linux-arm-gnueabihf@0.1.83':
'@napi-rs/canvas-linux-arm-gnueabihf@0.1.84':
optional: true
'@napi-rs/canvas-linux-arm64-gnu@0.1.83':
'@napi-rs/canvas-linux-arm64-gnu@0.1.84':
optional: true
'@napi-rs/canvas-linux-arm64-musl@0.1.83':
'@napi-rs/canvas-linux-arm64-musl@0.1.84':
optional: true
'@napi-rs/canvas-linux-riscv64-gnu@0.1.83':
'@napi-rs/canvas-linux-riscv64-gnu@0.1.84':
optional: true
'@napi-rs/canvas-linux-x64-gnu@0.1.83':
'@napi-rs/canvas-linux-x64-gnu@0.1.84':
optional: true
'@napi-rs/canvas-linux-x64-musl@0.1.83':
'@napi-rs/canvas-linux-x64-musl@0.1.84':
optional: true
'@napi-rs/canvas-win32-x64-msvc@0.1.83':
'@napi-rs/canvas-win32-x64-msvc@0.1.84':
optional: true
'@napi-rs/canvas@0.1.83':
'@napi-rs/canvas@0.1.84':
optionalDependencies:
'@napi-rs/canvas-android-arm64': 0.1.83
'@napi-rs/canvas-darwin-arm64': 0.1.83
'@napi-rs/canvas-darwin-x64': 0.1.83
'@napi-rs/canvas-linux-arm-gnueabihf': 0.1.83
'@napi-rs/canvas-linux-arm64-gnu': 0.1.83
'@napi-rs/canvas-linux-arm64-musl': 0.1.83
'@napi-rs/canvas-linux-riscv64-gnu': 0.1.83
'@napi-rs/canvas-linux-x64-gnu': 0.1.83
'@napi-rs/canvas-linux-x64-musl': 0.1.83
'@napi-rs/canvas-win32-x64-msvc': 0.1.83
'@napi-rs/canvas-android-arm64': 0.1.84
'@napi-rs/canvas-darwin-arm64': 0.1.84
'@napi-rs/canvas-darwin-x64': 0.1.84
'@napi-rs/canvas-linux-arm-gnueabihf': 0.1.84
'@napi-rs/canvas-linux-arm64-gnu': 0.1.84
'@napi-rs/canvas-linux-arm64-musl': 0.1.84
'@napi-rs/canvas-linux-riscv64-gnu': 0.1.84
'@napi-rs/canvas-linux-x64-gnu': 0.1.84
'@napi-rs/canvas-linux-x64-musl': 0.1.84
'@napi-rs/canvas-win32-x64-msvc': 0.1.84
optional: true
'@napi-rs/wasm-runtime@1.0.7':
@@ -5045,21 +4970,21 @@ snapshots:
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0
'@peculiar/asn1-x509-attr': 2.6.0
asn1js: 3.0.6
asn1js: 3.0.7
tslib: 2.8.1
'@peculiar/asn1-csr@2.6.0':
dependencies:
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0
asn1js: 3.0.6
asn1js: 3.0.7
tslib: 2.8.1
'@peculiar/asn1-ecc@2.6.0':
dependencies:
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0
asn1js: 3.0.6
asn1js: 3.0.7
tslib: 2.8.1
'@peculiar/asn1-pfx@2.6.0':
@@ -5068,14 +4993,14 @@ snapshots:
'@peculiar/asn1-pkcs8': 2.6.0
'@peculiar/asn1-rsa': 2.6.0
'@peculiar/asn1-schema': 2.6.0
asn1js: 3.0.6
asn1js: 3.0.7
tslib: 2.8.1
'@peculiar/asn1-pkcs8@2.6.0':
dependencies:
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0
asn1js: 3.0.6
asn1js: 3.0.7
tslib: 2.8.1
'@peculiar/asn1-pkcs9@2.6.0':
@@ -5086,19 +5011,19 @@ snapshots:
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0
'@peculiar/asn1-x509-attr': 2.6.0
asn1js: 3.0.6
asn1js: 3.0.7
tslib: 2.8.1
'@peculiar/asn1-rsa@2.6.0':
dependencies:
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0
asn1js: 3.0.6
asn1js: 3.0.7
tslib: 2.8.1
'@peculiar/asn1-schema@2.6.0':
dependencies:
asn1js: 3.0.6
asn1js: 3.0.7
pvtsutils: 1.3.6
tslib: 2.8.1
@@ -5106,13 +5031,13 @@ snapshots:
dependencies:
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.0
asn1js: 3.0.6
asn1js: 3.0.7
tslib: 2.8.1
'@peculiar/asn1-x509@2.6.0':
dependencies:
'@peculiar/asn1-schema': 2.6.0
asn1js: 3.0.6
asn1js: 3.0.7
pvtsutils: 1.3.6
tslib: 2.8.1
@@ -5167,7 +5092,7 @@ snapshots:
'@push.rocks/smartbucket': 3.3.10
'@push.rocks/smartcache': 1.0.18
'@push.rocks/smartenv': 5.0.13
'@push.rocks/smartexit': 1.0.23
'@push.rocks/smartexit': 1.1.0
'@push.rocks/smartfile': 11.2.7
'@push.rocks/smartjson': 5.2.0
'@push.rocks/smartpath': 6.0.0
@@ -5396,7 +5321,7 @@ snapshots:
clean-stack: 1.3.0
make-error-cause: 2.3.0
'@push.rocks/smartexit@1.0.23':
'@push.rocks/smartexit@1.1.0':
dependencies:
'@push.rocks/lik': 6.2.2
'@push.rocks/smartdelay': 3.0.5
@@ -5589,7 +5514,7 @@ snapshots:
'@push.rocks/smartdata': 5.16.7
'@push.rocks/smartpath': 5.1.0
'@push.rocks/smartpromise': 4.2.3
mongodb-memory-server: 10.4.1
mongodb-memory-server: 10.4.2
transitivePeerDependencies:
- '@aws-sdk/credential-providers'
- '@mongodb-js/zstd'
@@ -5709,7 +5634,7 @@ snapshots:
'@push.rocks/smartshell@3.3.0':
dependencies:
'@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartexit': 1.0.23
'@push.rocks/smartexit': 1.1.0
'@push.rocks/smartpromise': 4.2.3
'@types/which': 3.0.4
tree-kill: 1.2.2
@@ -5823,7 +5748,7 @@ snapshots:
'@push.rocks/smartxml@2.0.0':
dependencies:
fast-xml-parser: 5.3.2
fast-xml-parser: 5.3.3
'@push.rocks/smartyaml@2.0.5':
dependencies:
@@ -5981,55 +5906,55 @@ snapshots:
'@rolldown/pluginutils@1.0.0-beta.52': {}
'@rspack/binding-darwin-arm64@1.6.6':
'@rspack/binding-darwin-arm64@1.6.7':
optional: true
'@rspack/binding-darwin-x64@1.6.6':
'@rspack/binding-darwin-x64@1.6.7':
optional: true
'@rspack/binding-linux-arm64-gnu@1.6.6':
'@rspack/binding-linux-arm64-gnu@1.6.7':
optional: true
'@rspack/binding-linux-arm64-musl@1.6.6':
'@rspack/binding-linux-arm64-musl@1.6.7':
optional: true
'@rspack/binding-linux-x64-gnu@1.6.6':
'@rspack/binding-linux-x64-gnu@1.6.7':
optional: true
'@rspack/binding-linux-x64-musl@1.6.6':
'@rspack/binding-linux-x64-musl@1.6.7':
optional: true
'@rspack/binding-wasm32-wasi@1.6.6':
'@rspack/binding-wasm32-wasi@1.6.7':
dependencies:
'@napi-rs/wasm-runtime': 1.0.7
optional: true
'@rspack/binding-win32-arm64-msvc@1.6.6':
'@rspack/binding-win32-arm64-msvc@1.6.7':
optional: true
'@rspack/binding-win32-ia32-msvc@1.6.6':
'@rspack/binding-win32-ia32-msvc@1.6.7':
optional: true
'@rspack/binding-win32-x64-msvc@1.6.6':
'@rspack/binding-win32-x64-msvc@1.6.7':
optional: true
'@rspack/binding@1.6.6':
'@rspack/binding@1.6.7':
optionalDependencies:
'@rspack/binding-darwin-arm64': 1.6.6
'@rspack/binding-darwin-x64': 1.6.6
'@rspack/binding-linux-arm64-gnu': 1.6.6
'@rspack/binding-linux-arm64-musl': 1.6.6
'@rspack/binding-linux-x64-gnu': 1.6.6
'@rspack/binding-linux-x64-musl': 1.6.6
'@rspack/binding-wasm32-wasi': 1.6.6
'@rspack/binding-win32-arm64-msvc': 1.6.6
'@rspack/binding-win32-ia32-msvc': 1.6.6
'@rspack/binding-win32-x64-msvc': 1.6.6
'@rspack/binding-darwin-arm64': 1.6.7
'@rspack/binding-darwin-x64': 1.6.7
'@rspack/binding-linux-arm64-gnu': 1.6.7
'@rspack/binding-linux-arm64-musl': 1.6.7
'@rspack/binding-linux-x64-gnu': 1.6.7
'@rspack/binding-linux-x64-musl': 1.6.7
'@rspack/binding-wasm32-wasi': 1.6.7
'@rspack/binding-win32-arm64-msvc': 1.6.7
'@rspack/binding-win32-ia32-msvc': 1.6.7
'@rspack/binding-win32-x64-msvc': 1.6.7
'@rspack/core@1.6.6':
'@rspack/core@1.6.7':
dependencies:
'@module-federation/runtime-tools': 0.21.6
'@rspack/binding': 1.6.6
'@rspack/binding': 1.6.7
'@rspack/lite-tapable': 1.1.0
'@rspack/lite-tapable@1.1.0': {}
@@ -6599,27 +6524,27 @@ snapshots:
'@types/bn.js@5.2.0':
dependencies:
'@types/node': 24.10.1
'@types/node': 24.10.4
'@types/body-parser@1.19.6':
dependencies:
'@types/connect': 3.4.38
'@types/node': 24.10.1
'@types/node': 24.10.4
'@types/buffer-json@2.0.3': {}
'@types/clean-css@4.2.11':
dependencies:
'@types/node': 24.10.1
'@types/node': 24.10.4
source-map: 0.6.1
'@types/connect@3.4.38':
dependencies:
'@types/node': 24.10.1
'@types/node': 24.10.4
'@types/cors@2.8.19':
dependencies:
'@types/node': 24.10.1
'@types/node': 24.10.4
'@types/debug@4.1.12':
dependencies:
@@ -6627,7 +6552,7 @@ snapshots:
'@types/dns-packet@5.6.5':
dependencies:
'@types/node': 24.10.1
'@types/node': 24.10.4
'@types/elliptic@6.4.18':
dependencies:
@@ -6635,7 +6560,7 @@ snapshots:
'@types/express-serve-static-core@5.1.0':
dependencies:
'@types/node': 24.10.1
'@types/node': 24.10.4
'@types/qs': 6.14.0
'@types/range-parser': 1.2.7
'@types/send': 1.2.1
@@ -6648,17 +6573,17 @@ snapshots:
'@types/from2@2.3.6':
dependencies:
'@types/node': 24.10.1
'@types/node': 24.10.4
'@types/fs-extra@11.0.4':
dependencies:
'@types/jsonfile': 6.1.4
'@types/node': 24.10.1
'@types/node': 24.10.4
'@types/glob@8.1.0':
dependencies:
'@types/minimatch': 5.1.2
'@types/node': 24.10.1
'@types/node': 24.10.4
'@types/hast@3.0.4':
dependencies:
@@ -6680,12 +6605,12 @@ snapshots:
'@types/jsonfile@6.1.4':
dependencies:
'@types/node': 24.10.1
'@types/node': 24.10.4
'@types/jsonwebtoken@9.0.10':
dependencies:
'@types/ms': 2.1.0
'@types/node': 24.10.1
'@types/node': 24.10.4
'@types/linkify-it@5.0.0': {}
@@ -6708,17 +6633,17 @@ snapshots:
'@types/mute-stream@0.0.4':
dependencies:
'@types/node': 24.10.1
'@types/node': 24.10.4
'@types/node-forge@1.3.14':
dependencies:
'@types/node': 24.10.1
'@types/node': 24.10.4
'@types/node@22.19.3':
dependencies:
undici-types: 6.21.0
'@types/node@24.10.1':
'@types/node@24.10.4':
dependencies:
undici-types: 7.16.0
@@ -6734,22 +6659,22 @@ snapshots:
'@types/send@1.2.1':
dependencies:
'@types/node': 24.10.1
'@types/node': 24.10.4
'@types/serve-static@2.2.0':
dependencies:
'@types/http-errors': 2.0.5
'@types/node': 24.10.1
'@types/node': 24.10.4
'@types/symbol-tree@3.2.5': {}
'@types/tar-stream@3.1.4':
dependencies:
'@types/node': 24.10.1
'@types/node': 24.10.4
'@types/through2@2.0.41':
dependencies:
'@types/node': 24.10.1
'@types/node': 24.10.4
'@types/trusted-types@2.0.7': {}
@@ -6779,12 +6704,12 @@ snapshots:
'@ungap/structured-clone@1.3.0': {}
'@uptime.link/webwidget@1.2.5(@tiptap/pm@2.27.1)':
'@uptime.link/webwidget@1.2.6(@tiptap/pm@2.27.1)':
dependencies:
'@design.estate/dees-catalog': 2.0.7(@tiptap/pm@2.27.1)
'@design.estate/dees-catalog': 3.3.1(@tiptap/pm@2.27.1)
'@design.estate/dees-domtools': 2.3.6
'@design.estate/dees-element': 2.1.3
'@design.estate/dees-wcctools': 1.2.1
'@design.estate/dees-wcctools': 2.0.1
transitivePeerDependencies:
- '@nuxt/kit'
- '@tiptap/pm'
@@ -6809,7 +6734,7 @@ snapshots:
acme-client@5.4.0:
dependencies:
'@peculiar/x509': 1.14.2
asn1js: 3.0.6
asn1js: 3.0.7
axios: 1.13.2(debug@4.4.3)
debug: 4.4.3
node-forge: 1.3.3
@@ -6853,7 +6778,7 @@ snapshots:
argparse@2.0.1: {}
asn1js@3.0.6:
asn1js@3.0.7:
dependencies:
pvtsutils: 1.3.6
pvutils: 1.1.5
@@ -6893,7 +6818,7 @@ snapshots:
content-type: 1.0.5
debug: 4.4.3
http-errors: 2.0.1
iconv-lite: 0.7.0
iconv-lite: 0.7.1
on-finished: 2.4.1
qs: 6.14.0
raw-body: 3.0.2
@@ -7149,7 +7074,7 @@ snapshots:
engine.io@6.6.4:
dependencies:
'@types/cors': 2.8.19
'@types/node': 24.10.1
'@types/node': 24.10.4
accepts: 1.3.8
base64id: 2.0.0
cookie: 0.7.2
@@ -7291,9 +7216,9 @@ snapshots:
dependencies:
strnum: 2.1.2
fast-xml-parser@5.3.2:
fast-xml-parser@5.3.3:
dependencies:
strnum: 2.1.1
strnum: 2.1.2
fault@2.0.1:
dependencies:
@@ -7575,7 +7500,7 @@ snapshots:
dependencies:
safer-buffer: 2.1.2
iconv-lite@0.7.0:
iconv-lite@0.7.1:
dependencies:
safer-buffer: 2.1.2
@@ -7693,7 +7618,7 @@ snapshots:
kind-of@6.0.3: {}
lenis@1.3.15: {}
lenis@1.3.16: {}
linkify-it@5.0.0:
dependencies:
@@ -7784,8 +7709,6 @@ snapshots:
lru-cache@11.2.4: {}
lucide@0.555.0: {}
lucide@0.560.0: {}
make-dir@3.1.0:
@@ -8198,7 +8121,7 @@ snapshots:
'@types/whatwg-url': 13.0.0
whatwg-url: 14.2.0
mongodb-memory-server-core@10.4.1:
mongodb-memory-server-core@10.4.2:
dependencies:
async-mutex: 0.5.0
camelcase: 6.3.0
@@ -8224,9 +8147,9 @@ snapshots:
- socks
- supports-color
mongodb-memory-server@10.4.1:
mongodb-memory-server@10.4.2:
dependencies:
mongodb-memory-server-core: 10.4.1
mongodb-memory-server-core: 10.4.2
tslib: 2.8.1
transitivePeerDependencies:
- '@aws-sdk/credential-providers'
@@ -8242,13 +8165,13 @@ snapshots:
mongodb@6.21.0:
dependencies:
'@mongodb-js/saslprep': 1.3.2
'@mongodb-js/saslprep': 1.4.0
bson: 6.10.4
mongodb-connection-string-url: 3.0.2
mongodb@7.0.0:
dependencies:
'@mongodb-js/saslprep': 1.3.2
'@mongodb-js/saslprep': 1.4.0
bson: 7.0.0
mongodb-connection-string-url: 7.0.0
@@ -8370,7 +8293,7 @@ snapshots:
pdfjs-dist@4.10.38:
optionalDependencies:
'@napi-rs/canvas': 0.1.83
'@napi-rs/canvas': 0.1.84
peek-readable@5.4.2: {}
@@ -8532,7 +8455,7 @@ snapshots:
dependencies:
bytes: 3.1.2
http-errors: 2.0.1
iconv-lite: 0.7.0
iconv-lite: 0.7.1
unpipe: 1.0.0
rc@1.2.8:
@@ -8861,8 +8784,6 @@ snapshots:
strnum@1.1.2: {}
strnum@2.1.1: {}
strnum@2.1.2: {}
strtok3@10.3.4:
+1 -1
View File
@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@idp.global/idp.global',
version: '1.12.0',
version: '1.13.0',
description: 'An identity provider software managing user authentications, registrations, and sessions.'
}
+42 -1
View File
@@ -4,6 +4,10 @@ import { Reception } from './reception/classes.reception.js';
export const runCli = async () => {
const serviceQenv = new plugins.qenv.Qenv('./', './.nogit', false);
// Create reception first so we can reference it in routes
let reception: Reception;
const websiteServer = new plugins.typedserver.utilityservers.UtilityWebsiteServer({
feedMetadata: null,
domain: 'idp.global',
@@ -22,11 +26,48 @@ export const runCli = async () => {
addCustomRoutes: async (typedserver) => {
// Enable SPA fallback - serves index.html for non-file routes (e.g., /login, /dashboard)
typedserver.options.spaFallback = true;
// OIDC Discovery endpoint
typedserver.addRoute('/.well-known/openid-configuration', 'GET', async (req) => {
return new Response(JSON.stringify(reception.oidcManager.getDiscoveryDocument()), {
headers: { 'Content-Type': 'application/json' },
});
});
// JWKS endpoint
typedserver.addRoute('/.well-known/jwks.json', 'GET', async (req) => {
return new Response(JSON.stringify(reception.oidcManager.getJwks()), {
headers: { 'Content-Type': 'application/json' },
});
});
// OAuth Authorization endpoint
typedserver.addRoute('/oauth/authorize', 'GET', async (req) => {
return reception.oidcManager.handleAuthorize(req);
});
// OAuth Token endpoint
typedserver.addRoute('/oauth/token', 'POST', async (req) => {
return reception.oidcManager.handleToken(req);
});
// OAuth UserInfo endpoint (GET and POST)
typedserver.addRoute('/oauth/userinfo', 'GET', async (req) => {
return reception.oidcManager.handleUserInfo(req);
});
typedserver.addRoute('/oauth/userinfo', 'POST', async (req) => {
return reception.oidcManager.handleUserInfo(req);
});
// OAuth Revocation endpoint
typedserver.addRoute('/oauth/revoke', 'POST', async (req) => {
return reception.oidcManager.handleRevoke(req);
});
},
});
// lets add the reception routes
const reception = new Reception({
reception = new Reception({
name: (await serviceQenv.getEnvVarOnDemand('INSTANCE_NAME')) || 'idp.global',
mongoDescriptor: {
mongoDbUrl: await serviceQenv.getEnvVarOnDemand('MONGODB_URL'),
+684
View File
@@ -0,0 +1,684 @@
import * as plugins from '../plugins.js';
import type { Reception } from './classes.reception.js';
import type { App } from './classes.app.js';
/**
* OidcManager handles OpenID Connect (OIDC) server functionality
* for third-party client authentication.
*/
export class OidcManager {
public receptionRef: Reception;
public get db() {
return this.receptionRef.db.smartdataDb;
}
// In-memory store for authorization codes (short-lived, 10 min TTL)
private authorizationCodes = new Map<string, plugins.idpInterfaces.data.IAuthorizationCode>();
// In-memory store for access tokens (for validation)
private accessTokens = new Map<string, plugins.idpInterfaces.data.IOidcAccessToken>();
// In-memory store for refresh tokens
private refreshTokens = new Map<string, plugins.idpInterfaces.data.IOidcRefreshToken>();
// In-memory store for user consents (should be persisted later)
private userConsents = new Map<string, plugins.idpInterfaces.data.IUserConsent>();
constructor(receptionRefArg: Reception) {
this.receptionRef = receptionRefArg;
// Start cleanup task for expired codes/tokens
this.startCleanupTask();
}
/**
* Get the OIDC Discovery Document
*/
public getDiscoveryDocument(): plugins.idpInterfaces.data.IOidcDiscoveryDocument {
const baseUrl = this.receptionRef.options.baseUrl || 'https://idp.global';
return {
issuer: baseUrl,
authorization_endpoint: `${baseUrl}/oauth/authorize`,
token_endpoint: `${baseUrl}/oauth/token`,
userinfo_endpoint: `${baseUrl}/oauth/userinfo`,
jwks_uri: `${baseUrl}/.well-known/jwks.json`,
revocation_endpoint: `${baseUrl}/oauth/revoke`,
scopes_supported: ['openid', 'profile', 'email', 'organizations', 'roles'],
response_types_supported: ['code'],
grant_types_supported: ['authorization_code', 'refresh_token'],
subject_types_supported: ['public'],
id_token_signing_alg_values_supported: ['RS256'],
token_endpoint_auth_methods_supported: ['client_secret_basic', 'client_secret_post'],
code_challenge_methods_supported: ['S256'],
claims_supported: [
'sub', 'iss', 'aud', 'exp', 'iat', 'auth_time', 'nonce',
'name', 'preferred_username', 'picture',
'email', 'email_verified',
'organizations', 'roles'
],
};
}
/**
* Get the JSON Web Key Set (JWKS)
*/
public getJwks(): plugins.idpInterfaces.data.IJwks {
const keypair = this.receptionRef.jwtManager.smartjwtInstance.getKeyPairAsJson();
// Convert PEM to JWK format
const jwk = this.pemToJwk(keypair.publicPem);
return {
keys: [jwk],
};
}
/**
* Convert PEM public key to JWK format
*/
private pemToJwk(publicPem: string): plugins.idpInterfaces.data.IJwk {
// For now, use a simplified approach - in production, parse the PEM properly
// The smartjwt library should provide this, or use crypto.createPublicKey
const kid = plugins.smarthash.sha256FromStringSync(publicPem).substring(0, 16);
// This is a placeholder - proper implementation would extract n and e from PEM
// For now, return a minimal structure
return {
kty: 'RSA',
use: 'sig',
alg: 'RS256',
kid: kid,
// These would be extracted from the actual public key
n: Buffer.from(publicPem).toString('base64url').substring(0, 256),
e: 'AQAB', // Standard RSA exponent (65537)
};
}
/**
* Handle the authorization endpoint request
*/
public async handleAuthorize(request: Request): Promise<Response> {
const url = new URL(request.url);
const params = url.searchParams;
// Extract authorization request parameters
const clientId = params.get('client_id');
const redirectUri = params.get('redirect_uri');
const responseType = params.get('response_type');
const scope = params.get('scope');
const state = params.get('state');
const codeChallenge = params.get('code_challenge');
const codeChallengeMethod = params.get('code_challenge_method');
const nonce = params.get('nonce');
const prompt = params.get('prompt') as 'none' | 'login' | 'consent' | null;
// Validate required parameters
if (!clientId || !redirectUri || !responseType || !scope || !state) {
return this.errorResponse('invalid_request', 'Missing required parameters');
}
if (responseType !== 'code') {
return this.errorResponse('unsupported_response_type', 'Only code response type is supported');
}
// Validate code challenge method if present
if (codeChallenge && codeChallengeMethod !== 'S256') {
return this.errorResponse('invalid_request', 'Only S256 code challenge method is supported');
}
// Find the app by client_id
const app = await this.findAppByClientId(clientId);
if (!app) {
return this.errorResponse('invalid_client', 'Unknown client_id');
}
// Validate redirect URI
if (!app.data.oauthCredentials.redirectUris.includes(redirectUri)) {
return this.errorResponse('invalid_request', 'Invalid redirect_uri');
}
// Parse and validate scopes
const requestedScopes = scope.split(' ') as plugins.idpInterfaces.data.TOidcScope[];
const allowedScopes = app.data.oauthCredentials.allowedScopes as plugins.idpInterfaces.data.TOidcScope[];
const validScopes = requestedScopes.filter(s => allowedScopes.includes(s));
if (!validScopes.includes('openid')) {
return this.errorResponse('invalid_scope', 'openid scope is required');
}
// For now, redirect to login page with OAuth parameters
// The login page will handle authentication and call back to complete authorization
const baseUrl = this.receptionRef.options.baseUrl || 'https://idp.global';
const loginUrl = new URL(`${baseUrl}/login`);
loginUrl.searchParams.set('oauth', 'true');
loginUrl.searchParams.set('client_id', clientId);
loginUrl.searchParams.set('redirect_uri', redirectUri);
loginUrl.searchParams.set('scope', validScopes.join(' '));
loginUrl.searchParams.set('state', state);
if (codeChallenge) {
loginUrl.searchParams.set('code_challenge', codeChallenge);
loginUrl.searchParams.set('code_challenge_method', codeChallengeMethod!);
}
if (nonce) {
loginUrl.searchParams.set('nonce', nonce);
}
return Response.redirect(loginUrl.toString(), 302);
}
/**
* Generate an authorization code after user authentication
*/
public async generateAuthorizationCode(
clientId: string,
userId: string,
scopes: plugins.idpInterfaces.data.TOidcScope[],
redirectUri: string,
codeChallenge?: string,
nonce?: string
): Promise<string> {
const code = plugins.smartunique.shortId(32);
const authCode: plugins.idpInterfaces.data.IAuthorizationCode = {
code,
clientId,
userId,
scopes,
redirectUri,
codeChallenge,
codeChallengeMethod: codeChallenge ? 'S256' : undefined,
nonce,
expiresAt: Date.now() + 10 * 60 * 1000, // 10 minutes
used: false,
};
this.authorizationCodes.set(code, authCode);
return code;
}
/**
* Handle the token endpoint request
*/
public async handleToken(request: Request): Promise<Response> {
// Parse form data
const contentType = request.headers.get('content-type');
if (!contentType?.includes('application/x-www-form-urlencoded')) {
return this.tokenErrorResponse('invalid_request', 'Content-Type must be application/x-www-form-urlencoded');
}
const formData = await request.formData();
const grantType = formData.get('grant_type') as string;
// Extract client credentials from Basic auth or form
let clientId = formData.get('client_id') as string;
let clientSecret = formData.get('client_secret') as string;
const authHeader = request.headers.get('authorization');
if (authHeader?.startsWith('Basic ')) {
const base64 = authHeader.substring(6);
const decoded = Buffer.from(base64, 'base64').toString('utf-8');
const [id, secret] = decoded.split(':');
clientId = clientId || id;
clientSecret = clientSecret || secret;
}
if (!clientId) {
return this.tokenErrorResponse('invalid_client', 'Missing client_id');
}
// Find and validate app
const app = await this.findAppByClientId(clientId);
if (!app) {
return this.tokenErrorResponse('invalid_client', 'Unknown client');
}
// Validate client secret for confidential clients
if (clientSecret) {
const secretHash = await plugins.smarthash.sha256FromString(clientSecret);
if (secretHash !== app.data.oauthCredentials.clientSecretHash) {
return this.tokenErrorResponse('invalid_client', 'Invalid client credentials');
}
}
if (grantType === 'authorization_code') {
return this.handleAuthorizationCodeGrant(formData, app);
} else if (grantType === 'refresh_token') {
return this.handleRefreshTokenGrant(formData, app);
} else {
return this.tokenErrorResponse('unsupported_grant_type', 'Unsupported grant type');
}
}
/**
* Handle authorization_code grant type
*/
private async handleAuthorizationCodeGrant(
formData: FormData,
app: App
): Promise<Response> {
const code = formData.get('code') as string;
const redirectUri = formData.get('redirect_uri') as string;
const codeVerifier = formData.get('code_verifier') as string;
if (!code || !redirectUri) {
return this.tokenErrorResponse('invalid_request', 'Missing code or redirect_uri');
}
// Find and validate authorization code
const authCode = this.authorizationCodes.get(code);
if (!authCode) {
return this.tokenErrorResponse('invalid_grant', 'Invalid authorization code');
}
if (authCode.used) {
// Code reuse attack - revoke all tokens for this code
this.authorizationCodes.delete(code);
return this.tokenErrorResponse('invalid_grant', 'Authorization code already used');
}
if (authCode.expiresAt < Date.now()) {
this.authorizationCodes.delete(code);
return this.tokenErrorResponse('invalid_grant', 'Authorization code expired');
}
if (authCode.clientId !== app.data.oauthCredentials.clientId) {
return this.tokenErrorResponse('invalid_grant', 'Client ID mismatch');
}
if (authCode.redirectUri !== redirectUri) {
return this.tokenErrorResponse('invalid_grant', 'Redirect URI mismatch');
}
// Verify PKCE if code challenge was used
if (authCode.codeChallenge) {
if (!codeVerifier) {
return this.tokenErrorResponse('invalid_grant', 'Code verifier required');
}
const expectedChallenge = this.generateS256Challenge(codeVerifier);
if (expectedChallenge !== authCode.codeChallenge) {
return this.tokenErrorResponse('invalid_grant', 'Invalid code verifier');
}
}
// Mark code as used
authCode.used = true;
// Generate tokens
const tokens = await this.generateTokens(
authCode.userId,
app.data.oauthCredentials.clientId,
authCode.scopes,
authCode.nonce
);
return new Response(JSON.stringify(tokens), {
status: 200,
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'no-store',
'Pragma': 'no-cache',
},
});
}
/**
* Handle refresh_token grant type
*/
private async handleRefreshTokenGrant(
formData: FormData,
app: App
): Promise<Response> {
const refreshToken = formData.get('refresh_token') as string;
if (!refreshToken) {
return this.tokenErrorResponse('invalid_request', 'Missing refresh_token');
}
const tokenHash = await plugins.smarthash.sha256FromString(refreshToken);
const storedToken = this.refreshTokens.get(tokenHash);
if (!storedToken) {
return this.tokenErrorResponse('invalid_grant', 'Invalid refresh token');
}
if (storedToken.revoked) {
return this.tokenErrorResponse('invalid_grant', 'Refresh token has been revoked');
}
if (storedToken.expiresAt < Date.now()) {
this.refreshTokens.delete(tokenHash);
return this.tokenErrorResponse('invalid_grant', 'Refresh token expired');
}
if (storedToken.clientId !== app.data.oauthCredentials.clientId) {
return this.tokenErrorResponse('invalid_grant', 'Client ID mismatch');
}
// Generate new tokens (without new refresh token by default)
const tokens = await this.generateTokens(
storedToken.userId,
storedToken.clientId,
storedToken.scopes,
undefined,
false // Don't generate new refresh token
);
return new Response(JSON.stringify(tokens), {
status: 200,
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'no-store',
'Pragma': 'no-cache',
},
});
}
/**
* Generate access token, ID token, and optionally refresh token
*/
private async generateTokens(
userId: string,
clientId: string,
scopes: plugins.idpInterfaces.data.TOidcScope[],
nonce?: string,
includeRefreshToken = true
): Promise<plugins.idpInterfaces.data.ITokenResponse> {
const now = Date.now();
const accessTokenLifetime = 3600; // 1 hour
const refreshTokenLifetime = 30 * 24 * 3600; // 30 days
// Generate access token
const accessToken = plugins.smartunique.shortId(32);
const accessTokenHash = await plugins.smarthash.sha256FromString(accessToken);
const accessTokenData: plugins.idpInterfaces.data.IOidcAccessToken = {
id: plugins.smartunique.shortId(8),
tokenHash: accessTokenHash,
clientId,
userId,
scopes,
expiresAt: now + accessTokenLifetime * 1000,
issuedAt: now,
};
this.accessTokens.set(accessTokenHash, accessTokenData);
// Generate ID token (JWT)
const idToken = await this.generateIdToken(userId, clientId, scopes, nonce);
const response: plugins.idpInterfaces.data.ITokenResponse = {
access_token: accessToken,
token_type: 'Bearer',
expires_in: accessTokenLifetime,
id_token: idToken,
scope: scopes.join(' '),
};
// Generate refresh token if requested
if (includeRefreshToken) {
const refreshToken = plugins.smartunique.shortId(48);
const refreshTokenHash = await plugins.smarthash.sha256FromString(refreshToken);
const refreshTokenData: plugins.idpInterfaces.data.IOidcRefreshToken = {
id: plugins.smartunique.shortId(8),
tokenHash: refreshTokenHash,
clientId,
userId,
scopes,
expiresAt: now + refreshTokenLifetime * 1000,
issuedAt: now,
revoked: false,
};
this.refreshTokens.set(refreshTokenHash, refreshTokenData);
response.refresh_token = refreshToken;
}
return response;
}
/**
* Generate an ID token (JWT)
*/
private async generateIdToken(
userId: string,
clientId: string,
scopes: plugins.idpInterfaces.data.TOidcScope[],
nonce?: string
): Promise<string> {
const baseUrl = this.receptionRef.options.baseUrl || 'https://idp.global';
const now = Math.floor(Date.now() / 1000);
const claims: plugins.idpInterfaces.data.IIdTokenClaims = {
iss: baseUrl,
sub: userId,
aud: clientId,
exp: now + 3600, // 1 hour
iat: now,
auth_time: now,
};
if (nonce) {
claims.nonce = nonce;
}
// Add claims based on scopes
if (scopes.includes('profile') || scopes.includes('email') || scopes.includes('organizations') || scopes.includes('roles')) {
const userInfo = await this.getUserClaims(userId, scopes);
Object.assign(claims, userInfo);
}
// Sign the JWT
const idToken = await this.receptionRef.jwtManager.smartjwtInstance.createJWT(claims);
return idToken;
}
/**
* Handle the userinfo endpoint
*/
public async handleUserInfo(request: Request): Promise<Response> {
// Get access token from Authorization header
const authHeader = request.headers.get('authorization');
if (!authHeader?.startsWith('Bearer ')) {
return new Response(JSON.stringify({ error: 'invalid_token' }), {
status: 401,
headers: {
'Content-Type': 'application/json',
'WWW-Authenticate': 'Bearer error="invalid_token"',
},
});
}
const accessToken = authHeader.substring(7);
const tokenHash = await plugins.smarthash.sha256FromString(accessToken);
const tokenData = this.accessTokens.get(tokenHash);
if (!tokenData) {
return new Response(JSON.stringify({ error: 'invalid_token' }), {
status: 401,
headers: {
'Content-Type': 'application/json',
'WWW-Authenticate': 'Bearer error="invalid_token"',
},
});
}
if (tokenData.expiresAt < Date.now()) {
this.accessTokens.delete(tokenHash);
return new Response(JSON.stringify({ error: 'invalid_token', error_description: 'Token expired' }), {
status: 401,
headers: {
'Content-Type': 'application/json',
'WWW-Authenticate': 'Bearer error="invalid_token", error_description="Token expired"',
},
});
}
// Get user claims based on token scopes
const userInfo = await this.getUserClaims(tokenData.userId, tokenData.scopes);
return new Response(JSON.stringify(userInfo), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
/**
* Get user claims based on scopes
*/
private async getUserClaims(
userId: string,
scopes: plugins.idpInterfaces.data.TOidcScope[]
): Promise<plugins.idpInterfaces.data.IUserInfoResponse> {
const user = await this.receptionRef.userManager.CUser.getInstance({ id: userId });
if (!user) {
return { sub: userId };
}
const claims: plugins.idpInterfaces.data.IUserInfoResponse = {
sub: userId,
};
// Profile scope
if (scopes.includes('profile')) {
claims.name = user.data?.name;
claims.preferred_username = user.data?.username;
// claims.picture = user.data?.avatarUrl; // If avatar exists
}
// Email scope
if (scopes.includes('email')) {
claims.email = user.data?.email;
claims.email_verified = user.data?.status === 'active';
}
// Organizations scope (custom)
if (scopes.includes('organizations')) {
const organizations = await this.receptionRef.organizationmanager.getAllOrganizationsForUser(user);
const roles = await this.receptionRef.roleManager.getAllRolesForUser(user);
if (organizations) {
claims.organizations = organizations.map(org => ({
id: org.id,
name: org.data?.name || '',
slug: org.data?.slug || '',
roles: roles
.find(r => r.data?.organizationId === org.id)?.data?.roles || [],
}));
}
}
// Roles scope (custom - global roles)
if (scopes.includes('roles')) {
const roles: string[] = ['user'];
if (user.data?.isGlobalAdmin) {
roles.push('admin');
}
claims.roles = roles;
}
return claims;
}
/**
* Handle the revocation endpoint
*/
public async handleRevoke(request: Request): Promise<Response> {
const formData = await request.formData();
const token = formData.get('token') as string;
const tokenTypeHint = formData.get('token_type_hint') as string;
if (!token) {
return new Response(null, { status: 200 }); // Spec says always return 200
}
const tokenHash = await plugins.smarthash.sha256FromString(token);
// Try to revoke as refresh token
if (!tokenTypeHint || tokenTypeHint === 'refresh_token') {
const refreshToken = this.refreshTokens.get(tokenHash);
if (refreshToken) {
refreshToken.revoked = true;
return new Response(null, { status: 200 });
}
}
// Try to revoke as access token
if (!tokenTypeHint || tokenTypeHint === 'access_token') {
if (this.accessTokens.has(tokenHash)) {
this.accessTokens.delete(tokenHash);
return new Response(null, { status: 200 });
}
}
// Token not found - still return 200 per spec
return new Response(null, { status: 200 });
}
/**
* Find an app by its OAuth client_id
*/
private async findAppByClientId(clientId: string): Promise<App | null> {
const apps = await this.receptionRef.appManager.CApp.getInstances({
'data.oauthCredentials.clientId': clientId,
});
return apps[0] || null;
}
/**
* Generate S256 PKCE challenge from verifier
*/
private generateS256Challenge(verifier: string): string {
const hash = plugins.smarthash.sha256FromStringSync(verifier);
return Buffer.from(hash, 'hex').toString('base64url');
}
/**
* Create an error response for authorization endpoint
*/
private errorResponse(error: string, description: string): Response {
return new Response(JSON.stringify({ error, error_description: description }), {
status: 400,
headers: { 'Content-Type': 'application/json' },
});
}
/**
* Create an error response for token endpoint
*/
private tokenErrorResponse(
error: plugins.idpInterfaces.data.ITokenErrorResponse['error'],
description: string
): Response {
const body: plugins.idpInterfaces.data.ITokenErrorResponse = {
error,
error_description: description,
};
return new Response(JSON.stringify(body), {
status: 400,
headers: { 'Content-Type': 'application/json' },
});
}
/**
* Start cleanup task for expired tokens/codes
*/
private startCleanupTask(): void {
setInterval(() => {
const now = Date.now();
// Clean up expired authorization codes
for (const [code, data] of this.authorizationCodes) {
if (data.expiresAt < now) {
this.authorizationCodes.delete(code);
}
}
// Clean up expired access tokens
for (const [hash, data] of this.accessTokens) {
if (data.expiresAt < now) {
this.accessTokens.delete(hash);
}
}
// Clean up expired refresh tokens
for (const [hash, data] of this.refreshTokens) {
if (data.expiresAt < now) {
this.refreshTokens.delete(hash);
}
}
}, 60 * 1000); // Run every minute
}
}
+2
View File
@@ -17,6 +17,7 @@ import { AppManager } from './classes.appmanager.js';
import { AppConnectionManager } from './classes.appconnectionmanager.js';
import { ActivityLogManager } from './classes.activitylogmanager.js';
import { UserInvitationManager } from './classes.userinvitationmanager.js';
import { OidcManager } from './classes.oidcmanager.js';
export interface IReceptionOptions {
/**
@@ -49,6 +50,7 @@ export class Reception {
public appConnectionManager = new AppConnectionManager(this);
public activityLogManager = new ActivityLogManager(this);
public userInvitationManager = new UserInvitationManager(this);
public oidcManager = new OidcManager(this);
housekeeping = new ReceptionHousekeeping(this);
constructor(public options: IReceptionOptions) {
+1
View File
@@ -1,5 +1,6 @@
export * from './loint-reception.activity.js';
export * from './loint-reception.app.js';
export * from './loint-reception.oidc.js';
export * from './loint-reception.appconnection.js';
export * from './loint-reception.billingplan.js';
export * from './loint-reception.device.js';
+267
View File
@@ -0,0 +1,267 @@
/**
* OIDC (OpenID Connect) data interfaces for third-party client support
*/
/**
* Supported OIDC scopes
*/
export type TOidcScope = 'openid' | 'profile' | 'email' | 'organizations' | 'roles';
/**
* Authorization code for OAuth 2.0 authorization code flow
*/
export interface IAuthorizationCode {
/** The authorization code string */
code: string;
/** OAuth client ID */
clientId: string;
/** User ID who authorized */
userId: string;
/** Scopes granted */
scopes: TOidcScope[];
/** Redirect URI used in authorization request */
redirectUri: string;
/** PKCE code challenge (S256 hashed) */
codeChallenge?: string;
/** PKCE code challenge method */
codeChallengeMethod?: 'S256';
/** Nonce from authorization request (for ID token) */
nonce?: string;
/** Expiration timestamp (10 minutes from creation) */
expiresAt: number;
/** Whether the code has been used (single-use) */
used: boolean;
}
/**
* OIDC Access Token (opaque or JWT)
*/
export interface IOidcAccessToken {
/** Token identifier */
id: string;
/** The access token string (or hash for storage) */
tokenHash: string;
/** OAuth client ID */
clientId: string;
/** User ID */
userId: string;
/** Granted scopes */
scopes: TOidcScope[];
/** Expiration timestamp */
expiresAt: number;
/** Creation timestamp */
issuedAt: number;
}
/**
* OIDC Refresh Token
*/
export interface IOidcRefreshToken {
/** Token identifier */
id: string;
/** The refresh token string (or hash for storage) */
tokenHash: string;
/** OAuth client ID */
clientId: string;
/** User ID */
userId: string;
/** Granted scopes */
scopes: TOidcScope[];
/** Expiration timestamp */
expiresAt: number;
/** Creation timestamp */
issuedAt: number;
/** Whether the token has been revoked */
revoked: boolean;
}
/**
* User consent record for an OAuth client
*/
export interface IUserConsent {
/** Unique identifier */
id: string;
/** User who gave consent */
userId: string;
/** OAuth client ID */
clientId: string;
/** Scopes the user consented to */
scopes: TOidcScope[];
/** When consent was granted */
grantedAt: number;
/** When consent was last updated */
updatedAt: number;
}
/**
* OIDC Discovery Document (OpenID Provider Configuration)
*/
export interface IOidcDiscoveryDocument {
issuer: string;
authorization_endpoint: string;
token_endpoint: string;
userinfo_endpoint: string;
jwks_uri: string;
revocation_endpoint: string;
scopes_supported: TOidcScope[];
response_types_supported: string[];
grant_types_supported: string[];
subject_types_supported: string[];
id_token_signing_alg_values_supported: string[];
token_endpoint_auth_methods_supported: string[];
code_challenge_methods_supported: string[];
claims_supported: string[];
}
/**
* JSON Web Key Set (JWKS) response
*/
export interface IJwks {
keys: IJwk[];
}
/**
* JSON Web Key (RSA public key)
*/
export interface IJwk {
kty: 'RSA';
use: 'sig';
alg: 'RS256';
kid: string;
n: string; // RSA modulus (base64url encoded)
e: string; // RSA exponent (base64url encoded)
}
/**
* ID Token claims (JWT payload)
*/
export interface IIdTokenClaims {
/** Issuer (idp.global URL) */
iss: string;
/** Subject (user ID) */
sub: string;
/** Audience (client ID) */
aud: string;
/** Expiration time (Unix timestamp) */
exp: number;
/** Issued at (Unix timestamp) */
iat: number;
/** Authentication time (Unix timestamp) */
auth_time?: number;
/** Nonce (if provided in authorization request) */
nonce?: string;
/** Access token hash (for hybrid flows) */
at_hash?: string;
// Profile scope claims
name?: string;
preferred_username?: string;
picture?: string;
// Email scope claims
email?: string;
email_verified?: boolean;
// Custom claims for organizations scope
organizations?: IOrganizationClaim[];
// Custom claims for roles scope
roles?: string[];
}
/**
* Organization claim in ID token / userinfo
*/
export interface IOrganizationClaim {
id: string;
name: string;
slug: string;
roles: string[];
}
/**
* UserInfo endpoint response
*/
export interface IUserInfoResponse {
/** Subject (user ID) - always included */
sub: string;
// Profile scope
name?: string;
preferred_username?: string;
picture?: string;
// Email scope
email?: string;
email_verified?: boolean;
// Organizations scope (custom)
organizations?: IOrganizationClaim[];
// Roles scope (custom)
roles?: string[];
}
/**
* Token endpoint response
*/
export interface ITokenResponse {
access_token: string;
token_type: 'Bearer';
expires_in: number;
refresh_token?: string;
id_token?: string;
scope: string;
}
/**
* Token endpoint error response
*/
export interface ITokenErrorResponse {
error: 'invalid_request' | 'invalid_client' | 'invalid_grant' | 'unauthorized_client' | 'unsupported_grant_type' | 'invalid_scope';
error_description?: string;
error_uri?: string;
}
/**
* Authorization request parameters
*/
export interface IAuthorizationRequest {
client_id: string;
redirect_uri: string;
response_type: 'code';
scope: string;
state: string;
code_challenge?: string;
code_challenge_method?: 'S256';
nonce?: string;
prompt?: 'none' | 'login' | 'consent';
}
/**
* Token request for authorization_code grant
*/
export interface ITokenRequestAuthCode {
grant_type: 'authorization_code';
code: string;
redirect_uri: string;
client_id: string;
client_secret?: string;
code_verifier?: string;
}
/**
* Token request for refresh_token grant
*/
export interface ITokenRequestRefresh {
grant_type: 'refresh_token';
refresh_token: string;
client_id: string;
client_secret?: string;
scope?: string;
}
/**
* Union type for token requests
*/
export type ITokenRequest = ITokenRequestAuthCode | ITokenRequestRefresh;
+1 -1
View File
@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@idp.global/idp.global',
version: '1.12.0',
version: '1.13.0',
description: 'An identity provider software managing user authentications, registrations, and sessions.'
}