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

#c# #dotnet-httpclient #bearer-token

Вопрос:

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

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

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

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

Спасибо.

Комментарии:

1. является ли ваш вопрос решением, если да, пожалуйста, поделитесь здесь.

2. Да, Выбранный ответ приведен ниже. В других ответах также есть ряд хороших альтернатив.

Ответ №1:

В Postman нажмите «Сгенерировать код«, а затем в диалоговом окне «Сгенерировать фрагменты кода» вы можете выбрать другой язык программирования, включая C# (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_credentialsamp;client_id=abcamp;client_secret=123", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
 

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

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

Комментарии:

1. Блестяще, сработало идеально — я буду винить noobness в том, что он не заметил, что я мог бы изменить язык кода. Я также обнаружил (после публикации этого), что мне также необходимо изменить протокол безопасности ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; (по крайней мере, в моем случае), встречаются другие разумные коды состояния «0».

2. Какое пространство имен вы используете, я не знаком с RestClient

3. поделитесь кодом для access_token, используя ответ. я не могу его найти. мне также нужен access_token и Refresh_token.

4. Спас мне жизнь! большое спасибо

5. Вы никогда не должны допускать этого без предварительной аутентификации пользователя.

Ответ №2:

В остальном ответ клиента идеален! (Я поддержал его)

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

……….

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

«абстрактно» то, что вы делаете, — это

  1. создание запроса на публикацию.
  2. с телом полезной нагрузки «типа» «x-www-форма-url-кодированный». ( см. FormUrlEncodedContent https://docs.microsoft.com/en-us/dotnet/api/system.net.http.formurlencodedcontent?view=net-5.0 и обратите внимание на конструктор : https://docs.microsoft.com/en-us/dotnet/api/system.net.http.formurlencodedcontent.-ctor?view=net-5.0)
  3. и в полезной нагрузке «тип»: x-www-форма-url-кодированный, вы вводите определенные значения, такие как grant_type, client_id, client_secret и т. Д.

Обратите внимание, попробуйте заставить его работать в PostMan, и тогда будет проще «закодировать его», используя приведенный ниже код.

Но вот и мы, код с использованием HttpClient.

…….

 /*
.nugetpackagesnewtonsoft.json12.0.1
.nugetpackagessystem.net.http4.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/oauth2/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;
    }
 

добавлять

Бонусный Материал!

Если ты когда-нибудь получишь

«Удаленный сертификат недействителен в соответствии с процедурой проверки».

исключение……вы можете подключить обработчик, чтобы увидеть, что происходит (и при необходимости помассировать)

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

namespace MyNamespace
{
    public class MyTokenRetrieverWithExtraStuff
    {
        public static async Task<Token> GetElibilityToken()
        {
            using (HttpClientHandler httpClientHandler = new HttpClientHandler())
            {
                httpClientHandler.ServerCertificateCustomValidationCallback = CertificateValidationCallBack;
                using (HttpClient client = new HttpClient(httpClientHandler))
                {
                    return await GetElibilityToken(client);
                }
            }
        }

        private static async Task<Token> GetElibilityToken(HttpClient client)
        {
            // throws certificate error if your cert is wired to localhost // 
            //string baseAddress = @"https://127.0.0.1/someapp/oauth2/token";

            //string baseAddress = @"https://localhost/someapp/oauth2/token";

        string baseAddress = @"https://blah.blah.blah.com/oauth2/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;
        }

        private static bool CertificateValidationCallBack(
        object sender,
        System.Security.Cryptography.X509Certificates.X509Certificate certificate,
        System.Security.Cryptography.X509Certificates.X509Chain chain,
        System.Net.Security.SslPolicyErrors sslPolicyErrors)
        {
            // If the certificate is a valid, signed certificate, return true.
            if (sslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
            {
                return true;
            }

            // If there are errors in the certificate chain, look at each error to determine the cause.
            if ((sslPolicyErrors amp; System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors) != 0)
            {
                if (chain != null amp;amp; chain.ChainStatus != null)
                {
                    foreach (System.Security.Cryptography.X509Certificates.X509ChainStatus status in chain.ChainStatus)
                    {
                        if ((certificate.Subject == certificate.Issuer) amp;amp;
                           (status.Status == System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.UntrustedRoot))
                        {
                            // Self-signed certificates with an untrusted root are valid. 
                            continue;
                        }
                        else
                        {
                            if (status.Status != System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.NoError)
                            {
                                // If there are any other errors in the certificate chain, the certificate is invalid,
                                // so the method returns false.
                                return false;
                            }
                        }
                    }
                }

                // When processing reaches this line, the only errors in the certificate chain are 
                // untrusted root errors for self-signed certificates. These certificates are valid
                // for default Exchange server installations, so return true.
                return true;
            }


            /* overcome localhost and 127.0.0.1 issue */
            if ((sslPolicyErrors amp; System.Net.Security.SslPolicyErrors.RemoteCertificateNameMismatch) != 0)
            {
                if (certificate.Subject.Contains("localhost"))
                {
                    HttpRequestMessage castSender = sender as HttpRequestMessage;
                    if (null != castSender)
                    {
                        if (castSender.RequestUri.Host.Contains("127.0.0.1"))
                        {
                            return true;
                        }
                    }
                }
            }

            return false;

        }


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

    }
}
 

