3737import { isSuperTokensEnabled } from '../authMode.js' ;
3838import { buildProviders , resolvePublicOrigin } from './providers.js' ;
3939import { buildSignInUpOverride } from './mapping.js' ;
40+ import { probeAuthedEndpoint , isRefused } from './coreProbe.js' ;
4041
4142// SuperTokens' own default API base path. It is also why the runbook widens
4243// the GitHub OAuth registration to /auth: SuperTokens serves its callbacks at
@@ -135,26 +136,25 @@ export function isLoopback(uri) {
135136/**
136137 * Confirms the core actually refuses unauthenticated callers.
137138 *
138- * Probes an endpoint that requires an API key when one is configured. A 401
139- * means the core is closed and all is well; a 200 means it answered a caller
140- * holding no key at all, which is the state where anyone who can reach it can
141- * mint a session for any user id .
139+ * Throws ONLY on a confirmed-open core - an endpoint we know exists answering
140+ * an unkeyed request with 200. Every other outcome warns and lets the boot
141+ * proceed, because this guard sits on the startup path and a wrong answer here
142+ * does not print a scary line, it stops the container .
142143 *
143- * `/hello` is deliberately NOT used - it answers unauthenticated by design as
144- * a health check, so probing it would prove nothing.
144+ * That distinction was missing in v1.8.2: this probed a single hardcoded path
145+ * that core 12 does not implement, and treated the resulting 404 as proof the
146+ * core was open - so setting AUTH_MODE=dual against a perfectly well-secured
147+ * core would have refused to start, blaming the operator for a URL this code
148+ * got wrong. The probe now lives in ./coreProbe.js and is shared with the
149+ * preflight, so the two cannot drift again.
145150 */
146151export async function assertCoreRejectsAnonymous ( { connectionURI, hasKey, fetchImpl = fetch } ) {
147- const url = `${ connectionURI . replace ( / \/ $ / , '' ) } /recipe/users/count` ;
148- let response ;
152+ let probe ;
149153 try {
150- response = await fetchImpl ( url , {
151- method : 'GET' ,
152- headers : { 'api-version' : '3.0' } ,
153- signal : AbortSignal . timeout ( 5000 ) ,
154- } ) ;
154+ probe = await probeAuthedEndpoint ( { connectionURI, fetchImpl } ) ;
155155 } catch ( e ) {
156156 // Unreachable, DNS failure, timeout. Cannot establish anything; the core
157- // may simply still be starting. Warn rather than refuse - see the caller .
157+ // may simply still be starting. Warn rather than refuse.
158158 console . warn (
159159 `[auth] could not verify that the SuperTokens core at ${ connectionURI } requires `
160160 + `authentication (${ e . message } ). If it is running without API_KEYS, anyone who can `
@@ -163,16 +163,28 @@ export async function assertCoreRejectsAnonymous({ connectionURI, hasKey, fetchI
163163 return 'unverified' ;
164164 }
165165
166- if ( response . status === 401 ) return 'closed' ;
166+ if ( isRefused ( probe . status ) ) return 'closed' ;
167167
168- throw new Error (
169- `The SuperTokens core at ${ connectionURI } answered an unauthenticated request with `
170- + `HTTP ${ response . status } , which means it is running without API_KEYS. Anyone who can `
171- + 'reach it can mint a session for any user id, including every value in SUPER_ADMIN_IDS, '
172- + 'without any request reaching RackStack. Set API_KEYS on the core to the same value as '
173- + `SUPERTOKENS_API_KEY here${ hasKey ? '' : ' (which is also unset)' } , and do not publish `
174- + 'its port.' ,
168+ if ( probe . status === 200 ) {
169+ throw new Error (
170+ `The SuperTokens core at ${ connectionURI } answered an unauthenticated request to `
171+ + `${ probe . path } with HTTP 200, which means it is running without API_KEYS. Anyone who `
172+ + 'can reach it can mint a session for any user id, including every value in '
173+ + 'SUPER_ADMIN_IDS, without any request reaching RackStack. Set API_KEYS on the core to '
174+ + `the same value as SUPERTOKENS_API_KEY here${ hasKey ? '' : ' (which is also unset)' } , `
175+ + 'and do not publish its port.' ,
176+ ) ;
177+ }
178+
179+ // A 404 from every candidate, or any other unexpected status, says our URL
180+ // is wrong for this core version - not that the core is open. Refusing to
181+ // boot on that would be punishing the operator for our own mistake.
182+ console . warn (
183+ `[auth] could not verify that the SuperTokens core at ${ connectionURI } requires `
184+ + `authentication (${ probe . status === null ? 'no known endpoint answered' : `unexpected HTTP ${ probe . status } ` } ). `
185+ + 'This is NOT evidence that it is open, but do check it by hand.' ,
175186 ) ;
187+ return 'unverified' ;
176188}
177189
178190/**
0 commit comments