Azure Проверка подлинности Devops Oauth: невозможно получить токен доступа (BadRequest Не удалось десериализовать объект JsonWebToken) - PullRequest
0 голосов
/ 19 апреля 2020

Я пытаюсь реализовать поток OAUth 2.0 для пользовательского веб-приложения для Azure Devops. Я следую за этой https://docs.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/oauth?view=azure-devops документацией, а также за https://github.com/microsoft/azure-devops-auth-samples/tree/master/OAuthWebSample OauthWebSample, но использую ASP. NET Core (я также читал одну проблему на SO, которая выглядела аналогично но это не так: Доступ Azure DevOps REST API с oAuth )

Воспроизведение

Я зарегистрировал приложение azdo на https://app.vsaex.visualstudio.com/app/register и этап авторизации , кажется, работает нормально, то есть пользователь может авторизовать приложение, а перенаправление на мое приложение возвращает что-то, похожее на действительный токен 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, Не удалось десериализовать объект JsonWebToken .

Вот полный пример:

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";
}

/// <summary>
/// Create azdo application at https://app.vsaex.visualstudio.com/
/// Use configured values in above 'Config' (using ngrok to have a public url that proxies to localhost)
/// navigating to localhost:5001/azdoaccount/signin 
/// => redirect to https://app.vssps.visualstudio.com/oauth2/authorize and let user authorize (seems to work)
/// => redirect back to localhost:5001/azdoaccount/callback with auth code
/// => post to https://app.vssps.visualstudio.com/oauth2/token => BadReqest: invalid_client, Failed to deserialize the JsonWebToken object
/// </summary>
[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<ActionResult> Callback(string code, Guid state)
    {
        string token = await GetAccessToken(code, state);
        return Ok();
    }

    public async Task<string> GetAccessToken(string code, Guid state)
    {
        Dictionary<string, string> form = new Dictionary<string, string>()
                {
                    { "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) // is always false for me
        {
            string body = await responseMessage.Content.ReadAsStringAsync();
            // TODO parse body and return access token
            return "";
        }
        else
        {
            // Bad Request ({"Error":"invalid_client","ErrorDescription":"Failed to deserialize the JsonWebToken object."})
            string content = await responseMessage.Content.ReadAsStringAsync();
            throw new Exception($"{responseMessage.ReasonPhrase} {(string.IsNullOrEmpty(content) ? "" : $"({content})")}");
        }
    }
}

1 Ответ

0 голосов
/ 19 апреля 2020

При запросе токенов доступа необходимо указать Client Secret , а не App Secret для параметра client_assertion :

enter image description here

...