简体   繁体   English

如何使用RestSharp为承载令牌实现自定义IAuthenticationModule

[英]How to implement a custom IAuthenticationModule for bearer tokens with RestSharp

I am using RestSharp to make requests to an API which uses bearer token authentication. 我正在使用RestSharp向使用承载令牌身份验证的API发出请求。 Most requests run as expected, but there is a specific endpoint which always redirects the request to a dynamic location. 大多数请求都按预期运行,但是有一个特定的端点始终将请求重定向到动态位置。 When this redirect occurs, the Authorization header is lost (by design), thus resulting in a Bad Request. 发生此重定向时,授权标头会丢失(根据设计),从而导致错误请求。

I've done some looking into the issue and found one similar issue here , but the custom AuthenticationModule I made is never having the Authenticate function called. 我已经对该问题进行了一些调查,并在这里发现了一个类似的问题,但是我制作的自定义AuthenticationModule从未调用过Authenticate函数。

Am I missing something obvious in the setup that is preventing the Authentication module from being used, or is something else going on? 我是否在设置中遗漏了一些明显的东西,这些东西阻止了Authentication模块的使用,或者还有其他事情吗?

Thanks! 谢谢!

My Authenticator class: 我的身份验证器类:

public class AdpAuthenticator : IAuthenticator
{
    /// <summary>
    /// The current access token for making requests to the API.
    /// </summary>
    private static string AccessToken { get; set; }

    /// <summary>
    /// When the current access token expires.
    /// </summary>
    private static DateTime TokenExpiresOn { get; set; }

    private static CredentialCache CredentialCache { get; set; }

    /// <summary>
    /// Singleton instance for making requests for access tokens.
    /// </summary>
    private static IRestClient AuthenticationClient { get; set; }

    /// <summary>
    /// Singleton instance of the request for obtaining access tokens.
    /// </summary>
    private static IRestRequest AuthenticationRequest { get; set; }

    /// <summary>
    /// Construct a new AdpAuthenticator.
    /// </summary>
    /// <param name="adpClientId"></param>
    /// <param name="adpClientSecret"></param>
    /// <param name="adpCertPath"></param>
    public AdpAuthenticator(string adpClientId, string adpClientSecret, string adpCertPath)
    {
        if (string.IsNullOrWhiteSpace(adpClientId)) throw new ArgumentNullException("Passed adpClientId was empty or null.");
        if (string.IsNullOrWhiteSpace(adpClientSecret)) throw new ArgumentNullException("Passed adpClientSecret was empty or null.");

        if (CredentialCache == null)
        {
            CredentialCache = new CredentialCache
            {
                {new Uri("https://api.adp.com"), "Basic", new NetworkCredential(adpClientId, adpClientSecret) }
            };
        }

        if (AuthenticationClient == null)
        {
            X509Certificate2Collection certificateCollection;
            X509Certificate2 certificate = new X509Certificate2(adpCertPath);
            certificateCollection = new X509Certificate2Collection
            {
                certificate
            };

            AuthenticationClient = new RestClient("https://api.adp.com")
            {
                ClientCertificates = certificateCollection,
                Authenticator = new HttpBasicAuthenticator(adpClientId, adpClientSecret)
            };
            AuthenticationClient.UseSerializer(new JsonNetSerializer());
        }

        if (AuthenticationRequest == null)
        {
            AuthenticationRequest = new RestRequest("auth/oauth/v2/token", Method.POST)
            {
                Credentials = CredentialCache
            };
            AuthenticationRequest.AddOrUpdateParameter("grant_type", "client_credentials", ParameterType.QueryString);
        }

        RegisterAuthenticationModule(new Uri("https://api.adp.com/"));
    }

    /// <summary>
    /// Authenticate a request.
    /// </summary>
    /// <param name="client"></param>
    /// <param name="request"></param>
    public void Authenticate(IRestClient client, IRestRequest request)
    { 
        //If accessToken is null or expired, get a new one.
        if (!HasValidToken())
        {
            RefreshAccessToken();
        }
        //request.AddOrUpdateParameter("Authorization", AccessToken, ParameterType.HttpHeader);
        //var newCache = new CredentialCache
        //{
        //    {new Uri("https://api.adp.com/"), "Bearer", new NetworkCredential(AccessToken, "") }
        //};
        var newCache = new CredentialCache();
        newCache.Add(new Uri("https://api.adp.com/"), AdpAuthenticationModule.TheAuthenticationType, new NetworkCredential(AccessToken, ""));

        request.Credentials = newCache;
        //request.AddOrUpdateParameter("Authorization", "Bearer " + AccessToken, ParameterType.HttpHeader);

    }

    private void RefreshAccessToken()
    {
        try
        {
            var response = AuthenticationClient.Execute<AuthorizationResponse>(AuthenticationRequest);
            if (response.StatusCode != System.Net.HttpStatusCode.OK)
            {
                throw new FailedAuthenticationException($"Authentication failed to refresh access token, returned with code {response.StatusCode}. Content: \"{response.Content}\".", null);
            }
            if (string.IsNullOrWhiteSpace(response.Data.access_token))
            {
                throw new Exception("Error: response returned during access token refresh gave Status 200 OK, but access_token returned was null or whitespace.");
            }
            AccessToken = response.Data.access_token;
            if (response.Data.expires_in <= 0)
            {
                throw new Exception("Error: response returned during access token refresh gave Status 200 OK, but expires_in value returned was <=0.");
            }
            TokenExpiresOn = DateTime.Now.AddSeconds(response.Data.expires_in);
        }
        catch (FailedAuthenticationException)
        {
            throw;
        }
        catch (Exception e)
        {
            throw new FailedAuthenticationException($"Authentication failed to refresh access token, see inner exception details.", e);
        }
    }

