mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-20 13:51:45 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 842d9760df | |||
| 58218ff5f6 |
@@ -1,5 +1,5 @@
|
|||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
name: trek
|
name: trek
|
||||||
version: 3.0.3
|
version: 3.0.4
|
||||||
description: Minimal Helm chart for TREK app
|
description: Minimal Helm chart for TREK app
|
||||||
appVersion: "3.0.3"
|
appVersion: "3.0.4"
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "trek-client",
|
"name": "trek-client",
|
||||||
"version": "3.0.3",
|
"version": "3.0.4",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "trek-client",
|
"name": "trek-client",
|
||||||
"version": "3.0.3",
|
"version": "3.0.4",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-pdf/renderer": "^4.3.2",
|
"@react-pdf/renderer": "^4.3.2",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "trek-client",
|
"name": "trek-client",
|
||||||
"version": "3.0.3",
|
"version": "3.0.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default function ConfirmDialog({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 z-[10000] flex items-center justify-center px-4 trek-backdrop-enter"
|
className="fixed inset-0 z-[10000] flex items-center justify-center px-4 trek-backdrop-enter"
|
||||||
style={{ backgroundColor: 'rgba(15, 23, 42, 0.5)' }}
|
style={{ backgroundColor: 'rgba(15, 23, 42, 0.5)', paddingBottom: 'var(--bottom-nav-h)' }}
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export default function CopyTripDialog({ isOpen, tripTitle, onClose, onConfirm }
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 z-[10000] flex items-center justify-center px-4 trek-backdrop-enter"
|
className="fixed inset-0 z-[10000] flex items-center justify-center px-4 trek-backdrop-enter"
|
||||||
style={{ backgroundColor: 'rgba(15, 23, 42, 0.5)' }}
|
style={{ backgroundColor: 'rgba(15, 23, 42, 0.5)', paddingBottom: 'var(--bottom-nav-h)' }}
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "trek-server",
|
"name": "trek-server",
|
||||||
"version": "3.0.3",
|
"version": "3.0.4",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "trek-server",
|
"name": "trek-server",
|
||||||
"version": "3.0.3",
|
"version": "3.0.4",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.28.0",
|
"@modelcontextprotocol/sdk": "^1.28.0",
|
||||||
"archiver": "^6.0.1",
|
"archiver": "^6.0.1",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "trek-server",
|
"name": "trek-server",
|
||||||
"version": "3.0.3",
|
"version": "3.0.4",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node --import tsx src/index.ts",
|
"start": "node --import tsx src/index.ts",
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ router.get('/callback', async (req: Request, res: Response) => {
|
|||||||
tokenData.id_token,
|
tokenData.id_token,
|
||||||
doc,
|
doc,
|
||||||
config.clientId,
|
config.clientId,
|
||||||
config.issuer,
|
(doc.issuer ?? '').replace(/\/+$/, '') || config.issuer,
|
||||||
);
|
);
|
||||||
if (idVerify.ok !== true) {
|
if (idVerify.ok !== true) {
|
||||||
const reason = 'error' in idVerify ? idVerify.error : 'unknown';
|
const reason = 'error' in idVerify ? idVerify.error : 'unknown';
|
||||||
|
|||||||
@@ -140,11 +140,21 @@ export async function discover(issuer: string, discoveryUrl?: string | null): Pr
|
|||||||
const res = await fetch(url);
|
const res = await fetch(url);
|
||||||
if (!res.ok) throw new Error('Failed to fetch OIDC discovery document');
|
if (!res.ok) throw new Error('Failed to fetch OIDC discovery document');
|
||||||
const doc = (await res.json()) as OidcDiscoveryDoc;
|
const doc = (await res.json()) as OidcDiscoveryDoc;
|
||||||
// Validate that the discovery doc's issuer matches the operator-configured
|
// Validate that the discovery doc's issuer matches the operator-configured one.
|
||||||
// one. A MITM or compromised doc could otherwise supply a crafted issuer
|
// When no custom discoveryUrl is set, a mismatch signals a MITM or misconfiguration
|
||||||
// that passes jwt.verify() because we used doc.issuer as the expected value.
|
// and we reject. When the operator explicitly overrides the discovery URL (e.g.
|
||||||
if (doc.issuer && doc.issuer.replace(/\/+$/, '') !== issuer) {
|
// Authentik realm paths), the discovery doc's issuer is the canonical value —
|
||||||
throw new Error(`OIDC discovery issuer mismatch: expected "${issuer}", got "${doc.issuer}"`);
|
// trust it and warn rather than blocking login.
|
||||||
|
const docIssuer = doc.issuer?.replace(/\/+$/, '') ?? '';
|
||||||
|
if (docIssuer && docIssuer !== issuer) {
|
||||||
|
if (discoveryUrl) {
|
||||||
|
console.warn(
|
||||||
|
`[OIDC] Discovery doc issuer "${doc.issuer}" differs from configured OIDC_ISSUER "${issuer}". ` +
|
||||||
|
`Using discovery doc issuer for id_token verification (custom OIDC_DISCOVERY_URL is set).`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new Error(`OIDC discovery issuer mismatch: expected "${issuer}", got "${doc.issuer}"`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
doc._issuer = url;
|
doc._issuer = url;
|
||||||
discoveryCache = doc;
|
discoveryCache = doc;
|
||||||
|
|||||||
@@ -219,6 +219,59 @@ describe('discover', () => {
|
|||||||
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: false }));
|
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: false }));
|
||||||
await expect(discover('https://bad-issuer.example.com')).rejects.toThrow();
|
await expect(discover('https://bad-issuer.example.com')).rejects.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('OIDC-SVC-037: accepts mismatched doc issuer when discoveryUrl is explicit', async () => {
|
||||||
|
const doc = {
|
||||||
|
issuer: 'https://auth.example.com/application/o/myapp/',
|
||||||
|
authorization_endpoint: 'https://auth.example.com/application/o/myapp/authorize/',
|
||||||
|
token_endpoint: 'https://auth.example.com/application/o/token/',
|
||||||
|
userinfo_endpoint: 'https://auth.example.com/application/o/userinfo/',
|
||||||
|
};
|
||||||
|
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: true, json: async () => doc }));
|
||||||
|
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||||
|
|
||||||
|
const result = await discover(
|
||||||
|
'https://auth.example.com',
|
||||||
|
'https://auth.example.com/application/o/myapp/.well-known/openid-configuration',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.issuer).toBe(doc.issuer);
|
||||||
|
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('differs from configured OIDC_ISSUER'));
|
||||||
|
warnSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('OIDC-SVC-038: throws on mismatched doc issuer when discoveryUrl is omitted', async () => {
|
||||||
|
const doc = {
|
||||||
|
issuer: 'https://evil.example.com',
|
||||||
|
authorization_endpoint: 'https://unique-2.example.com/auth',
|
||||||
|
token_endpoint: 'https://unique-2.example.com/token',
|
||||||
|
userinfo_endpoint: 'https://unique-2.example.com/userinfo',
|
||||||
|
};
|
||||||
|
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: true, json: async () => doc }));
|
||||||
|
|
||||||
|
await expect(discover('https://unique-2.example.com')).rejects.toThrow(
|
||||||
|
'OIDC discovery issuer mismatch',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('OIDC-SVC-039: trailing-slash-only mismatch with explicit discoveryUrl does not warn', async () => {
|
||||||
|
const doc = {
|
||||||
|
issuer: 'https://auth.example.com/',
|
||||||
|
authorization_endpoint: 'https://auth.example.com/auth',
|
||||||
|
token_endpoint: 'https://auth.example.com/token',
|
||||||
|
userinfo_endpoint: 'https://auth.example.com/userinfo',
|
||||||
|
};
|
||||||
|
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: true, json: async () => doc }));
|
||||||
|
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||||
|
|
||||||
|
await discover(
|
||||||
|
'https://auth.example.com',
|
||||||
|
'https://auth.example.com/.well-known/openid-configuration',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(warnSpy).not.toHaveBeenCalled();
|
||||||
|
warnSpy.mockRestore();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── issuer trailing-slash regex (ReDoS guard) ─────────────────────────────────
|
// ── issuer trailing-slash regex (ReDoS guard) ─────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user