如何用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:客户端IDredirect_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_codecode:获取到的授权码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
相关产品推荐
相关产品推荐

