Как получить токен аутентификации OAuth 2.0 в С#

У меня есть эти настройки:

Затем мне нужно позвонить с использованием токена на предъявителя в заголовке.

Я могу заставить это работать в Почтальоне, но наткнулся на стену, пытаясь решить, как реализовать это в С#. Я использую RestSharp (но открыт для других). Все это кажется настолько непрозрачным, когда я думал, что это будет довольно просто: это консольное приложение, поэтому мне не нужны навороты.

В конечном итоге я хочу, чтобы мое приложение (программно) получило токен, а затем использовало его для моих последующих вызовов. Я был бы признателен всем, кто указал мне на документацию или примеры, которые ясно объясняют, что я ищу. Все, с чем я сталкивался, частично или для служб, работающих в другом потоке.

Благодарю.

Ответ 1

В "Почтальоне" нажмите "Создать код", а затем в диалоговом окне "Создать фрагменты кода" вы можете выбрать другой язык кодирования, включая С# (RestSharp).

Кроме того, вам нужен только URL токена доступа. Параметры формы тогда:

grant_type=client_credentials
client_id=abc    
client_secret=123

Фрагмент кода:

/* using RestSharp; // https://www.nuget.org/packages/RestSharp/ */

var client = new RestClient("https://service.endpoint.com/api/oauth2/token");
var request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddParameter("application/x-www-form-urlencoded", "grant_type=client_credentials&client_id=abc&client_secret=123", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);

Из тела ответа вы можете получить свой токен доступа. Например, для типа токена Bearer вы можете добавить следующий заголовок к последующим аутентифицированным запросам:

request.AddHeader("authorization", "Bearer <access_token>");

Ответ 2

Ответ Rest Client идеален! (Я проголосовал за это)

Но, на всякий случай, если вы хотите пойти "сырой"

..........

Я получил это для работы с HttpClient.

/*
.nuget\packages\newtonsoft.json\12.0.1
.nuget\packages\system.net.http\4.3.4
*/

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web;


    private static async Task<Token> GetElibilityToken(HttpClient client)
    {
        string baseAddress = @"https://blah.blah.blah.com/token";

        string grant_type = "client_credentials";
        string client_id = "myId";
        string client_secret = "shhhhhhhhhhhhhhItsSecret";

        var form = new Dictionary<string, string>
                {
                    {"grant_type", grant_type},
                    {"client_id", client_id},
                    {"client_secret", client_secret},
                };

        HttpResponseMessage tokenResponse = await client.PostAsync(baseAddress, new FormUrlEncodedContent(form));
        var jsonContent = await tokenResponse.Content.ReadAsStringAsync();
        Token tok = JsonConvert.DeserializeObject<Token>(jsonContent);
        return tok;
    }


internal class Token
{
    [JsonProperty("access_token")]
    public string AccessToken { get; set; }

    [JsonProperty("token_type")]
    public string TokenType { get; set; }

    [JsonProperty("expires_in")]
    public int ExpiresIn { get; set; }

    [JsonProperty("refresh_token")]
    public string RefreshToken { get; set; }
}       

Вот еще один рабочий пример (основанный на ответе выше)...... с еще несколькими изменениями. Иногда сервис токенов является привередливым:

    private static async Task<Token> GetATokenToTestMyRestApiUsingHttpClient(HttpClient client)
    {
        /* this code has lots of commented out stuff with different permutations of tweaking the request  */

        /* this is a version of asking for token using HttpClient.  aka, an alternate to using default libraries instead of RestClient */

        OAuthValues oav = GetOAuthValues(); /* object has has simple string properties for TokenUrl, GrantType, ClientId and ClientSecret */

        var form = new Dictionary<string, string>
                {
                    { "grant_type", oav.GrantType },
                    { "client_id", oav.ClientId },
                    { "client_secret", oav.ClientSecret }
                };

        /* now tweak the http client */
        client.DefaultRequestHeaders.Clear();
        client.DefaultRequestHeaders.Add("cache-control", "no-cache");

        /* try 1 */
        ////client.DefaultRequestHeaders.Add("content-type", "application/x-www-form-urlencoded");

        /* try 2 */
        ////client.DefaultRequestHeaders            .Accept            .Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));//ACCEPT header

        /* try 3 */
        ////does not compile */client.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");

        ////application/x-www-form-urlencoded

        HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, oav.TokenUrl);
        /////req.RequestUri = new Uri(baseAddress);

        req.Content = new FormUrlEncodedContent(form);

        ////string jsonPayload = "{\"grant_type\":\"" + oav.GrantType + "\",\"client_id\":\"" + oav.ClientId + "\",\"client_secret\":\"" + oav.ClientSecret + "\"}";
        ////req.Content = new StringContent(jsonPayload,                                                Encoding.UTF8,                                                "application/json");//CONTENT-TYPE header

        req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");

        /* now make the request */
        ////HttpResponseMessage tokenResponse = await client.PostAsync(baseAddress, new FormUrlEncodedContent(form));
        HttpResponseMessage tokenResponse = await client.SendAsync(req);
        Console.WriteLine(string.Format("HttpResponseMessage.ReasonPhrase='{0}'", tokenResponse.ReasonPhrase));

        if (!tokenResponse.IsSuccessStatusCode)
        {
            throw new HttpRequestException("Call to get Token with HttpClient failed.");
        }

        var jsonContent = await tokenResponse.Content.ReadAsStringAsync();
        Token tok = JsonConvert.DeserializeObject<Token>(jsonContent);

        return tok;
    }