……………………

Недавно я нашел (январь/2020) статью обо всем этом. Я добавлю ссылку здесь….иногда, когда 2 разных человека показывают/объясняют, что это помогает кому-то, кто пытается это выучить.

http://luisquintanilla.me/2017/12/25/client-credentials-authentication-csharp/

Комментарии:

1. Спасибо за это! Помогает при попытке свести количество пакетов NuGet к минимуму, где это возможно.

2. как насчет перестановки, в которой используется сертификат?

3. Не могли бы вы предоставить ссылку на тип гранта oauth2, на который вы ссылаетесь? alexbilbie.com/guide-to-oauth-2-grants в конце концов, вы просто массируете http-запрос со вкусом oauth (имеет определенные заголовки, определенные элементы в полезной нагрузке и т. Д. и т. Д.)

4. Я использую это для неправильного идентификатора клиента, он должен вызывать исключение, но нет. Я использовал блоки try catch, но код не попал туда, не знаю почему?

5. Какова ценность вашего : HttpResponseMessage. Свойство StatusCode (в этом примере кода в моем ответе, это, вероятно, ответ на токен. Код состояния ) ? docs.microsoft.com/en-us/dotnet/api/…

Ответ №3:

Вот полный пример. Щелкните правой кнопкой мыши на решении для управления пакетами 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_credentialsamp;scope=allamp;client_id="   id   "amp;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);
        }        
    }
}
 

Комментарии:

1. используя приведенный выше код, появляется ошибка: исключение типа «Newtonsoft.Json.JsonReaderException» произошло в Newtonsoft.Json.dll но не был обработан в пользовательском коде Дополнительная информация: Ошибка чтения объекта задания из JsonReader. Путь «, строка 0, позиция 0.

2. @RaviKantSingh, ты должен проверить свой response . Я предполагаю, что ваш запрос был отклонен, поэтому вы получили неправильный ответ, который объект работы не может понять

Ответ №4:

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

Зачем использовать 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;
       }
   }
 

Комментарии:

1. Может ли даунвотер указать причину для даунвота?

2. Разве это не ограничивается токенами Microsoft?

3. В документации говорится, что он также должен работать с третьей стороной.

4. Я этого не читаю. Насколько я понимаю, единственным поставщиком токенов является Azure AD.

5. откуда берется конфигурация аутентификации?

Ответ №5:

