How do I get an OAuth 2.0 authentication token in C#

后端 未结 7 1162
借酒劲吻你
借酒劲吻你 2020-12-02 11:15

I have these settings:

  • Auth URL (which happens to be a \"https://login.microsoftonline.com/...\") if that helps.
  • Access Token URL \"https://service.en
相关标签:
7条回答
  • 2020-12-02 11:35

    In Postman, click Generate Code and then in Generate Code Snippets dialog you can select a different coding language, including C# (RestSharp).

    Also, you should only need the access token URL. The form parameters are then:

    grant_type=client_credentials
    client_id=abc    
    client_secret=123
    

    Code Snippet:

    /* 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);
    

    From the response body you can then obtain your access token. For instance for a Bearer token type you can then add the following header to subsequent authenticated requests:

    request.AddHeader("authorization", "Bearer <access_token>");
    
    0 讨论(0)
  • 2020-12-02 11:35

    The Rest Client answer is perfect! (I upvoted it)

    But, just in case you want to go "raw"

    ..........

    I got this to work with 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/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; }
    }       
    

    Here is another working example (based off the answer above)......with a few more tweaks. Sometimes the token-service is finicky:

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

    APPEND

    Bonus Material!

    If you ever get a

    "The remote certificate is invalid according to the validation procedure."

    exception......you can wire in a handler to see what is going on (and massage if necessary)

    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 & System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors) != 0)
                {
                    if (chain != null && chain.ChainStatus != null)
                    {
                        foreach (System.Security.Cryptography.X509Certificates.X509ChainStatus status in chain.ChainStatus)
                        {
                            if ((certificate.Subject == certificate.Issuer) &&
                               (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 & 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; }
            }
    
        }
    }
    

    ........................

    I recently found (Jan/2020) an article about all this. I'll add a link here....sometimes having 2 different people show/explain it helps someone trying to learn it.

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

    0 讨论(0)
  • 2020-12-02 11:39

    Clearly:

    Server side generating a token example

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

    (note: Tokenizer is my helper class that contains Issuer Audience etc..)

    Definitely:

    Client side getting a token for authentication

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

    You can use this token for the authorization (that is in the subsequent requests)

    0 讨论(0)
  • 2020-12-02 11:39

    https://github.com/IdentityModel/IdentityModel adds extensions to HttpClient to acquire tokens using different flows and the documentation is great too. It's very handy because you don't have to think how to implement it yourself. I'm not aware if any official MS implementation exists.

    0 讨论(0)
  • 2020-12-02 11:51

    I used ADAL.NET/ Microsoft Identity Platform to achieve this. The advantage of using it was that we get a nice wrapper around the code to acquire AccessToken and we get additional features like Token Cache out-of-the-box. From the documentation:

    Why use ADAL.NET ?

    ADAL.NET V3 (Active Directory Authentication Library for .NET) enables developers of .NET applications to acquire tokens in order to call secured Web APIs. These Web APIs can be the Microsoft Graph, or 3rd party Web APIs.

    Here is the code snippet:

        // 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;
           }
       }
    
    0 讨论(0)
  • 2020-12-02 11:52

    Here is a complete example. Right click on the solution to manage nuget packages and get Newtonsoft and 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);
            }        
        }
    }
    
    0 讨论(0)
提交回复
热议问题