【问题标题】:How do I use the VSTS OAuth2 Bearer Token provided by the authorization routine?如何使用授权例程提供的 VSTS OAuth2 Bearer Token?
【发布时间】:2018-03-10 11:15:53
【问题描述】:

我正在编写一个 MVC5 Web 应用程序,以允许我在 Visual Studio Team Services (VSTS) 中查询我们的工作项。

遵循this onethis one 之类的教程后,我已经成功创建了应用程序,以便它可以使用我为开发目的创建的个人访问令牌 (PAT) 检索我想要的工作项。我还通过密切关注可用的示例here,成功创建了整个 OAuth2 流程,以便将用户带到 VSTS,要求授权我的应用程序,然后返回到我的回调页面。回调 URL 正确包含用户的访问令牌、刷新令牌等。到目前为止,一切顺利。

我将用户的刷新令牌连同到期日期和时间一起存储在我的数据库中的用户记录中(以便我知道如果他们在访问令牌过期后尝试访问应用程序时刷新令牌)。

我的问题是我无法弄清楚如何在 C# 代码中使用访问令牌而不是我自己的 PAT 来查询 VSTS。我正在使用的代码如下(它与我在上面链接到的 GitHub 上的示例中的代码几乎相同),它运行良好,但正如您所看到的,它使用的是 PAT。我该如何改用用户的访问令牌,目前我只是将其视为string,当它由 API 返回时(可能是错误的?)。

public class GetFeatures
{
    readonly string _uri;
    readonly string _personalAccessToken;
    readonly string _project;

    public GetFeatures()
    {
        _uri = "https://myaccount.visualstudio.com";
        _personalAccessToken = "abc123xyz456"; //Obviously I've redacted my actual PAT
        _project = "My Project";
    }

    public List<VSTSFeatureModel> AllFeatures()
    {
        Uri uri = new Uri(_uri);
        string personalAccessToken = _personalAccessToken;
        string project = _project;

        VssBasicCredential credentials = new VssBasicCredential("", _personalAccessToken);

        //create a wiql object and build our query
        Wiql wiql = new Wiql()
        {
            Query = "Select [State], [Title] " +
                    "From WorkItems " +
                    "Where [Work Item Type] = 'Feature' " +
                    "And [System.TeamProject] = '" + project + "' " +
                    "And [System.State] <> 'Removed' " +
                    "Order By [State] Asc, [Changed Date] Desc"
        };

        //create instance of work item tracking http client
        using (WorkItemTrackingHttpClient workItemTrackingHttpClient = new WorkItemTrackingHttpClient(uri, credentials))
        {
            //execute the query to get the list of work items in the results
            WorkItemQueryResult workItemQueryResult = workItemTrackingHttpClient.QueryByWiqlAsync(wiql).Result;

            //some error handling                
            if (workItemQueryResult.WorkItems.Count() != 0)
            {
                //...do stuff                   
            }

            return null;
        }
    }
}

