Azure DevOps Oauth身份验证:无法获取访问令牌(BadRequest无法反序列化JsonWebToken对象)

11 浏览
0 Comments

Azure DevOps Oauth身份验证:无法获取访问令牌(BadRequest无法反序列化JsonWebToken对象)

我正在尝试为Azure DevOps的自定义Web应用程序实现OAuth 2.0流程。我正在遵循此文档(https://learn.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/oauth?view=azure-devops)以及此OauthWebSample(https://github.com/microsoft/azure-devops-auth-samples/tree/master/OAuthWebSample),但是使用ASP.NET Core(我也阅读了一个类似的SO问题,但并不相同:https://stackoverflow.com/questions/56527208/access-azure-devops-rest-api-with-oauth)。

复现

我已在https://app.vsaex.visualstudio.com/app/register注册了一个azdo应用程序,并且授权步骤似乎正常工作,即用户可以授权应用程序,并且重定向到我的应用程序返回的内容看起来像是有效的JWT令牌:

header: {

"typ": "JWT",

"alg": "RS256",

"x5t": "oOvcz5M_7p-HjIKlFXz93u_V0Zo"

}

payload: {

"aui": "b3426a71-1c05-497c-ab76-259161dbcb9e",

"nameid": "7e8ce1ba-1e70-4c21-9b51-35f91deb6d14",

"scp": "vso.identity vso.work_write vso.authorization_grant",

"iss": "app.vstoken.visualstudio.com",

"aud": "app.vstoken.visualstudio.com",

"nbf": 1587294992,

"exp": 1587295892

}

下一步是获取一个访问令牌,但是出现了BadReqest: invalid_client, Failed to deserialize the JsonWebToken object错误。以下是完整的示例:

public class Config
{
    public string ClientId { get; set; } = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
    public string Secret { get; set; } = "....";
    public string Scope { get; set; } = "vso.identity vso.work_write";
    public string RedirectUri { get; set; } = "https://....ngrok.io/azdoaccount/callback";
}
[Route("[controller]/[action]")]
public class AzdoAccountController : Controller
{
    private readonly Config config = new Config();
    [HttpGet]
    public ActionResult SignIn()
    {
        Guid state = Guid.NewGuid();
        UriBuilder uriBuilder = new UriBuilder("https://app.vssps.visualstudio.com/oauth2/authorize");
        NameValueCollection queryParams = HttpUtility.ParseQueryString(uriBuilder.Query ?? string.Empty);
        queryParams["client_id"] = config.ClientId;
        queryParams["response_type"] = "Assertion";
        queryParams["state"] = state.ToString();
        queryParams["scope"] = config.Scope;
        queryParams["redirect_uri"] = config.RedirectUri;
        uriBuilder.Query = queryParams.ToString();
        return Redirect(uriBuilder.ToString());
    }
    [HttpGet]
    public async Task Callback(string code, Guid state)
    {
        string token = await GetAccessToken(code, state);
        return Ok();
    }
    public async Task GetAccessToken(string code, Guid state)
    {
        Dictionary form = new Dictionary()
        {
            { "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" },
            { "client_assertion", config.Secret },
            { "grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer" },
            { "assertion", code },
            { "redirect_uri", config.RedirectUri }
        };
        HttpClient httpClient = new HttpClient();
        HttpResponseMessage responseMessage = await httpClient.PostAsync(
            "https://app.vssps.visualstudio.com/oauth2/token",
            new FormUrlEncodedContent(form)
        );
        if (responseMessage.IsSuccessStatusCode)
        {
            string body = await responseMessage.Content.ReadAsStringAsync();
            // TODO 解析body并返回访问令牌
            return "";
        }
        else
        {
            string content = await responseMessage.Content.ReadAsStringAsync();
            throw new Exception($"{responseMessage.ReasonPhrase} {(string.IsNullOrEmpty(content) ? "" : $"({content})")}");
        }
    }
}

0
0 Comments

问题原因:在请求访问令牌时,应该提供Client Secret而不是App Secret作为client_assertion参数。

解决方法:使用Client Secret替代App Secret作为client_assertion参数。

0