예제 #1
0
 /// <summary>
 /// Token request for refresh
 /// </summary>
 public OAuthTokenRequest(TokenClaimsPrincipal current, String scope)
 {
     this.GrantType    = "refresh_token";
     this.RefreshToken = current.RefreshToken;
     this.Scope        = scope;
 }
예제 #2
0
        /// <summary>
        /// Do internal authentication
        /// </summary>
        private IPrincipal DoAuthenticationInternal(String userName = null, String password = null, string tfaSecret = null, bool isOverride = false, TokenClaimsPrincipal refreshPrincipal = null, string purposeOfUse = null, string[] policies = null)
        {
            AuthenticatingEventArgs e = new AuthenticatingEventArgs(userName);

            this.Authenticating?.Invoke(this, e);
            if (e.Cancel)
            {
                this.m_tracer.TraceWarning("Pre-Event ordered cancel of auth {0}", userName);
                return(e.Principal);
            }

            var localIdp = ApplicationContext.Current.GetService <IOfflineIdentityProviderService>();

            IPrincipal retVal = null;

            // Authenticate
            try
            {
                // Is the user a LOCAL_USER only?
                if (localIdp?.IsLocalUser(userName) == true)
                {
                    retVal = localIdp.Authenticate(userName, password, tfaSecret);
                }
                else
                {
                    using (IRestClient restClient = ApplicationContext.Current.GetRestClient("acs"))
                    {
                        // Construct oauth req
                        OAuthTokenRequest request = null;
                        if (refreshPrincipal != null)
                        {
                            request = new OAuthTokenRequest(refreshPrincipal, "*");
                        }
                        else if (!String.IsNullOrEmpty(password))
                        {
                            request = new OAuthTokenRequest(userName, password, "*");
                        }
                        else
                        {
                            request = new OAuthTokenRequest(userName, null, "*");
                        }

                        // Explicit policies
                        if (policies != null)
                        {
                            request.Scope = String.Join(" ", policies);
                        }

                        // Set credentials for oauth req
                        if (ApplicationContext.Current.Configuration.GetSection <SecurityConfigurationSection>().DomainAuthentication == DomainClientAuthentication.Basic)
                        {
                            restClient.Credentials = new OAuthTokenServiceCredentials(null);
                        }
                        else
                        {
                            request.ClientId     = ApplicationContext.Current.Application.Name;
                            request.ClientSecret = ApplicationContext.Current.Application.ApplicationSecret;
                        }

                        restClient.Requesting += (o, p) =>
                        {
                            if (!String.IsNullOrEmpty(tfaSecret))
                            {
                                p.AdditionalHeaders.Add(HeaderTypes.HttpTfaSecret, tfaSecret);
                            }
                            if (isOverride)
                            {
                                p.AdditionalHeaders.Add(HeaderTypes.HttpClaims, Convert.ToBase64String(Encoding.UTF8.GetBytes(
                                                                                                           $"{SanteDBClaimTypes.PurposeOfUse}={purposeOfUse};{SanteDBClaimTypes.SanteDBOverrideClaim}=true"
                                                                                                           )));
                            }
                            // Add device credential
                            if (!String.IsNullOrEmpty(ApplicationContext.Current.Device.DeviceSecret))
                            {
                                p.AdditionalHeaders.Add(HeaderTypes.HttpDeviceAuthentication, $"BASIC {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{ApplicationContext.Current.Device.Name}:{ApplicationContext.Current.Device.DeviceSecret}"))}");
                            }
                        };

                        if (ApplicationServiceContext.Current.GetService <INetworkInformationService>().IsNetworkAvailable)
                        {
                            // Try OAUTH server
                            try
                            {
                                restClient.Description.Endpoint[0].Timeout = 5000;
                                restClient.Invoke <Object, Object>("PING", "/", null, null);

                                if (userName == ApplicationContext.Current.Configuration.GetSection <SecurityConfigurationSection>().DeviceName)
                                {
                                    restClient.Description.Endpoint[0].Timeout = restClient.Description.Endpoint[0].Timeout * 2;
                                }
                                else
                                {
                                    restClient.Description.Endpoint[0].Timeout = (int)(restClient.Description.Endpoint[0].Timeout * 0.6666f);
                                }

                                // GEt configuration
                                var configuration = this.GetConfigurationInfo();
                                if (configuration == null) // default action
                                {
                                    var oauthResponse = restClient.Post <OAuthTokenRequest, OAuthTokenResponse>("oauth2_token", "application/x-www-form-urlencoded", request);
                                    retVal = new TokenClaimsPrincipal(oauthResponse.AccessToken, oauthResponse.IdToken ?? oauthResponse.AccessToken, oauthResponse.TokenType, oauthResponse.RefreshToken, configuration);
                                }
                                else
                                {
                                    if (!configuration.GrantTypesSupported.Contains("password"))
                                    {
                                        throw new InvalidOperationException("Password grants not supported by this provider");
                                    }

                                    var oauthResponse = restClient.Post <OAuthTokenRequest, OAuthTokenResponse>(configuration.TokenEndpoint, "application/x-www-form-urlencoded", request);
                                    retVal = new TokenClaimsPrincipal(oauthResponse.AccessToken, oauthResponse.IdToken ?? oauthResponse.AccessToken, oauthResponse.TokenType, oauthResponse.RefreshToken, configuration);
                                }
                            }
                            catch (RestClientException <OAuthTokenResponse> ex) // there was an actual OAUTH problem
                            {
                                this.m_tracer.TraceError("REST client exception: {0}", ex.Message);
                                throw new SecurityException($"err_oauth_{ex.Result.Error}", ex);
                            }
                            catch (SecurityException ex)
                            {
                                this.m_tracer.TraceError("Server was contacted however the token is invalid: {0}", ex.Message);
                                throw;
                            }
                            catch (Exception ex) // All others, try local
                            {
                                this.m_tracer.TraceWarning("Original OAuth2 request failed trying local - Original Exception : {0}", ex);
                            }

                            if (retVal == null) // Some error occurred, use local
                            {
                                this.m_tracer.TraceWarning("Network unavailable, trying local");
                                try
                                {
                                    if (localIdp == null)
                                    {
                                        throw new SecurityException(Strings.err_offline_no_local_available);
                                    }

                                    retVal = localIdp.Authenticate(userName, password);
                                }
                                catch (Exception ex2)
                                {
                                    this.m_tracer.TraceError("Error falling back to local IDP: {0}", ex2);
                                    throw new SecurityException(String.Format(Strings.err_offline_use_cache_creds, ex2.Message), ex2);
                                }
                            }

                            // We have a match! Lets make sure we cache this data
                            // TODO: Clean this up
                            if (!(retVal is IOfflinePrincipal))
                            {
                                try
                                {
                                    ApplicationContext.Current.GetService <IThreadPoolService>().QueueUserWorkItem(o => this.SynchronizeSecurity(password, o as IPrincipal), retVal);
                                }
                                catch (Exception e2)
                                {
                                    this.m_tracer.TraceError("An error occurred when inserting the local credential: {0}", e2);
                                }
                            }
                        }
                        else
                        {
                            retVal = localIdp?.Authenticate(userName, password);
                        }
                    }
                }

                this.Authenticated?.Invoke(this, new AuthenticatedEventArgs(userName, retVal, true));
            }
            catch (Exception ex)
            {
                this.m_tracer.TraceError("OAUTH Error: {0}", ex.ToString());
                this.Authenticated?.Invoke(this, new AuthenticatedEventArgs(userName, retVal, false));
                throw new SecurityException($"Error establishing authentication session - {ex.Message}", ex);
            }

            return(retVal);
        }
예제 #3
0
        /// <summary>
        /// Challenge key authentication for password change only
        /// </summary>
        public IPrincipal Authenticate(string userName, Guid challengeKey, string response, string tfaSecret)
        {
            AuthenticatingEventArgs e = new AuthenticatingEventArgs(userName);

            this.Authenticating?.Invoke(this, e);
            if (e.Cancel)
            {
                this.m_tracer.TraceWarning("Pre-Event ordered cancel of auth {0}", userName);
                return(e.Principal);
            }

            var        localIdp = ApplicationContext.Current.GetService <IOfflineIdentityProviderService>();
            IPrincipal retVal   = null;

            // Get the scope being requested
            try
            {
                if (localIdp?.IsLocalUser(userName) == true)
                {
                    var localScIdp = ApplicationServiceContext.Current.GetService <IOfflineSecurityChallengeIdentityService>();
                    retVal = localScIdp.Authenticate(userName, challengeKey, response, tfaSecret);
                }
                else
                {
                    using (IRestClient restClient = ApplicationContext.Current.GetRestClient("acs"))
                    {
                        // Create grant information
                        OAuthTokenRequest request = new OAuthResetTokenRequest(userName, challengeKey.ToString(), response);

                        // Set credentials
                        if (ApplicationContext.Current.Configuration.GetSection <SecurityConfigurationSection>().DomainAuthentication == DomainClientAuthentication.Basic)
                        {
                            restClient.Credentials = new OAuthTokenServiceCredentials(null);
                        }
                        else
                        {
                            request.ClientId     = ApplicationContext.Current.Application.Name;
                            request.ClientSecret = ApplicationContext.Current.Application.ApplicationSecret;
                        }

                        try
                        {
                            restClient.Requesting += (o, p) =>
                            {
                                // Add device credential
                                if (!String.IsNullOrEmpty(ApplicationContext.Current.Device.DeviceSecret))
                                {
                                    p.AdditionalHeaders.Add(HeaderTypes.HttpDeviceAuthentication, $"BASIC {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{ApplicationContext.Current.Device.Name}:{ApplicationContext.Current.Device.DeviceSecret}"))}");
                                }

                                if (!String.IsNullOrEmpty(tfaSecret))
                                {
                                    p.AdditionalHeaders.Add(HeaderTypes.HttpTfaSecret, tfaSecret);
                                }
                            };

                            // Invoke
                            if (ApplicationServiceContext.Current.GetService <INetworkInformationService>().IsNetworkAvailable)
                            {
                                restClient.Description.Endpoint[0].Timeout = 5000;
                                restClient.Invoke <Object, Object>("PING", "/", null, null);
                                restClient.Description.Endpoint[0].Timeout = (int)(restClient.Description.Endpoint[0].Timeout * 0.6666f);

                                // Swap out the endpoint and authenticate
                                var configuration = this.GetConfigurationInfo();
                                if (configuration == null) // default action
                                {
                                    var oauthResponse = restClient.Post <OAuthTokenRequest, OAuthTokenResponse>("oauth2_token", "application/x-www-form-urlencoded", request);
                                    retVal = new TokenClaimsPrincipal(oauthResponse.AccessToken, oauthResponse.IdToken ?? oauthResponse.AccessToken, oauthResponse.TokenType, oauthResponse.RefreshToken, configuration);
                                }
                                else
                                {
                                    if (!configuration.GrantTypesSupported.Contains("password"))
                                    {
                                        throw new InvalidOperationException("Password grants not supported by this provider");
                                    }

                                    var oauthResponse = restClient.Post <OAuthTokenRequest, OAuthTokenResponse>(configuration.TokenEndpoint, "application/x-www-form-urlencoded", request);
                                    retVal = new TokenClaimsPrincipal(oauthResponse.AccessToken, oauthResponse.IdToken ?? oauthResponse.AccessToken, oauthResponse.TokenType, oauthResponse.RefreshToken, configuration);
                                }

                                return(retVal);
                            }
                            else
                            {
                                throw new InvalidOperationException("Cannot send reset credential while offline");
                            }
                        }
                        catch (RestClientException <OAuthTokenResponse> ex)
                        {
                            this.m_tracer.TraceError("REST client exception: {0}", ex.Message);
                            var se = new SecurityException(
                                String.Format("err_oauth2_{0}", ex.Result.Error),
                                ex
                                );
                            se.Data.Add("oauth_result", ex.Result);
                            throw se;
                        }
                        catch (SecurityException ex)
                        {
                            this.m_tracer.TraceError("Server was contacted however the token is invalid: {0}", ex.Message);
                            throw;
                        }
                        catch (Exception ex) // fallback to local
                        {
                            throw new SecurityException($"General authentication error occurred: {ex.Message}", ex);
                        }
                    }
                }

                this.Authenticated?.Invoke(this, new AuthenticatedEventArgs(userName, retVal, true));
            }
            catch (Exception ex)
            {
                this.m_tracer.TraceError("OAUTH Error: {0}", ex.ToString());
                this.Authenticated?.Invoke(this, new AuthenticatedEventArgs(userName, null, false));
                throw;
            }

            return(retVal);
        }
        /// <summary>
        /// Authenticate the specified device
        /// </summary>
        public IPrincipal Authenticate(string deviceId, string deviceSecret, AuthenticationMethod authMethod = AuthenticationMethod.Any)
        {
            AuthenticatingEventArgs e = new AuthenticatingEventArgs(deviceId);

            this.Authenticating?.Invoke(this, e);
            if (e.Cancel)
            {
                this.m_tracer.TraceWarning("Pre-Event ordered cancel of auth {0}", deviceId);
                return(e.Principal);
            }

            // Authenticate
            IPrincipal retVal = null;

            using (IRestClient restClient = ApplicationContext.Current.GetRestClient("acs"))
            {
                // Create grant information
                OAuthTokenRequest request = OAuthTokenRequest.CreateClientCredentialRequest(ApplicationContext.Current.Application.Name, ApplicationContext.Current.Application.ApplicationSecret, "*");

                try
                {
                    restClient.Requesting += (o, p) =>
                    {
                        // Add device credential
                        p.AdditionalHeaders.Add("X-Device-Authorization", $"BASIC {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{deviceId}:{deviceSecret}"))}");
                    };

                    // Invoke
                    if (authMethod.HasFlag(AuthenticationMethod.Online) &&
                        ApplicationServiceContext.Current.GetService <INetworkInformationService>().IsNetworkAvailable&&
                        ApplicationServiceContext.Current.GetService <IAdministrationIntegrationService>()?.IsAvailable() != false) // Network may be on but internet is not available
                    {
                        restClient.Description.Endpoint[0].Timeout = (int)(restClient.Description.Endpoint[0].Timeout * 0.333f);

                        var configuration = this.GetConfigurationInfo();
                        if (configuration == null)
                        {
                            OAuthTokenResponse response = restClient.Post <OAuthTokenRequest, OAuthTokenResponse>("oauth2_token", "application/x-www-form-urlencoded", request);
                            retVal = new TokenClaimsPrincipal(response.AccessToken, response.IdToken ?? response.AccessToken, response.TokenType, response.RefreshToken, configuration);
                        }
                        else
                        {
                            OAuthTokenResponse response = restClient.Post <OAuthTokenRequest, OAuthTokenResponse>(configuration.TokenEndpoint, "application/x-www-form-urlencoded", request);
                            retVal = new TokenClaimsPrincipal(response.AccessToken, response.IdToken ?? response.AccessToken, response.TokenType, response.RefreshToken, configuration);
                        }

                        // HACK: Set preferred sid to device SID
                        var cprincipal = retVal as IClaimsPrincipal;

                        var devId = cprincipal.FindFirst(SanteDBClaimTypes.SanteDBDeviceIdentifierClaim);
                        if (devId != null)
                        {
                            cprincipal.Identities.First().RemoveClaim(cprincipal.FindFirst(SanteDBClaimTypes.Sid));
                            cprincipal.Identities.First().AddClaim(new SanteDBClaim(SanteDBClaimTypes.Sid, devId.Value));
                        }

                        // Synchronize the security devices
                        try
                        {
                            this.SynchronizeSecurity(deviceSecret, deviceId, retVal);
                        }
                        catch (Exception e2)
                        {
                            this.m_tracer.TraceError("Error synchronizing the local device security: {0}", e2);
                        }
                        this.Authenticated?.Invoke(this, new AuthenticatedEventArgs(deviceId, retVal, true)
                        {
                            Principal = retVal
                        });
                    }
                    else if (authMethod.HasFlag(AuthenticationMethod.Local))
                    {
                        // Is this another device authenticating against me?
                        return(ApplicationContext.Current.GetService <IOfflineDeviceIdentityProviderService>().Authenticate(deviceId, deviceSecret));
                    }
                    else
                    {
                        throw new InvalidOperationException("Cannot determine authentication method");
                    }
                }

                catch (SecurityTokenException ex)
                {
                    this.m_tracer.TraceError("TOKEN exception: {0}", ex.Message);
                    throw new SecurityException(
                              String.Format("err_token_{0}", ex.Type),
                              ex
                              );
                }
                catch (SecurityException ex)
                {
                    this.m_tracer.TraceError("Server was contacted however the token is invalid: {0}", ex.Message);
                    throw;
                }
                catch (RestClientException <OAuthTokenResponse> ex)
                {
                    this.m_tracer.TraceError("REST client exception: {0}", ex.Message);

                    // Not network related, but a protocol level error
                    if (authMethod.HasFlag(AuthenticationMethod.Local) && deviceId != ApplicationContext.Current.Device.Name)
                    {
                        this.m_tracer.TraceWarning("Network unavailable falling back to local");
                        return(ApplicationContext.Current.GetService <IOfflineDeviceIdentityProviderService>().Authenticate(deviceId, deviceSecret));
                    }
                    else
                    {
                        this.m_tracer.TraceWarning("Original OAuth2 request failed: {0}", ex.Message);
                        throw new SecurityException(Strings.err_authentication_exception, ex);
                    }
                }
                catch (Exception ex) // Raw level web exception
                {
                    // Not network related, but a protocol level error
                    if (authMethod.HasFlag(AuthenticationMethod.Local) && deviceId != ApplicationContext.Current.Device.Name)
                    {
                        this.m_tracer.TraceWarning("Network unavailable falling back to local");
                        return(ApplicationContext.Current.GetService <IOfflineDeviceIdentityProviderService>().Authenticate(deviceId, deviceSecret));
                    }
                    else
                    {
                        this.m_tracer.TraceWarning("Original OAuth2 request failed: {0}", ex.Message);
                        throw new SecurityException(Strings.err_authentication_exception, ex);
                    }
                }

                return(retVal);
            }
        }