Ответ 3

В этом примере показано, как получить токен HttpWebRequest

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(pathapi);
        request.Method = "POST";
        string postData = "grant_type=password";
        ASCIIEncoding encoding = new ASCIIEncoding();
        byte[] byte1 = encoding.GetBytes(postData);

        request.ContentType = "application/x-www-form-urlencoded";

        request.ContentLength = byte1.Length;
        Stream newStream = request.GetRequestStream();
        newStream.Write(byte1, 0, byte1.Length);

        HttpWebResponse response = request.GetResponse() as HttpWebResponse;            
        using (Stream responseStream = response.GetResponseStream())
        {
            StreamReader reader = new StreamReader(responseStream, Encoding.UTF8);
            getreaderjson = reader.ReadToEnd();
        }

Ответ 4

Очевидно:

Пример создания токена на стороне сервера

private string GenerateToken(string userName)
{
    var someClaims = new Claim[]{
        new Claim(JwtRegisteredClaimNames.UniqueName, userName),
        new Claim(JwtRegisteredClaimNames.Email, GetEmail(userName)),
        new Claim(JwtRegisteredClaimNames.NameId,Guid.NewGuid().ToString())
    };

    SecurityKey securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_settings.Tokenizer.Key));
    var token = new JwtSecurityToken(
        issuer: _settings.Tokenizer.Issuer,
        audience: _settings.Tokenizer.Audience,
        claims: someClaims,
        expires: DateTime.Now.AddHours(_settings.Tokenizer.ExpiryHours),
        signingCredentials: new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256)
    );

    return new JwtSecurityTokenHandler().WriteToken(token);
}

(примечание: Tokenizer - это мой вспомогательный класс, содержащий аудиторию эмитента и т.д.)

Определенно:

Клиентская сторона получает токен для аутентификации

    public async Task<string> GetToken()
    {
        string token = "";
        var siteSettings = DependencyResolver.Current.GetService<SiteSettings>();

        var client = new HttpClient();
        client.BaseAddress = new Uri(siteSettings.PopularSearchRequest.StaticApiUrl);
        client.DefaultRequestHeaders.Accept.Clear();
        //client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        StatisticUserModel user = new StatisticUserModel()
        {
            Password = siteSettings.PopularSearchRequest.Password,
            Username = siteSettings.PopularSearchRequest.Username
        };

        string jsonUser = JsonConvert.SerializeObject(user, Formatting.Indented);
        var stringContent = new StringContent(jsonUser, Encoding.UTF8, "application/json");
        var response = await client.PostAsync(siteSettings.PopularSearchRequest.StaticApiUrl + "/api/token/new", stringContent);
        token = await response.Content.ReadAsStringAsync();

        return token;
    }

Вы можете использовать этот токен для авторизации (то есть в последующих запросах)

Ответ 5

Вот полный пример. Щелкните правой кнопкой мыши по решению, чтобы управлять пакетами nuget и получить Newtonsoft и RestSharp:

using Newtonsoft.Json.Linq;
using RestSharp;
using System;


namespace TestAPI
{
    class Program
    {
        static void Main(string[] args)
        {
            String id = "xxx";
            String secret = "xxx";

            var client = new RestClient("https://xxx.xxx.com/services/api/oauth2/token");
            var request = new RestRequest(Method.POST);
            request.AddHeader("cache-control", "no-cache");
            request.AddHeader("content-type", "application/x-www-form-urlencoded");
            request.AddParameter("application/x-www-form-urlencoded", "grant_type=client_credentials&scope=all&client_id=" + id + "&client_secret=" + secret, ParameterType.RequestBody);
            IRestResponse response = client.Execute(request);

            dynamic resp = JObject.Parse(response.Content);
            String token = resp.access_token;            

            client = new RestClient("https://xxx.xxx.com/services/api/x/users/v1/employees");
            request = new RestRequest(Method.GET);
            request.AddHeader("authorization", "Bearer " + token);
            request.AddHeader("cache-control", "no-cache");
            response = client.Execute(request);
        }        
    }
}

Ответ 6

Я использовал ADAL.NET/Microsoft Identity Platform для достижения этой цели. Преимущество его использования состояло в том, что мы получаем красивую оболочку вокруг кода для приобретения AccessToken и получаем дополнительные функции, такие как Token Cache, из коробки. Из документации:

Why use ADAL.NET?

ADAL.NET V3 (библиотека аутентификации Active Directory для .NET) позволяет разработчикам приложений .NET приобретать токены для вызова защищенных веб-API. Этими веб-API могут быть Microsoft Graph или сторонние веб-API.

Вот фрагмент кода:

    // Import Nuget package: Microsoft.Identity.Client
    public class AuthenticationService
    {
         private readonly List<string> _scopes;
         private readonly IConfidentialClientApplication _app;

        public AuthenticationService(AuthenticationConfiguration authentication)
        {

             _app = ConfidentialClientApplicationBuilder
                         .Create(authentication.ClientId)
                         .WithClientSecret(authentication.ClientSecret)
                         .WithAuthority(authentication.Authority)
                         .Build();

           _scopes = new List<string> {$"{authentication.Audience}/.default"};
       }

       public async Task<string> GetAccessToken()
       {
           var authenticationResult = await _app.AcquireTokenForClient(_scopes) 
                                                .ExecuteAsync();
           return authenticationResult.AccessToken;
       }
   }