    /// <summary>
    /// Returns whether the current access token is valid.
    /// </summary>
    /// <returns>False if token is null or has 10 or less minutes until expiry; else returns true.</returns>
    public bool HasValidToken()
    {
        return !string.IsNullOrEmpty(AccessToken) && DateTime.Now.CompareTo(TokenExpiresOn.AddMinutes(-10.0)) < 0;
    }

    private static AdpAuthenticationModule RegisterAuthenticationModule(Uri loginServerUrl)
    {
        var registeredModules = AuthenticationManager.RegisteredModules;
        AdpAuthenticationModule authenticationModule;

        while (registeredModules.MoveNext())
        {
            object current = registeredModules.Current;
            if (current is AdpAuthenticationModule)
            {
                authenticationModule = (AdpAuthenticationModule)current;
                if (authenticationModule.LoginServerUrl.Equals(loginServerUrl))
                {
                    return authenticationModule;
                }
            }
        }

        authenticationModule = new AdpAuthenticationModule(loginServerUrl);
        AuthenticationManager.Register(authenticationModule);
        return authenticationModule;
    }
}

My Custom Authentication Module: 我的自定义身份验证模块:

public class AdpAuthenticationModule : IAuthenticationModule
{
    /// <summary>
    /// The name of the custom authentication type.
    /// </summary>
    public string AuthenticationType => TheAuthenticationType;

    public static string TheAuthenticationType => "AdpAuthentication";

    /// <summary>
    /// Returns false, as this IAuthenticationModule cannot pre-authenticate.
    /// </summary>
    public bool CanPreAuthenticate => false;

    private readonly CredentialCache credentialCache = new CredentialCache();
    private readonly Uri loginServerUrl;

    internal CredentialCache CredentialCache
    {
        get
        {
            return credentialCache;
        }
    }

    internal Uri LoginServerUrl
    {
        get
        {
            return loginServerUrl;
        }
    }

    internal AdpAuthenticationModule(Uri loginServerUrl)
    {
        this.loginServerUrl = loginServerUrl ?? throw new ArgumentNullException("AdpAuthenticationModule.loginServerUrl");
    }

    /// <summary>
    /// Builds and returns a <see cref="Authorization"/> object for a request.
    /// </summary>
    /// <param name="challenge"></param>
    /// <param name="request"></param>
    /// <param name="credentials"></param>
    /// <returns></returns>
    public Authorization Authenticate(string challenge, WebRequest request, ICredentials credentials)
    {
        Authorization result = null;

        if (request != null && credentials != null)
        {
            NetworkCredential creds = credentials.GetCredential(LoginServerUrl, AuthenticationType);
            if (creds == null)
            {
                return null;
            }

            ICredentialPolicy policy = AuthenticationManager.CredentialPolicy;
            if (policy != null && !policy.ShouldSendCredential(LoginServerUrl, request, creds, this))
            {
                return null;
            }

            string token = Convert.ToBase64String(Encoding.UTF8.GetBytes(creds.UserName));

            result = new Authorization(string.Format("Bearer {0}", token));
        }

        return result;
    }

    /// <summary>
    /// Returns null, since this IAuthenticationModule cannot pre-authenticate.
    /// </summary>
    /// <param name="request"></param>
    /// <param name="credentials"></param>
    /// <returns></returns>
    public Authorization PreAuthenticate(WebRequest request, ICredentials credentials)
    {
        return null;
    }
}

Implementation of IAuthenticationModule need to be registered in the AuthenticationManager class from System.Net. 需要从System.Net在AuthenticationManager类中注册IAuthenticationModule的实现。

Use the following code : 使用以下代码:

AuthenticationManager.Register(new AdpAuthenticationModule());

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Web API承载令牌 - 我可以使用自定义令牌吗? - Web API Bearer tokens - can I use custom tokens? 如何使用RestSharp实现反序列化规则? - How to implement deserialization rules with RestSharp? “ WebRequest”对象如何确定要使用的“ IAuthenticationModule”实现? - How “WebRequest” object determine the “IAuthenticationModule” implementation to use? 在WebApi2中使用Owin返回带有Bearer令牌的自定义类型 - Return custom type with Bearer Tokens using Owin in WebApi2 如何使用RestSharp实现调用ExecuteAsync的调用 - How to implement call to call ExecuteAsync using RestSharp 授权和应用服务器-无ajax。 如何使用承载令牌? - Authorization and Application Server - no ajax. How to use bearer tokens? "如何更改刷新令牌和不记名身份验证中的声明值" - How to change claim values in refresh tokens and bearer authentication 如何使RestSharp返回我的自定义RestResponse - How to make RestSharp return my custom RestResponse OWIN Cookie和不记名令牌是否相同? - Are OWIN Cookie & Bearer Tokens the same? 我应该如何在 Windows Phone 7 上使用 RestSharp 实现 ExecuteAsync? - How should I implement ExecuteAsync with RestSharp on Windows Phone 7?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM