public string CreateAuthorizationUrl()
        {
            var state = Guid.NewGuid();

            authorizationRequests[state] = new AzdoToken()
            {
                IsPending = true
            };

            var uriBuilder = new UriBuilder(config.AuthUrl);
            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(uriBuilder.ToString());
        }
        public async Task <AzdoToken> GetAccessToken(string code, Guid state)
        {
            if (ValidateCallbackValues(code, state.ToString(), out var error))
            {
                var form = new Dictionary <string, string>()
                {
                    { "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" },
                    { "client_assertion", config.ClientSecret },
                    { "grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer" },
                    { "assertion", code },
                    { "redirect_uri", config.RedirectUri }
                };

                HttpClient httpClient = clientFactory.CreateClient();

                HttpResponseMessage responseMessage = await httpClient.PostAsync(
                    "https://app.vssps.visualstudio.com/oauth2/token" ?? config.TokenUrl,
                    new FormUrlEncodedContent(form)
                    );

                if (responseMessage.IsSuccessStatusCode)
                {
                    var body = await responseMessage.Content.ReadAsStringAsync();

                    AzdoToken tokenModel = authorizationRequests[state];
                    JsonConvert.PopulateObject(body, tokenModel);

                    return(tokenModel);
                }
                else
                {
                    error = responseMessage.ReasonPhrase;
                    var content = await responseMessage.Content.ReadAsStringAsync();

                    throw new Exception(
                              $"{responseMessage.ReasonPhrase} {(string.IsNullOrEmpty(content) ? "" : $"({content})")}");
                }
            }