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

如何用oauth2-server实现用户名密码认证并返回Authorization Code

解决OAuth2授权码模式返回授权码的问题

核心问题定位

你当前是用**密码模式(Password Grant)**直接调用/token端点,所以返回的是直接的access_token。而授权码模式(Authorization Code Grant)是分两步执行的流程:必须先通过/authorize端点完成用户认证并获取授权码,再用授权码去/token端点换取令牌。

调整步骤

1. 确保OAuth2服务配置开启授权码模式

初始化oauth2-server时,要明确启用authorization_code授权类型,同时实现对应的数据存储逻辑(客户端、授权码、令牌的存储/验证):

const oauth = new OAuth2Server({
  model: {
    // 验证客户端合法性
    getClient: async (clientId, clientSecret) => { /* 实现客户端查询逻辑 */ },
    // 存储生成的授权码
    saveAuthorizationCode: async (code, client, user) => { /* 保存授权码到数据库 */ },
    // 查询授权码信息
    getAuthorizationCode: async (authorizationCode) => { /* 从数据库获取授权码 */ },
    // 吊销已使用的授权码
    revokeAuthorizationCode: async (code) => { /* 标记授权码为已使用 */ },
    // 生成access_token
    generateAccessToken: async (client, user, scope) => { /* 生成并返回令牌 */ },
    // 其他必要的model方法(如getUser等)
  },
  authorizationCodeLifetime: 300, // 授权码有效期,默认5分钟
});

2. 实现/authorize端点处理授权请求

授权码模式的第一步是引导用户访问/authorize端点,需携带以下参数:

  • response_type=code:明确要求返回授权码
  • client_id:客户端ID
  • redirect_uri:授权成功后的跳转地址(需与客户端配置一致)
  • scope(可选):请求的权限范围

示例Express路由:

app.get('/authorize', async (req, res) => {
  const request = new Request(req);
  const response = new Response(res);

  try {
    // 验证客户端和请求参数合法性
    const authInfo = await oauth.authorize(request, response);
    // 渲染登录页面,让用户输入用户名密码
    res.render('login', { 
      client: authInfo.client, 
      redirectUri: authInfo.redirectUri,
      state: req.query.state
    });
  } catch (err) {
    res.status(err.code || 500).json(err);
  }
});

3. 实现用户认证并生成授权码

用户提交用户名密码后,在登录验证端点完成身份校验,通过后触发授权码生成逻辑:

app.post('/login', async (req, res) => {
  const { username, password, client_id, redirect_uri, state } = req.body;
  
  // 自定义用户验证逻辑(比如从数据库查询用户)
  const user = await checkUserCredentials(username, password);
  if (!user) {
    return res.status(401).send('用户名或密码错误');
  }

  // 构造授权请求,指定返回授权码
  const request = new Request({
    query: {
      response_type: 'code',
      client_id,
      redirect_uri,
      state
    },
    user,
  });
  const response = new Response(res);

  try {
    const result = await oauth.authorize(request, response);
    // 跳转到redirect_uri,携带授权码和state参数
    res.redirect(`${redirect_uri}?code=${result.authorizationCode.code}&state=${state || ''}`);
  } catch (err) {
    res.status(err.code || 500).json(err);
  }
});

4. 用授权码调用/token端点换取令牌

客户端拿到授权码后,再调用/token端点,此时参数需改为:

  • grant_type=authorization_code
  • code:获取到的授权码
  • client_id/client_secret:客户端凭证
  • redirect_uri:与授权请求时一致

示例/token端点实现:

app.post('/token', async (req, res) => {
  const request = new Request(req);
  const response = new Response(res);

  try {
    const token = await oauth.token(request, response);
    res.json(token);
  } catch (err) {
    res.status(err.code || 500).json(err);
  }
});

关键注意点

  • 授权码模式必须经过用户交互(输入用户名密码),无法直接通过/token一步获取授权码
  • 客户端配置的redirect_uri必须与请求时的参数完全一致,否则会触发验证失败
  • 授权码是一次性凭证,使用后会被吊销,不能重复使用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 16:37:48