diff --git a/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs b/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs index 2879b4a..3cf891a 100755 --- a/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs +++ b/OwinOAuthProvidersDemo/App_Start/Startup.Auth.cs @@ -126,14 +126,50 @@ namespace OwinOAuthProvidersDemo // clientId: "", // clientSecret: ""); - //in scenarios where a sandbox URL needs to be used - //var salesforceOptions = new SalesforceAuthenticationOptions + // Salesforce Option 1: don't specify explicit Endpoint config and use Production endpoint defaults + //var salesforceOptions1 = new SalesforceAuthenticationOptions + //{ + // ClientId = "", + // ClientSecret = "", + // Provider = new SalesforceAuthenticationProvider() + // { + // OnAuthenticated = async context => + // { + // System.Diagnostics.Debug.WriteLine(context.AccessToken); + // System.Diagnostics.Debug.WriteLine(context.RefreshToken); + // System.Diagnostics.Debug.WriteLine(context.OrganizationId); + // } + // } + //}; + + // Salesforce Option 2: ask for Sandbox environment; no need to know what those endpoints are + //var salesforceOptions2 = new SalesforceAuthenticationOptions //{ // Endpoints = // new SalesforceAuthenticationOptions.SalesforceAuthenticationEndpoints // { - // AuthorizationEndpoint = - // "https://ap1.salesforce.com/services/oauth2/authorize", + // Environment = Owin.Security.Providers.Salesforce.Constants.SandboxEnvironment + // }, + // ClientId = "", + // ClientSecret = "", + // Provider = new SalesforceAuthenticationProvider() + // { + // OnAuthenticated = async context => + // { + // System.Diagnostics.Debug.WriteLine(context.AccessToken); + // System.Diagnostics.Debug.WriteLine(context.RefreshToken); + // System.Diagnostics.Debug.WriteLine(context.OrganizationId); + // } + // } + //}; + + // Salesforce Option 3: explicitly specify endpoints (will take precedence over Environment choice) + //var salesforceOptions3 = new SalesforceAuthenticationOptions + //{ + // Endpoints = + // new SalesforceAuthenticationOptions.SalesforceAuthenticationEndpoints + // { + // AuthorizationEndpoint = "https://ap1.salesforce.com/services/oauth2/authorize", // TokenEndpoint = "https://ap1.salesforce.com/services/oauth2/token" // }, // ClientId = "", @@ -148,7 +184,7 @@ namespace OwinOAuthProvidersDemo // } // } //}; - //app.UseSalesforceAuthentication(salesforceOptions); + //app.UseSalesforceAuthentication(salesforceOptions1); ////app.UseShopifyAuthentication("", ""); diff --git a/OwinOAuthProvidersDemo/Controllers/AccountController.cs b/OwinOAuthProvidersDemo/Controllers/AccountController.cs index 83a640f..bc417b5 100644 --- a/OwinOAuthProvidersDemo/Controllers/AccountController.cs +++ b/OwinOAuthProvidersDemo/Controllers/AccountController.cs @@ -419,6 +419,13 @@ namespace OwinOAuthProvidersDemo.Controllers properties.Dictionary[ShopNameKey] = ShopName; } + // if use Salesforce as OAuth provider you can ask for Sandbox auth endpoint + // for this particular request only + //properties.Dictionary.Add( + // Owin.Security.Providers.Salesforce.Constants.EnvironmentAuthenticationProperty, + // Owin.Security.Providers.Salesforce.Constants.SandboxEnvironment + // ); + context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider); } } diff --git a/src/Owin.Security.Providers.Salesforce/Constants.cs b/src/Owin.Security.Providers.Salesforce/Constants.cs index 80a5208..c98249b 100644 --- a/src/Owin.Security.Providers.Salesforce/Constants.cs +++ b/src/Owin.Security.Providers.Salesforce/Constants.cs @@ -1,7 +1,11 @@ namespace Owin.Security.Providers.Salesforce { - internal static class Constants + public static class Constants { public const string DefaultAuthenticationType = "Salesforce"; + + public const string EnvironmentAuthenticationProperty = "Environment"; + public const string ProductionEnvironment = "Production"; + public const string SandboxEnvironment = "Sandbox"; } } \ No newline at end of file diff --git a/src/Owin.Security.Providers.Salesforce/SalesforceAuthenticationHandler.cs b/src/Owin.Security.Providers.Salesforce/SalesforceAuthenticationHandler.cs index 27074d8..db72d5e 100644 --- a/src/Owin.Security.Providers.Salesforce/SalesforceAuthenticationHandler.cs +++ b/src/Owin.Security.Providers.Salesforce/SalesforceAuthenticationHandler.cs @@ -18,6 +18,12 @@ namespace Owin.Security.Providers.Salesforce { private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string"; + private const string ProductionHost = "https://login.salesforce.com"; + private const string SandboxHost = "https://test.salesforce.com"; + + private const string AuthorizationEndpoint = "/services/oauth2/authorize"; + private const string TokenEndpoint = "/services/oauth2/token"; + private readonly ILogger _logger; private readonly HttpClient _httpClient; @@ -74,7 +80,7 @@ namespace Owin.Security.Providers.Salesforce }; // Request the token - var requestMessage = new HttpRequestMessage(HttpMethod.Post, Options.Endpoints.TokenEndpoint); + var requestMessage = new HttpRequestMessage(HttpMethod.Post, ComposeTokenEndpoint(properties)); requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); requestMessage.Content = new FormUrlEncodedContent(body); var tokenResponse = await _httpClient.SendAsync(requestMessage); @@ -186,8 +192,10 @@ namespace Owin.Security.Providers.Salesforce var state = Options.StateDataFormat.Protect(properties); - var authorizationEndpoint = - $"{Options.Endpoints.AuthorizationEndpoint}?response_type={"code"}&client_id={Options.ClientId}&redirect_uri={HttpUtility.UrlEncode(redirectUri)}&display={"page"}&immediate={false}&state={Uri.EscapeDataString(state)}"; + var authorizationEndpoint = ComposeAuthorizationEndpoint(properties); + + authorizationEndpoint = + $"{authorizationEndpoint}?response_type={"code"}&client_id={Options.ClientId}&redirect_uri={HttpUtility.UrlEncode(redirectUri)}&display={"page"}&immediate={false}&state={Uri.EscapeDataString(state)}"; if (Options.Scope != null && Options.Scope.Count > 0) { @@ -253,5 +261,66 @@ namespace Owin.Security.Providers.Salesforce return context.IsRequestCompleted; } + + private string ComposeAuthorizationEndpoint(AuthenticationProperties properties) { + string endpointPath = AuthorizationEndpoint; + + string endpoint = + !String.IsNullOrEmpty(Options.Endpoints.AuthorizationEndpoint) ? + Options.Endpoints.AuthorizationEndpoint : + ComposeEndpoint(properties, endpointPath); + + // if AuthenticationProperties for this session specifies an environment property + // it should take precedence over the value in AuthenticationOptions + string environmentProperty = null; ; + if (properties.Dictionary.TryGetValue(Constants.EnvironmentAuthenticationProperty, out environmentProperty)) { + endpoint = + environmentProperty == Constants.SandboxEnvironment ? + SandboxHost + endpointPath : + ProductionHost + endpointPath; + } + + return endpoint; + } + + private string ComposeTokenEndpoint(AuthenticationProperties properties) { + string endpointPath = TokenEndpoint; + + string endpoint = + !String.IsNullOrEmpty(Options.Endpoints.TokenEndpoint) ? + Options.Endpoints.TokenEndpoint : + ComposeEndpoint(properties, endpointPath); + + // if AuthenticationProperties for this session specifies an environment property + // it should take precedence over the value in AuthenticationOptions + string environmentProperty = null; ; + if (properties.Dictionary.TryGetValue(Constants.EnvironmentAuthenticationProperty, out environmentProperty)) { + endpoint = + environmentProperty == Constants.SandboxEnvironment ? + SandboxHost + endpointPath : + ProductionHost + endpointPath; + } + + return endpoint; + } + + private string ComposeEndpoint(AuthenticationProperties properties, string endpointPath) { + string endpoint = + !String.IsNullOrEmpty(Options.Endpoints.Environment) && Options.Endpoints.Environment == Constants.SandboxEnvironment ? + SandboxHost + endpointPath : + ProductionHost + endpointPath; + + // if AuthenticationProperties for this session specifies an environment property + // it should take precedence over the value in AuthenticationOptions + string environmentProperty = null; ; + if (properties.Dictionary.TryGetValue(Constants.EnvironmentAuthenticationProperty, out environmentProperty)) { + endpoint = + environmentProperty == Constants.SandboxEnvironment ? + SandboxHost + endpointPath : + ProductionHost + endpointPath; + } + + return endpoint; + } } } diff --git a/src/Owin.Security.Providers.Salesforce/SalesforceAuthenticationOptions.cs b/src/Owin.Security.Providers.Salesforce/SalesforceAuthenticationOptions.cs index 4957236..97a71ce 100644 --- a/src/Owin.Security.Providers.Salesforce/SalesforceAuthenticationOptions.cs +++ b/src/Owin.Security.Providers.Salesforce/SalesforceAuthenticationOptions.cs @@ -19,10 +19,12 @@ namespace Owin.Security.Providers.Salesforce /// Endpoint which is used to exchange code for access token /// public string TokenEndpoint { get; set; } - } - private const string AuthorizationEndPoint = ""; - private const string TokenEndpoint = ""; + /// + /// Production or Sandbox. Use Constants.ProductionEnvironment or Constants.SandboxEnvironment + /// + public string Environment { get; set; } + } /// /// Gets or sets the a pinned certificate validator to use to validate the endpoints used @@ -79,8 +81,8 @@ namespace Owin.Security.Providers.Salesforce public string ClientSecret { get; set; } /// - /// Gets the sets of OAuth endpoints used to authenticate against Salesforce. Overriding these endpoints allows you to use Salesforce Enterprise for - /// authentication. + /// Gets the sets of OAuth endpoints used to authenticate against Salesforce. + /// Overriding these endpoints allows you to use Salesforce Enterprise for authentication. /// public SalesforceAuthenticationEndpoints Endpoints { get; set; } @@ -127,8 +129,8 @@ namespace Owin.Security.Providers.Salesforce BackchannelTimeout = TimeSpan.FromSeconds(60); Endpoints = new SalesforceAuthenticationEndpoints { - AuthorizationEndpoint = AuthorizationEndPoint, - TokenEndpoint = TokenEndpoint + AuthorizationEndpoint = null, + TokenEndpoint = null }; } }