Вы можете использовать следующий код для получения токена на предъявителя.

 private string GetBearerToken()
{
    var client = new RestClient("https://service.endpoint.com");
    client.Authenticator = new HttpBasicAuthenticator("abc", "123");
    var request = new RestRequest("api/oauth2/token", Method.POST);
    request.AddHeader("content-type", "application/json");
    request.AddParameter("application/json", "{ "grant_type":"client_credentials" }", 
    ParameterType.RequestBody);
    var responseJson = _client.Execute(request).Content;
    var token = JsonConvert.DeserializeObject<Dictionary<string, object>>(responseJson)["access_token"].ToString();
    if(token.Length == 0)
    {
        throw new AuthenticationException("API authentication failed.");
    }
    return token;
}
 

Ответ №6:

В этом примере получите токен thouth 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();
        }
 

Ответ №7:

Ясно:

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

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

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

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

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

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

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

Ответ №8:

https://github.com/IdentityModel/IdentityModel добавляет расширения HttpClient для получения токенов с использованием различных потоков, и документация также великолепна. Это очень удобно, потому что вам не нужно думать, как реализовать это самостоятельно. Я не знаю, существует ли какая-либо официальная реализация MS.

Ответ №9:

Я попытался таким образом получить токен аутентификации OAuth 2.0 с помощью c#

 namespace ConsoleApp2
{

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(GetToken());
            Console.Read();
        }

        /// <summary>
        /// Get access token from api
        /// </summary>
        /// <returns></returns>
        private static string GetToken()
        {
            string wClientId = "#######";
            string wClientSecretKey = "*********************";
            string wAccessToken;

//--------------------------- Approch-1 to get token using HttpClient -------------------------------------------------------------------------------------
            HttpResponseMessage responseMessage;
            using (HttpClient client = new HttpClient())
            {
                HttpRequestMessage tokenRequest = new HttpRequestMessage(HttpMethod.Post, "https://localhost:1001/oauth/token");
                HttpContent httpContent = new FormUrlEncodedContent(
                        new[]
                        {
                                        new KeyValuePair<string, string>("grant_type", "client_credentials"),
                        });
                tokenRequest.Content = httpContent;
                tokenRequest.Headers.Add("Authorization", "Basic "   Convert.ToBase64String(Encoding.Default.GetBytes(wClientId   ":"   wClientSecretKey)));
                responseMessage =  client.SendAsync(tokenRequest).Resu<
            }
            string ResponseJSON=   responseMessage.Content.ReadAsStringAsync().Resu<


//--------------------------- Approch-2 to get token using HttpWebRequest and deserialize json object into ResponseModel class -------------------------------------------------------------------------------------


            byte[] byte1 = Encoding.ASCII.GetBytes("grant_type=client_credentials");

            HttpWebRequest oRequest = WebRequest.Create("https://localhost:1001/oauth/token") as HttpWebRequest;
            oRequest.Accept = "application/json";
            oRequest.Method = "POST";
            oRequest.ContentType = "application/x-www-form-urlencoded";
            oRequest.ContentLength = byte1.Length;
            oRequest.KeepAlive = false;
            oRequest.Headers.Add("Authorization", "Basic "   Convert.ToBase64String(Encoding.Default.GetBytes(wClientId   ":"   wClientSecretKey)));
            Stream newStream = oRequest.GetRequestStream();
            newStream.Write(byte1, 0, byte1.Length);

            WebResponse oResponse = oRequest.GetResponse();

            using (var reader = new StreamReader(oResponse.GetResponseStream(), Encoding.UTF8))
            {
                var oJsonReponse = reader.ReadToEnd();
                ResponseModel oModel = JsonConvert.DeserializeObject<ResponseModel>(oJsonReponse);
                wAccessToken = oModel.access_token;
            }

            return wAccessToken;
        }
    }
  
  //----------------------------------------------------------------------------------------------------------------------------------------------------
  //---------------------------------- Response Class---------------------------------------------------------------------------------------
  //----------------------------------------------------------------------------------------------------------------------------------------------------

    /// <summary>
    /// De-serialize Web response Object into model class to read  
    /// </summary>
    public class ResponseModel
    {
        public string scope { get; set; }
        public string token_type { get; set; }
        public string expires_in { get; set; }
        public string refresh_token { get; set; }
        public string access_token { get; set; }
    }
}