{"record":{"id":"c0bfef8a63962e41","repo":"mongodb/node-mongodb-native","slug":"user-provided-oidc-callbacks-must-return-a-valid-o","errorCode":null,"errorMessage":"User provided OIDC callbacks must return a valid object with an accessToken.","messagePattern":"User provided OIDC callbacks must return a valid object with an accessToken\\.","errorType":"exception","errorClass":"MongoMissingCredentialsError","httpStatus":null,"severity":"error","filePath":"src/cmap/auth/mongodb_oidc/callback_workflow.ts","lineNumber":147,"sourceCode":"    token: string,\n    conversationId?: number\n  ): Promise<void> {\n    await connection.command(\n      ns(credentials.source),\n      finishCommandDocument(token, conversationId),\n      undefined\n    );\n  }\n\n  /**\n   * Executes the callback and validates the output.\n   */\n  protected async executeAndValidateCallback(params: OIDCCallbackParams): Promise<OIDCResponse> {\n    const result = await this.callback(params);\n    // Validate that the result returned by the callback is acceptable. If it is not\n    // we must clear the token result from the cache.\n    if (isCallbackResultInvalid(result)) {\n      throw new MongoMissingCredentialsError(CALLBACK_RESULT_ERROR);\n    }\n    return result;\n  }\n\n  /**\n   * Ensure the callback is only executed one at a time and throttles the calls\n   * to every 100ms.\n   */\n  protected withLock(callback: OIDCCallbackFunction): OIDCCallbackFunction {\n    let lock: Promise<any> = Promise.resolve();\n    return async (params: OIDCCallbackParams): Promise<OIDCResponse> => {\n      // We do this to ensure that we would never return the result of the\n      // previous lock, only the current callback's value would get returned.\n      await lock;\n      lock = lock\n\n        .catch(() => null)\n","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/dce7939f86fb283e167ad709955abedb7bf23124/src/cmap/auth/mongodb_oidc/callback_workflow.ts#L129-L165","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","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."],"exampleFix":"// before\nconst cb = async () => {\n  const r = await fetch(url);\n  return (await r.json()); // returns { access_token, expires_in }\n};\n// after\nconst cb = async () => {\n  const r = await fetch(url);\n  const j = await r.json();\n  return { accessToken: j.access_token, expiresInSeconds: Number(j.expires_in) };\n};","handlingStrategy":"type-guard","validationCode":"function isOidcResponse(v) {\n  return !!v && typeof v === 'object'\n    && typeof v.accessToken === 'string'\n    && (v.expiresInSeconds == null || typeof v.expiresInSeconds === 'number')\n    && (v.refreshToken == null || typeof v.refreshToken === 'string');\n}\nfunction safeCallback(inner) {\n  return async (params) => {\n    const r = await inner(params);\n    if (!isOidcResponse(r)) throw new TypeError('OIDC callback returned invalid shape');\n    return r;\n  };\n}","typeGuard":"function isOidcResponse(v): v is { accessToken: string; expiresInSeconds?: number; refreshToken?: string } {\n  return !!v && typeof v === 'object'\n    && typeof v.accessToken === 'string'\n    && (v.expiresInSeconds == null || typeof v.expiresInSeconds === 'number')\n    && (v.refreshToken == null || typeof v.refreshToken === 'string');\n}","tryCatchPattern":null,"preventionTips":["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')."],"tags":["authentication","oidc","callback","validation"],"backgroundTag":null,"analyzedSha":"dce7939f86fb283e167ad709955abedb7bf23124","analyzedAt":"2026-08-11T04:54:53.215Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}