You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node-oidc-provider如何实现客户端列表数据库存储

node-oidc-provider 数据库存储客户端列表实现方案

node-oidc-provider 本身不绑定客户端存储介质,通过内置的Adapter适配器模式支持自定义存储,不需要修改框架源码,按以下步骤实现即可。

1. 编写Client对应的数据库适配器

框架所有模型(包括Client、AccessToken、AuthorizationCode等)的读写都会走你传入的适配器,你不需要给所有模型实现数据库逻辑,单独处理Client模型即可,其他模型默认走内存存储。
适配器必须实现find、upsert、destroy三个核心方法,其余方法对Client模型无实际作用,空实现即可。
参考实现:

// 引入你自己业务层的客户端数据库操作类
const ClientDao = require('./your-project/dao/client');

class DbAdapter {
  constructor(modelName) {
    this.modelName = modelName;
  }

  // 根据client_id查询客户端配置
  async find(clientId) {
    if (this.modelName !== 'Client') return undefined;
    const dbClient = await ClientDao.findOne({ client_id: clientId });
    if (!dbClient) return undefined;
    // 注意返回的字段必须和oidc-provider要求的客户端配置字段完全匹配
    return {
      client_id: dbClient.client_id,
      client_secret: dbClient.client_secret,
      redirect_uris: dbClient.redirect_uris,
      grant_types: dbClient.grant_types,
      response_types: dbClient.response_types,
      token_endpoint_auth_method: dbClient.token_endpoint_auth_method,
      // 其余业务需要的客户端配置字段,和官方客户端配置项一一对应即可
    };
  }

  // 新增/更新客户端
  async upsert(clientId, clientConfig, expiresIn) {
    if (this.modelName !== 'Client') return;
    // 客户端一般为永久有效,expiresIn字段可忽略
    await ClientDao.updateOne(
      { client_id: clientId },
      { $set: clientConfig },
      { upsert: true }
    );
  }

  // 删除客户端
  async destroy(clientId) {
    if (this.modelName !== 'Client') return;
    await ClientDao.deleteOne({ client_id: clientId });
  }

  // 以下方法为其他令牌类模型使用,Client模型不需要实现,空函数即可
  async findByUserCode() {}
  async consume() {}
}

2. 注入适配器到Provider实例

初始化oidc-provider的时候,将编写好的适配器传入配置项即可:

const { Provider } = require('oidc-provider');
const oidcProvider = new Provider('https://your-oidc-domain.com', {
  // 其余你的业务配置:features、ttl、claims、cookies等
  adapter: DbAdapter,
  // 如果你有少量固定不变的客户端,也可以同时配置静态clients,静态配置优先级高于适配器查询结果
  // clients: []
});

常见踩坑说明

  • 不要直接返回数据库中驼峰命名的字段,必须转换为框架要求的下划线命名格式(比如redirectUris要转为redirect_uris),否则会直接抛客户端无效错误,且不会提示具体错误字段,排查成本很高
  • 动态新增、修改、删除客户端不需要重启服务,框架每次校验客户端时都会实时调用find方法查询,配置变更即时生效
  • 流量较高的场景可以在find方法中加一层短TTL缓存(比如30s~1min),减少数据库查询压力,客户端配置变更时主动清除对应缓存即可
  • 如果后续需要把令牌、会话等数据也存数据库,直接在适配器里扩展对应modelName的处理逻辑即可,不需要修改框架其他配置

内容的提问来源于stack exchange,提问作者manhh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 02:45:49