使用SpotifyAPI-NET授权时返回'invalid_grant'错误求助
SpotifyAPI-NET SDK 抛出
invalid_grant 异常,但手动请求正常的解决方法 我在AWS Lambda上用C#搭建后端,前端是本地React服务器,通过OAuth流程获取Spotify授权码后,调用后端接口server.com/api/authenticate?code=SPOTIFYCODE换取访问令牌。手动构造请求能成功获取令牌,但使用SpotifyAPI-NET SDK时,一直抛出带有invalid_grant的SpotifyAPI.Web.APIException异常,排查下来可以从以下几个方向解决:
可能的原因及修复步骤
1. RedirectUri 格式不一致
手动请求直接使用字符串RedirectUri,而SDK中通过new Uri(RedirectUri)创建Uri对象,可能存在格式差异(比如末尾是否带斜杠、大小写)。Spotify对redirect_uri的校验严格,必须和OAuth授权发起时的地址完全一致。
- 核对前端发起授权时的
redirect_uri,与后端手动请求、SDK中使用的地址完全匹配(包括斜杠、大小写) - 构造Uri时强制指定绝对路径,避免格式自动修正:
new Uri(RedirectUri, UriKind.Absolute)
2. SDK 默认使用 Basic 认证而非表单传参
Spotify支持两种凭证传递方式:表单中携带client_id和client_secret,或者使用Basic Auth请求头。手动请求是把凭证放在表单里,但SpotifyAPI-NET的OAuthClient默认会用Basic Auth方式传递凭证,如果凭证编码有问题或应用配置限制,就会导致校验失败。
- 强制SDK使用表单传参方式,构造
OAuthClient时指定认证类型:var httpClient = new HttpClient(); var oAuthClient = new OAuthClient(httpClient) { TokenCredentialType = OAuthClient.CredentialType.RequestBody }; var response = await oAuthClient.RequestToken( new AuthorizationCodeTokenRequest( ClientId, ClientSecret, code, new Uri(RedirectUri)) );
3. 授权码的URL编码问题
前端传递的授权码可能存在URL编码,手动请求时直接使用参数值没问题,但SDK可能重复编码或处理不当,导致传递给Spotify的授权码无效。
- 先对传入的
code做URL解码:string decodedCode = HttpUtility.UrlDecode(code); var response = await new OAuthClient().RequestToken( new AuthorizationCodeTokenRequest( ClientId, ClientSecret, decodedCode, new Uri(RedirectUri)) );
4. 升级SDK版本
旧版本的SpotifyAPI-NET可能存在请求构造的bug,建议升级到最新稳定版,避免已知的兼容性问题。
代码对比
手动请求成功代码
// GET api/authenticate [HttpGet] public async Task<ActionResult> Authenticate(string code) { // Exchange the authorization code for an access token var parameters = new Dictionary<string, string> { { "grant_type", "authorization_code" }, { "code", code }, { "redirect_uri", RedirectUri }, { "client_id", ClientId }, { "client_secret", ClientSecret } }; var content = new FormUrlEncodedContent(parameters); var response = await _httpClient.PostAsync(TokenEndpoint, content); if (!response.IsSuccessStatusCode) return BadRequest("Failed to get access token."); var responseContent = await response.Content.ReadAsStringAsync(); var tokenResponse = JsonSerializer.Deserialize<TokenResponse>(responseContent); var accessToken = tokenResponse?.AccessToken; // Create a new cookie with the access token if (string.IsNullOrEmpty(accessToken)) return BadRequest("Failed to get access token. Access token is null or empty."); var cookieOptions = new CookieOptions { HttpOnly = true, Secure = true, Expires = DateTimeOffset.Now.AddDays(1), SameSite = SameSiteMode.None, Domain = Request.Host.Value, Path = "/api", }; Response.Cookies.Append("access_token", accessToken, cookieOptions); return Ok(); }
SDK失败原代码
// GET api/authenticate [HttpGet] public async Task<ActionResult> Authenticate(string code) { var response = await new OAuthClient().RequestToken( new AuthorizationCodeTokenRequest( ClientId, ClientSecret, code, new Uri(RedirectUri)) ); var accessToken = response.AccessToken; // Create a new cookie with the access token if (string.IsNullOrEmpty(accessToken)) return BadRequest("Failed to get access token. Access token is null or empty."); var cookieOptions = new CookieOptions { HttpOnly = true, Secure = true, Expires = DateTimeOffset.Now.AddDays(1), SameSite = SameSiteMode.None, Domain = Request.Host.Value, Path = "/api", }; Response.Cookies.Append("access_token", accessToken, cookieOptions); return Ok(); }
内容的提问来源于stack exchange,提问作者Scott
相关产品推荐
相关产品推荐

