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

.NET C#中Spotify带Scope的授权令牌无法正常工作

问题根源与解决方案

你现在的代码犯了两个致命错误,直接导致了401错误和接口调用失败:


1. 用错了授权流程

你要实现的是授权码流程(Authorization Code Flow),但代码里写的是客户端凭证流程(Client Credentials Flow)。这俩完全不是一回事:

  • 客户端凭证流程拿的是「应用级令牌」,只能访问不需要用户授权的公共数据,根本没有user-read-private、user-read-email这类用户权限
  • 授权码流程拿的是「用户级令牌」,代表用户操作,才能访问/me这类需要用户权限的接口

2. 兑换授权码的参数完全错误

授权码流程兑换令牌时,必须满足以下要求:

  • grant_type必须设为authorization_code,不是client_credentials
  • 必须传入code参数(你之前拿到的授权码)
  • 必须传入redirect_uri参数(和你发起授权请求时用的回调地址完全一致,哪怕是本地测试地址也不能错)

修正后的代码实现

第一步:修正令牌兑换方法

public async Task<SpotifyTokenResponseEntity> ExchangeAuthorizationCodeForToken(
    SpotifyAuthRequestEntity authRequest, 
    string authorizationCode, 
    string redirectUri,
    string[] scopes)
{
    var scopeString = string.Join(" ", scopes);
    
    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("grant_type", "authorization_code"), 
        new KeyValuePair<string, string>("code", authorizationCode),
        new KeyValuePair<string, string>("redirect_uri", redirectUri),
        new KeyValuePair<string, string>("scope", scopeString)
    });

    var authString = $"{authRequest.ClientId}:{authRequest.ClientSecret}";
    var base64Auth = Convert.ToBase64String(Encoding.ASCII.GetBytes(authString));

    var request = new HttpRequestMessage(HttpMethod.Post, _tokenUrl)
    {
        Headers = { Authorization = new AuthenticationHeaderValue("Basic", base64Auth) },
        Content = content
    };

    var response = await _httpClient.SendAsync(request);

    if (!response.IsSuccessStatusCode)
    {
        var errorDetails = await response.Content.ReadAsStringAsync();
        throw new Exception($"兑换令牌失败: {response.StatusCode} - {errorDetails}");
    }

    var json = await response.Content.ReadAsStringAsync();
    return JsonSerializer.Deserialize<SpotifyTokenResponseEntity>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
}

第二步:修正命令处理器

你需要把拿到的授权码和回调地址传入处理器,而不是只传ClientId和ClientSecret:

public class GetSpotifyUserToken : IRequest<ResponseObjectJsonDto>
{
    public string ClientId { get; set; }
    public string ClientSecret { get; set; }
    public string AuthorizationCode { get; set; } // 新增:授权码
    public string RedirectUri { get; set; } // 新增:回调地址
}

public class GetSpotifyUserTokenHandler : IRequestHandler<GetSpotifyUserToken, ResponseObjectJsonDto>
{
    private readonly ISpotifyAuthService _spotifyAuthService;

    public GetSpotifyUserTokenHandler(ISpotifyAuthService spotifyAuthService)
    {
        _spotifyAuthService = spotifyAuthService;
    }

    public async Task<ResponseObjectJsonDto> Handle(GetSpotifyUserToken request, CancellationToken cancellationToken)
    {
        try
        {
            var authRequest = new SpotifyAuthRequestEntity
            {
                ClientId = request.ClientId,
                ClientSecret = request.ClientSecret
            };

            var scopes = new[]
            {
                "user-read-private",
                "user-read-email"
            };
            
            Console.WriteLine($"请求带权限的用户令牌: {string.Join(", ", scopes)}");
            
            // 调用修正后的兑换方法
            var token = await _spotifyAuthService.ExchangeAuthorizationCodeForToken(
                authRequest, 
                request.AuthorizationCode, 
                request.RedirectUri,
                scopes);

            return new ResponseObjectJsonDto()
            {
                Code = (int)CodeHttp.OK,
                Message = "用户令牌生成成功",
                Response = token
            };
        }
        catch (Exception ex)
        {
            return new ResponseObjectJsonDto()
            {
                Code = (int)CodeHttp.INTERNALSERVER,
                Message = ex.Message,
                Response = null
            };
        }
    }
}

额外注意事项

  1. 授权请求时的scope必须和兑换令牌时的scope一致,或者是其子集
  2. 回调地址redirect_uri必须和你在Spotify开发者后台配置的地址完全匹配(包括http/https、端口、路径)
  3. 授权码只能用一次,用过就失效,需要重新发起授权请求获取新的授权码
  4. 调用/me接口时,必须用兑换得到的access_token,格式是Authorization: Bearer {access_token}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 00:13:14