medusajs/medusa · error · MedusaError
OIDC '${label}' must use https (http is only allowed for loc
Error message
OIDC '${label}' must use https (http is only allowed for localhost outside of production) What it means
assertSecureUrl requires https for both issuer and callback_url. Plain http is tolerated only when the hostname is localhost/127.0.0.1/::1 AND the process is not running in production (NODE_ENV=production); otherwise it throws INVALID_DATA.
Source
Thrown at packages/modules/providers/auth-oidc/src/utils/assert-secure-url.ts:27
try {
url = new URL(value)
} catch (e) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`OIDC '${label}' must be a valid URL`
)
}
const isLocalhost =
url.hostname === "localhost" ||
url.hostname === "127.0.0.1" ||
url.hostname === "::1" ||
url.hostname === "[::1]"
const allowsHttp = isLocalhost && !isProduction()
if (url.protocol !== "https:" && !(url.protocol === "http:" && allowsHttp)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`OIDC '${label}' must use https (http is only allowed for localhost outside of production)`
)
}
}
View on GitHub (pinned to 5e06e544a2)
Solutions
- Use https URLs for issuer and callback_url in production (e.g. put your domain behind a TLS proxy/CDN).
- Derive callback_url from the forwarded public origin (e.g. trust X-Forwarded-Proto) instead of the internal request host.
- For non-production testing on non-localhost hosts, either add a hosts mapping to localhost or run NODE_ENV != production.
Example fix
// before
options: { issuer: "http://auth.internal:8080", callback_url: "http://app.internal:9000/auth/customer/oidc/callback" } // NODE_ENV=production -> throws
// after
options: { issuer: "https://auth.example.com", callback_url: "https://app.example.com/auth/customer/oidc/callback" } Defensive patterns
Strategy: validation
Validate before calling
const u = new URL(options.issuer)
const isLocal = ["localhost","127.0.0.1","::1"].includes(u.hostname)
if (process.env.NODE_ENV === "production" && u.protocol !== "https:") {
throw new Error("production OIDC URLs must use https")
} Type guard
const isSecureEnoughUrl = (v: string): boolean => { try { const u = new URL(v); return u.protocol === "https:" || (["localhost","127.0.0.1","::1"].includes(u.hostname) && process.env.NODE_ENV !== "production") } catch { return false } } Try / catch
try { assertSecureUrl(value, label) } catch (e) { if (e instanceof MedusaError && /must use https/.test(e.message)) { /* switch to https or non-production localhost */ } throw e } Prevention
- Terminate TLS before the app and build public URLs from X-Forwarded-Proto.
- Keep separate env files per environment with correct schemes.
- Never ship http IdP URLs with NODE_ENV=production.
When it happens
Trigger: Deploying with issuer or callback_url on http:// while NODE_ENV=production; using an http staging host that is not localhost; http callback through a non-localhost docker hostname in prod mode.
Common situations: Config built for local dev (http://localhost:8000) promoted to production where the public URL must be https; sitting behind a TLS-terminating proxy but the app constructs the callback from an internal http base URL; NODE_ENV left as production during a docker-compose test using http service names.
Related errors
- OIDC engine requires a 'callback_url' option
- The identity provider did not return an ID token; ensure the
- OIDC provider requires an 'issuer' option
- OIDC provider requires a 'client_id' option
- OIDC provider requires a 'callback_url' option
AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27).
Data as JSON: /api/errors/3738366d6d9fa73a.
Report an issue: GitHub.