【问题讨论】:

    标签: c# oauth-2.0 azure-devops azure-devops-rest-api


    【解决方案1】:

    您需要使用VssOAuthCredential 而不是VssBasicCredential

    【讨论】:

    • 我想知道是不是这样,但是查看类需要的参数我不知道应该传递什么值,而且看起来太复杂了。我有用户的访问令牌,当然我需要做的就是将其包含在我对 API 的请求中(正如此处“使用访问令牌”部分所暗示的那样:docs.microsoft.com/en-gb/vsts/integrate/get-started/…,尽管没有说明如何使用.NET 客户端库)。
    【解决方案2】:

    经过大量猜测,由于 VSTS .NET 客户端库的文档记录非常差,我发现 VssOAuthCredential 似乎已被弃用。我能够通过替换

    来让我的代码示例正常工作
    VssBasicCredential credentials = new VssBasicCredential("", _personalAccessToken)
    

    VssOAuthAccessTokenCredential credentials = new VssOAuthAccessTokenCredential(AccessToken);
    

    其中 AccessToken 是一个 string,包含用户的 OAuth 访问令牌。

    【讨论】:

      【解决方案3】:

      那个框架好像没啥用,哈哈……

      我目前正在开发一个备份 VSTS 帐户的项目,我正在通过 HttpRequests 到 REST API 来完成所有这些工作。

      public List<int> GetItemIDs()
          {
              HttpClient client = auth.AuthenticateHTTP(new HttpClient());
              string content = $@"{{""query"": ""Select[System.Id] From WorkItems order by[System.CreatedDate] desc"" }}";
              StringContent stringContent = new StringContent(content, Encoding.UTF8, "application/json");
              string endpoint = "DefaultCollection/_apis/wit/wiql?api-version=1.0";
              Uri requesturl = UriCombine(baseurl, endpoint);
              HttpResponseMessage response = client.PostAsync(requesturl, stringContent).Result;
              string result = response.Content.ReadAsStringAsync().Result;
              var json = Newtonsoft.Json.JsonConvert.DeserializeObject<QueryResponse>(result);
              return json.workItems.Select(x => x.id).ToList();
          }
      
      public List<string> ListToString200(List<int> ids) //Writes all IDs into comma seperated strings of up to 200 IDs and puts them into a List.
              {
                  List<string> idStrings = new List<string>();
      
                  if (ids.Count > 200)
                  {
                      while (ids.Count > 200)
                      {
                          List<int> t = new List<int>();
                          var IDs = ids.Take(200);
                          ids.Remove(200);
                          foreach (var item in IDs)
                          {
                              t.Add(item);
                          }
      
                          var ID = t.ConvertAll(element => element.ToString()).Aggregate((a, b) => $"{a},{b}");
                          idStrings.Add(ID);
                      }
                  }
                  else if (ids.Count > 0)
                  {
                      var ID = ids.ConvertAll(element => element.ToString()).Aggregate((a, b) => $"{a}, {b}");
                      idStrings.Add(ID);
                  }
      
                  return idStrings;
              }
      
      
      private List<WorkItem> GetAllWorkItems()
              {
                  List<int> ids = GetItemIDs();
                  List<WorkItemsContainer> Responses = new List<WorkItemsContainer>();
                  List<WorkItem> ResultList = new List<WorkItem>();
      
                  List<string> idStrings = ListToString200(ids);
      
                  using (HttpClient client = new HttpClient())
                  {
                      auth.AuthenticateHTTP(client);
      
                      foreach (var item in idStrings)
                      {
                          WorkItemsContainer WorkItem = new WorkItemsContainer();
      
                          string featurePath = $"DefaultCollection/_apis/wit/workitems?ids={item}&$expand=all&api-version=1.0";
                          Uri requestUri = Authenticator.UriCombine(baseurl, featurePath);
                          HttpResponseMessage response = client.GetAsync(requestUri).Result;
                          string result = response.Content.ReadAsStringAsync().Result;
                          result = result.Replace("System.", "System");
      
                          WorkItem = JsonConvert.DeserializeObject<WorkItemsContainer>(result);
                          Responses.Add(WorkItem);
                      }
                  }
      
                  foreach (var item in Responses)
                  {
                      foreach (var x in item.value.ToList<WorkItem>())
                      {
                          WorkItemsToJsonFile(x);
                          ResultList.Add(x);
                      }
                  }
                  return ResultList;
              }
      

      使用固定登录会容易得多,虽然不需要 Oauth2,但手动执行 OAuth2 并没有那么难,只需将令牌转换为不记名身份验证标头...

      【讨论】:

      • 我希望我一开始只使用 REST API,但是当我发布我的问题时,我已经投资了 .NET 客户端库!我最终得到了它 - 请参阅我发布的答案。此外,我热衷于使用 OAuth,因为我将有多个用户访问我的应用程序,并且我希望他们像自己一样从 API 读取/写入/写入 API。但是,如果您能告诉我如何在 Ajax 调用中成功传递 OAuth 访问令牌(可以通过 PAT 完成,但无法确定访问令牌),那么您就是我的英雄!
      • 从未听说过 PAT tbh :( 在 C# 中,使用 HttpClient 是 httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "Your Oauth token");
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-10
      • 2019-01-30
      • 1970-01-01
      • 1970-01-01
      • 2017-08-18
      相关资源
      最近更新 更多