Skip to content

Commit 02095ba

Browse files
v0.0.54 - Fix SSRF false positive for own issuer
When the server validates tokens from its own IdP, the SSRF check was blocking because the hostname resolves to localhost internally. Added trusted issuer whitelist - the server's own issuer is now automatically trusted when the IdP plugin initializes.
1 parent 33a14e8 commit 02095ba

3 files changed

Lines changed: 32 additions & 9 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "javascript-solid-server",
3-
"version": "0.0.53",
3+
"version": "0.0.54",
44
"description": "A minimal, fast Solid server",
55
"main": "src/index.js",
66
"type": "module",

src/auth/solid-oidc.js

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,19 @@ const jwksCache = new Map();
2020
// Cache TTL (15 minutes)
2121
const CACHE_TTL = 15 * 60 * 1000;
2222

23+
// Trusted issuers (skip SSRF check) - populated by server config
24+
const trustedIssuers = new Set();
25+
26+
/**
27+
* Add a trusted issuer (e.g., the server's own issuer)
28+
* Trusted issuers bypass SSRF validation since they're configured by admin
29+
*/
30+
export function addTrustedIssuer(issuer) {
31+
const normalized = issuer.replace(/\/$/, '');
32+
trustedIssuers.add(normalized);
33+
trustedIssuers.add(normalized + '/');
34+
}
35+
2336
// DPoP proof max age (5 minutes)
2437
const DPOP_MAX_AGE = 5 * 60;
2538

@@ -206,15 +219,21 @@ async function getOidcConfig(issuer) {
206219
return cached.config;
207220
}
208221

209-
// SSRF Protection: Validate issuer URL before fetching
210-
const validation = await validateExternalUrl(issuer, {
211-
requireHttps: true,
212-
blockPrivateIPs: true,
213-
resolveDNS: true
214-
});
222+
// Check if this is a trusted issuer (e.g., our own server)
223+
const normalizedIssuer = issuer.replace(/\/$/, '');
224+
const isTrusted = trustedIssuers.has(normalizedIssuer) || trustedIssuers.has(normalizedIssuer + '/');
225+
226+
// SSRF Protection: Validate issuer URL before fetching (skip for trusted issuers)
227+
if (!isTrusted) {
228+
const validation = await validateExternalUrl(issuer, {
229+
requireHttps: true,
230+
blockPrivateIPs: true,
231+
resolveDNS: true
232+
});
215233

216-
if (!validation.valid) {
217-
throw new Error(`Invalid OIDC issuer: ${validation.error}`);
234+
if (!validation.valid) {
235+
throw new Error(`Invalid OIDC issuer: ${validation.error}`);
236+
}
218237
}
219238

220239
const configUrl = `${issuer.replace(/\/$/, '')}/.well-known/openid-configuration`;

src/idp/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
handleCredentials,
1919
handleCredentialsInfo,
2020
} from './credentials.js';
21+
import { addTrustedIssuer } from '../auth/solid-oidc.js';
2122

2223
/**
2324
* IdP Fastify Plugin
@@ -32,6 +33,9 @@ export async function idpPlugin(fastify, options) {
3233
throw new Error('IdP requires issuer URL');
3334
}
3435

36+
// Register our own issuer as trusted (bypasses SSRF check for self-validation)
37+
addTrustedIssuer(issuer);
38+
3539
// Initialize signing keys
3640
await initializeKeys();
3741

0 commit comments

Comments
 (0)