mongodb/node-mongodb-native · error · MongoMissingCredentialsError
User provided OIDC callbacks must return a valid object…
Error message
User provided OIDC callbacks must return a valid object with an accessToken.
What it means
Thrown by the OIDC callback workflow when a user-supplied OIDC_CALLBACK or OIDC_HUMAN_CALLBACK returns a result that fails validation. The callback must return an object with a string accessToken; isCallbackResultInvalid() rejects results that are non-objects, lack accessToken, have a non-string accessToken, or carry disallowed properties. The cache is not populated on this failure.
Solutions
- Ensure the callback returns { accessToken: string, expiresInSeconds?: number, refreshToken?: string } with accessToken a non-empty string.
- Map your provider's fields: { accessToken: json.access_token, expiresInSeconds: Number(json.expires_in) }.
- Make sure the function actually returns (uses 'return' on the arrow/body) and that async fetch/parse errors are handled.
Example fix
// before
const cb = async () => {
const r = await fetch(url);
return (await r.json()); // returns { access_token, expires_in }
};
// after
const cb = async () => {
const r = await fetch(url);
const j = await r.json();
return { accessToken: j.access_token, expiresInSeconds: Number(j.expires_in) };
}; Defensive patterns
Strategy: type-guard
Validate before calling
function isOidcResponse(v) {
return !!v && typeof v === 'object'
&& typeof v.accessToken === 'string'
&& (v.expiresInSeconds == null || typeof v.expiresInSeconds === 'number')
&& (v.refreshToken == null || typeof v.refreshToken === 'string');
}
function safeCallback(inner) {
return async (params) => {
const r = await inner(params);
if (!isOidcResponse(r)) throw new TypeError('OIDC callback returned invalid shape');
return r;
};
} Type guard
function isOidcResponse(v): v is { accessToken: string; expiresInSeconds?: number; refreshToken?: string } {
return !!v && typeof v === 'object'
&& typeof v.accessToken === 'string'
&& (v.expiresInSeconds == null || typeof v.expiresInSeconds === 'number')
&& (v.refreshToken == null || typeof v.refreshToken === 'string');
} Prevention
- Map provider fields to { accessToken, expiresInSeconds, refreshToken } explicitly.
- Wrap the callback with the type guard above so errors surface with your own message.
- Ensure the async callback actually returns (use 'return').
When it happens
Trigger: The OIDC callback resolves with undefined, null, a string, or an object missing accessToken (or with accessToken not a string, or unexpected properties). Fires at callback_workflow.ts:147 inside executeAndValidateCallback().
Common situations: Callback returns the raw fetch Response or the parsed token JSON keyed differently (e.g. access_token instead of accessToken). Forgetting to return from an arrow function. Returning expiresInSeconds as a non-number. Including extra fields beyond accessToken/expiresInSeconds/refreshToken.
Related errors
- Auth mechanism property ALLOWED_HOSTS must be an array of…
- Currently only a ENVIRONMENT in
- OIDC callback timed out after
- OIDC callback timed out after
- Auth mechanism property ALLOWED_HOSTS is not allowed in the…
AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11).
Data as JSON: /api/errors/c0bfef8a63962e41.
Report an issue: GitHub.
Appendix: source
Thrown at src/cmap/auth/mongodb_oidc/callback_workflow.ts:147
token: string,
conversationId?: number
): Promise<void> {
await connection.command(
ns(credentials.source),
finishCommandDocument(token, conversationId),
undefined
);
}
/**
* Executes the callback and validates the output.
*/
protected async executeAndValidateCallback(params: OIDCCallbackParams): Promise<OIDCResponse> {
const result = await this.callback(params);
// Validate that the result returned by the callback is acceptable. If it is not
// we must clear the token result from the cache.
if (isCallbackResultInvalid(result)) {
throw new MongoMissingCredentialsError(CALLBACK_RESULT_ERROR);
}
return result;
}
/**
* Ensure the callback is only executed one at a time and throttles the calls
* to every 100ms.
*/
protected withLock(callback: OIDCCallbackFunction): OIDCCallbackFunction {
let lock: Promise<any> = Promise.resolve();
return async (params: OIDCCallbackParams): Promise<OIDCResponse> => {
// We do this to ensure that we would never return the result of the
// previous lock, only the current callback's value would get returned.
await lock;
lock = lock
.catch(() => null)
View on GitHub (pinned to dce7939f86)