使用OpenIdDict认证应用时无法获取授权码的问题
问题分析与解决
你的代码核心问题是完全搞错了授权码流程的调用逻辑,授权码流程不是直接POST授权端点就能拿到授权码的,它需要用户参与登录和授权,具体问题点和修正方案如下:
核心错误点
- 请求方式错误:
/connect/authorize授权端点需要用GET请求重定向用户,而不是直接POST。因为这个端点的作用是引导用户到授权服务器登录、确认授权,完成后服务器会把授权码通过你指定的redirect_uri回调回来。 - 参数冗余错误:
grant_type=code是令牌端点(/connect/token)的参数,授权端点不需要这个参数,你只需要传response_type=code即可。 - 客户端认证方式冲突:同时用了Basic认证和参数里传
client_secret,这完全没必要,而且授权端点通常不需要提前做客户端认证(机密客户端的话,在令牌交换阶段再做认证即可)。
正确的授权码流程实现步骤
步骤1:重定向用户到授权端点
在你的自研应用中,需要先把用户重定向到授权服务器的/connect/authorize端点,让用户完成登录和授权:
public IActionResult RedirectToAuthorization() { var redirectUri = "https://localhost:7002/callback"; // 改成你自己的回调地址,不能用Swagger的跳转地址 var state = Guid.NewGuid().ToString(); // 生成随机state,用于防CSRF // 构造授权端点的URL var authorizeUrl = $"https://localhost:7000/connect/authorize?client_id=web-client&response_type=code&redirect_uri={Uri.EscapeDataString(redirectUri)}&state={Uri.EscapeDataString(state)}"; // 重定向用户到授权服务器 return Redirect(authorizeUrl); }
步骤2:处理授权服务器的回调,提取授权码
当用户完成登录授权后,授权服务器会把请求重定向到你指定的redirect_uri,并在URL参数里带上code(授权码)和state,你需要在回调接口里提取这个授权码:
public async Task<IActionResult> Callback(string code, string state) { // 验证state(和之前生成的state对比,防止CSRF攻击) // 这里省略state验证逻辑,实际项目必须加 // 用授权码交换令牌 using var client = new HttpClient(); var authorizationToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"web-client:901564A5-E7FE-42CB-B10D-61EF6A8F3654")); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authorizationToken); var tokenParams = new Dictionary<string, string> { {"grant_type", "authorization_code"}, {"code", code}, // 这里传入刚才拿到的授权码 {"redirect_uri", "https://localhost:7002/callback"} // 必须和授权时的redirect_uri完全一致 }; var response = await client.PostAsync("https://localhost:7000/connect/token", new FormUrlEncodedContent(tokenParams)); response.EnsureSuccessStatusCode(); var tokenResponse = await response.Content.ReadFromJsonAsync<Dictionary<string, string>>(); var accessToken = tokenResponse["access_token"]; var refreshToken = tokenResponse["refresh_token"]; // 这里可以把令牌保存起来,返回给前端或者自己使用 return Ok(new { AccessToken = accessToken, RefreshToken = refreshToken }); }
关键说明
- 你之前用Postman、Swagger能成功,是因为这些工具自动处理了重定向到授权服务器、引导用户登录、接收回调提取授权码的整个流程,而你的自研代码跳过了用户交互的环节,直接POST授权端点,自然拿不到授权码。
redirect_uri必须和你在OpenIdDict客户端配置里注册的地址完全一致,否则授权服务器会拒绝请求。
内容的提问来源于stack exchange,提问作者Linney E Dew II
相关产品推荐
相关产品推荐

