WCF Authentication - TransportCredentialOnly with ASPNET Membership - asp.net

I feel like I'm close but I'm a newbie at WCF and can't figure out why this is not working. I've tried searching, but I couldn't find an example of using aspnet membership without using message level security. I am trying to authenticate over https from Android to a WCF service. It works just fine until I change the clientCredentialType from 'None' to 'Basic'. I have to authenticate via username and password. When I try to update my proxy by running slsvcutil.exe against the https://myPublicWebsite/ABCService/ABC.svc it gives the following error:
The authentication schemes configured on the host ('IntegratedWindowsAuthentication, Anonymous') do not allow those configured on the binding 'BasicHttpBinding' ('Basic'). Please ensure that the SecurityMode is set to Transport or TransportCredentialOnly. Additionally, this may be resolved by changing the authentication schemes for this application through the IIS management tool, through the ServiceHost.Authentication.AuthenticationSchemes property, in the application configuration file at the element, by updating the ClientCredentialType property on the binding, or by adjusting the AuthenticationScheme property on the HttpTransportBindingElement.
Here is my web.config of the service. Thanks for any help you can give me.
<system.web>
<compilation debug="false" strict="false" explicit="true" targetFramework="4.0" />
<customErrors mode="Off" />
<membership defaultProvider="AspNetSqlMembershipProvider" userIsOnlineTimeWindow="15">
<providers>
<remove name="AspNetSqlMembershipProvider" />
<clear />
<add
name="AspNetSqlMembershipProvider"
type="System.Web.Security.SqlMembershipProvider"
connectionStringName="LocalSqlServer"
applicationName="ABC"
enablePasswordRetrieval="false"
enablePasswordReset="false"
requiresQuestionAndAnswer="false"
minRequiredPasswordLength="8"
requiresUniqueEmail="true"
passwordFormat="Hashed" />
</providers>
</membership>
</system.web>
<system.serviceModel>
<services>
<service name="ABCService.ABC" behaviorConfiguration="metadataBehavior">
<endpoint
address=""
binding="basicHttpBinding"
bindingConfiguration="ABCBinding"
contract="ABCService.IService1"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="metadataBehavior">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceCredentials>
<userNameAuthentication
userNamePasswordValidationMode="MembershipProvider"
membershipProviderName="AspNetSqlMembershipProvider" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="ABCBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https"/>
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<directoryBrowse enabled="false"/>
</system.webServer>
Also, what do I put in the code of service to run the validation check? I have this:
Public Class MyCustomUserNameValidator
Inherits IdentityModel.Selectors.UserNamePasswordValidator
' This method validates users. It allows two users, test1 and test2
' with passwords 1tset and 2tset respectively.
' This code is for illustration purposes only and
' MUST NOT be used in a production environment because it is NOT secure.
Public Overrides Sub Validate(ByVal userName As String, ByVal password As String)
If Nothing = userName OrElse Nothing = password Then
Throw New ArgumentNullException()
End If
If Not (userName = "test1" AndAlso password = "1tset") AndAlso Not (userName = "test2" AndAlso password = "2tset") Then
Throw New IdentityModel.Tokens.SecurityTokenException("Unknown Username or Password")
End If
End Sub
End Class
But I don't really understand how it works because I never call it, and I would rather use a default one than a custom one. I'm sure this is simple, but all the examples I could find by searching are for 'custom' validators. Does this get called automatically? or do I even need it if I just want the default?

Are you using IIS? you need to install and enable basic authentication in IIS.
To install: Turn On or Off Windows Components, and enable Basic
Authentication under IIS.
To enable:
http://technet.microsoft.com/en-us/library/cc772009(v=ws.10).aspx
However, even if you enabled basic auth, you can only use membership providers and custom validators in message based security and when clientCredentialType set to UserName. Pure transport based security mode like TransportCredentialOnly and Transport will not work.
Reference - go to the Authentication section
This should work for wsHttpBinding
<security mode="Message">
<message clientCredentialType="UserName" />
</security>
Another possibility for wsHttpBinding and basicHttpBinding
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>

Related

Wrong connection strings returned

I've written a WCF service application.
When the project is generated, visual studio creates 3 web config files.
I've finished the project and to date I've been using a hard coded connection string within my GetOpenConnection() function, so I now want to move the connection string to the web.config files.
The following call returns null.
ConnectionStringSettings csSettings = ConfigurationManager.ConnectionStrings["PulseWcfConnectionString"];
When I run the following code it doesn't return the string set in my web.debug.config file.
for(int idx = 0; idx < ConfigurationManager.ConnectionStrings.Count; idx++)
Debug.WriteLine(ConfigurationManager.ConnectionStrings[idx].ConnectionString);
it returns the following 2 items, 2nd one is an empty string. I don't recognise the first line, maybe it's a default one?
data source=.\SQLEXPRESS;Integrated Security=SSPI; AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true
""
What am I missing please?
My web.debug.config contains the following which should be for a local sql server instance
<?xml version="1.0"?>
<!-- For more information on using web.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<connectionStrings>
<add
name="PulseWcfConnectionString"
connectionString="Data Source=WIN8-CLAIRE\SQLSRVDEV2008;Initial
Catalog=gcll;Persist Security Info=True;Integrated Security=True"
providerName="System.Data.SqlClient"
/>
</connectionStrings>
</configuration>
For now my web.release.config contains the same thing (it's being published to it's destination tomorrow so I'll change the details for it then)
<?xml version="1.0"?>
<!-- For more information on using web.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<connectionStrings>
<add
name="PulseWcfConnectionString"
connectionString="Data Source=WIN8-CLAIRE\SQLSRVDEV2008;Initial
Catalog=gcll;Persist Security Info=True;Integrated Security=True"
providerName="System.Data.SqlClient"
/>
</connectionStrings>
<system.web>
<compilation xdt:Transform="RemoveAttributes(debug)" />
</system.web>
</configuration>
web.config
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
<authentication mode="Windows" />
</system.web>
<system.serviceModel>
<services>
<service name ="pulse.smartcentre.wcf.service.app.PulseWebService"
behaviorConfiguration="ServiceBehavior">
<host>
<baseAddresses>
<add baseAddress = "http://localhost:52478/Design_Time_Addresses/pulse.smartcentre.wcf.service.app/PulseWebService/" />
</baseAddresses>
</host>
<endpoint address="" binding="wsHttpBinding"
bindingConfiguration="wsHttpBinding"
contract="pulse.smartcentre.wcf.service.app.IPulseWebService">
<!--
Upon deployment, the following identity element should be removed or replaced to reflect the
identity under which the deployed service runs. If removed, WCF will infer an appropriate identity
automatically.
-->
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<!-- Metadata Endpoints -->
<!-- The Metadata Exchange endpoint is used by the service to describe itself to clients. -->
<!-- This endpoint does not use a secure binding and should be secured or removed before deployment -->
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<!-- CNH -->
<bindings>
<!-- Secure binding (to use) -->
<wsHttpBinding>
<binding name="wsHttpBinding" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:10:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647"
messageEncoding="Text" textEncoding="utf-8"
useDefaultWebProxy="true" transactionFlow="true">
<readerQuotas
maxArrayLength="2147483647"
maxBytesPerRead="2147483647"
maxDepth="2147483647"
maxNameTableCharCount="2147483647"
maxStringContentLength="2147483647" />
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="Behaviors.EndpointBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
If you use the transformation files, you need to add the transformation property, and specify what you would like to do, Insert, Remove, Replace...
I use to put the local connection in the master web.config and then transform it in the Release configuration, by Replacing the attributes of the defined connection string.
Check this article: Web.config Transformation Syntax for Web Project Deployment Using Visual Studio
If you want to use your way, just add xdt:Transform="Insert" in the <add> node.
You can test your transformation using this web tester: Web.config Transformation Tester

Cannot access Elmah error log when using wcf

I've been trying to use ELMAH with WCF and I have added the "normal" ErrorHandler and ServiceErrorBehavior to a service. These work, I can see that when there is an exception the code in these runs and logs an error to ELMAH. So it should be fine but when I try to access the error log page elmah.axd I get HTTP 404 and WCF gives me the "Endpoint not found page".
Which means that the Elmah.ErrorLogPageFactory was not used on that url. So what it comes down to is my web.config (I think). I just can't get this right, here are the relevant parts of my web.config:
My service endpoints are setup like so:
<system.serviceModel>
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name="" helpEnabled="true"
automaticFormatSelectionEnabled="true" />
</webHttpEndpoint>
</standardEndpoints>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
<services>
<service name="SaraService.SaraService"
behaviorConfiguration="SaraSvcBehaviour" >
<endpoint name="SaraService"
address=""
binding="webHttpBinding" bindingConfiguration="webHttpBindingWithJsonP"
behaviorConfiguration="RestEndPointBehaviour"
contract="SaraService.ISaraService" />
<endpoint
address="soap"
behaviorConfiguration="SoapEndPoinstBehaviour"
binding="basicHttpBinding"
contract="SaraService.ISaraService" />
<endpoint
address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="SaraSvcBehaviour">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="RestEndPointBehaviour">
<webHttp />
</behavior>
<behavior name="SoapEndPoinstBehaviour">
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="webHttpBindingWithJsonP"
crossDomainScriptAccessEnabled="true">
</binding>
</webHttpBinding>
</bindings>
</system.serviceModel>
So there are endpoints in / and /soap urls.
And my ELMAH config is like this:
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<httpModules>
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />
<add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" />
<add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" />
</httpModules>
</system.web>
</location>
<system.webServer>
<handlers>
<add name="ELMAH" verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" preCondition="integratedMode" />
</handlers>
</system.webServer>
And I'm also running AutofacServiceHostFactory in the root url. Here is my global.asax.cs for the relevant parts:
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
var builder = new ContainerBuilder();
builder.RegisterType<SaraService>();
builder.RegisterType<MarkkinadataAccess.MarkkinadataEntities>().InstancePerLifetimeScope();
builder.RegisterType<GenerisDbConnectionSettings>().As<IGenerisDbConnectionSetting>().SingleInstance();
builder.RegisterType<GenerisApplication>().As<IGenerisApplication>().PropertiesAutowired().InstancePerLifetimeScope();
builder.RegisterType<CrmProductsQuery>().As<ICrmProductsQuery>();
builder.RegisterType <ProductsByNameQuery>().As<IProductsByNameQuery>();
builder.RegisterType<ProductMWQueryByName>().As<IProductMWQueryByName>();
builder.RegisterType<ProductMWQueryById>().As<IProductMWQueryById>();
AutofacHostFactory.Container = builder.Build();
RouteTable.Routes.Add(new ServiceRoute("", new AutofacServiceHostFactory(), typeof(SaraService)));
}
}
So my guess is that either running the service endpoint in root or the AutofacServiceHostFactory somehow prevents me accessing ELMAH log page in /elmah.axd url.
But I just don't understand why or how.
In any case, any help would be much appreciated.
Ok, I got this now (partially at least). The culprit is this line in my global.asax:
RouteTable.Routes.Add(new ServiceRoute("", new AutofacServiceHostFactory(), typeof(SaraService)));
So this will make the service endpoint to root which is what I wanted.
Also I just wanted a route (any route other than root) to access the ELMAH error log page. Which of course means that there has to be a route with a handler that runs the Elmah.ErrorLogPageFactory.
Now this should be simple but when I add my service to root url then it seems that I cannot access any route with a handler (axd) no matter what.
This just seems odd to me because I can create a file for example in www/index.html and that will be served just fine without touching the web.config at all. But in case of a "generated" route like elmah.axd or www/elmah.axd the service that I added in the root url will always get executed. Giving me the "no endpoint" page which is WCF pipelines HTTP 404.
In any case, if I put my service to some url other than root I can access ELMAH error log.
And yes, I did try ignoring elmah.axd url but it just gave me normal 404 error.
So I guess I'll just have to put my service to some place other than root unless somebody can come up with a better answer to do this.

Error 302 returned while using WCF for Forms Authentication and Accessing Authentication_JSON_AppService.axd

I have a client and application which are set up to use the ClientFormAuthenticationMembershipProvider for authentication. On the client side, when the application boots up the system prompts for a username and password. The server is set up to accept this, and a Web Application is supposed to permit the application to work. We are in the midst of setting up a new environment, and something is not working on the server side (if we point the client at another server, it works fine). We've painstakingly gone over every detail we can think of, and the result is the same: The call to ValidateUser() throws an exception. I have downloaded a network sniffer, and under the hood I can see that a 302 message is being returned when I try to call Authentication_JSON_AppService.axd. On the server side, ProcMon registers attempts to read the Authentication_JSON_AppService.axd file from within the wwwroot/../Authentication_JSON_AppService.axd, which obviously does not exist.
Client Side Configuration:
From our FormMain.cs (which attempts to call the provider)
if (!System.Web.Security.Membership.ValidateUser(null, null))
System.Windows.Forms.Application.Exit();
else
{
DoStartUp();
....
}
From Our FormLogin.cs (which prompts for UserName and Pass) Note: class inherits
IClientFormsAuthenticationCredentialsProvider
public System.Web.ClientServices.Providers.ClientFormsAuthenticationCredentials GetCredentials()
{
if (this.ShowDialog() == DialogResult.OK)
{
return new ClientFormsAuthenticationCredentials(
textEditUsername.Text, textEditPassword.Text,
false);
}
else
{
return null;
}
}
From the app.config:
<membership defaultProvider="ClientAuthenticationMembershipProvider">
<providers>
<clear />
<add name="ClientAuthenticationMembershipProvider" type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="http://SERVERNAME/APPNAME/Authentication_JSON_AppService.axd" credentialsProvider="APPNAME.Windows.FormLogin, APPNAME.Windows" savePasswordHashLocally="False" />
</providers>
</membership>
On the server side:
IIS has been configured for this application for anonymous authentication and forms authentication. (we're running IIS 7.5, .NET 4.0, Windows Server 2008 R2). We have the WCF Activation and HTTP Activation features installed on the server. The ApplicationPool is set to v4.0 Framework, 32-bit Applications not enabled, Integrated PipelineMode, most other values set to default.
The web.config file:
<system.web.extensions>
<scripting>
<webServices>
<authenticationService enabled="true" />
<roleService enabled="true" />
</webServices>
</scripting>
</system.web.extensions>
<authentication mode="Forms">
<forms name=".MDPSApp" loginUrl="~/Connect/Login.aspx" slidingExpiration="true" timeout="600000">
</forms>
</authentication>
<authorization>
<deny users="?" />
</authorization>
<membership defaultProvider="XYZ">
<providers>
<add name="XYZ" type="APPNAME.Web.Providers.MembershipProvider, APPNAME.Web" />
</providers>
</membership>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="DisableAutoCookieManagement" maxReceivedMessageSize="2147483647" allowCookies="false">
<readerQuotas maxDepth="64" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None" />
</binding>
</wsHttpBinding>
<behaviors>
<serviceBehaviors>
<behavior name="APPNAME.Application.Web.Services.AgenceMaster.ServiceAgenceMasterBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceAuthorization principalPermissionMode="None">
</serviceAuthorization>
</behavior>
<behavior name="APPNAME.Application.Web.Services.Agence.ServiceAgenceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="APPNAME.Application.Web.Services.AgenceMaster.ServiceAgenceMasterBehavior" name="APPNAME.Application.Web.Services.AgenceMaster.ServiceAgenceMaster">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="DisableAutoCookieManagement" contract="APPNAME.Services.AgenceMaster.IServiceAgenceMaster">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
<service behaviorConfiguration="APPNAME.Application.Web.Services.Agence.ServiceAgenceBehavior" name="APPNAME.Application.Web.Services.Agence.ServiceAgence">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="DisableAutoCookieManagement" contract="APPNAME.Services.Agence.IServiceAgence">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>
The APPNAME.Web.Providers.MembershipProvider class extends System.Web.Security.MembershipProvider and overrides ValidateUser(string username, string password) with custom code. This class is not getting instantiated or called during the scenario.
Something isn't configured properly on the server side, or else the server would know how to resolve the Authentication_JSON_AppService.axd call properly (and it seems to me like it's not). Any thoughts or help are appreciated!
This was driving me bonkers. In FireFox - if I entered the URL for my *.aspx page that invoked my WCF REST call - everything worked fine. If I then did a shift-reload - I would get a 302 which redirected me to a non-existent forms login page.
In Safari and Chrome - no shift-reloaded needed. It would fail with the 302 on the first load.
I found the basic answer in the Alex on ASP.NET blog
Short answer: change the web.config to remove forms authentication:
<modules runAllManagedModulesForAllRequests="true">
<remove name="FormsAuthentication" />
</modules>

"An error occurred while getting provider information from the database" while deploying Code First

I'm getting the following error while deploying my ASP.NET MVC 4 application (Code First) to IIS:
"An error occurred while getting provider information from the database.
This can be caused by Entity Framework using an incorrect connection string. Check the inner exceptions for details and ensure that the connection string is correct."
I've been looking around for answers, but none of the situations matches mine:
I got 2 projects
BackEnd (DataAccess with Code First + WCF services)
FrontEnd MVC 4 application (only service references to WCF services)
On my local IIS + SQL Server 2008 it works fine. Even if I change connectionstring to production database everything works as espected.
The problem occurs when deploying the BackEnd + FrontEnd to IIS.
The only connection string that accesses db is located in my BackEnd application:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appSettings>
<add key="fromMail" value="xxx#gmail.com" />
</appSettings>
<connectionStrings>
<add name="TTCWestelDbContext" connectionString="Data Source=192.168.2.14\SQLSERVER2008;Initial Catalog=TTCWestel001;Persist Security Info=True;User ID=XXX;Password=XXX" providerName="System.Data.SqlClient" />
</connectionStrings>
<system.web>
<compilation debug="true" targetFramework="4.0" />
<httpRuntime maxRequestLength="102400" executionTimeout="3600" />
</system.web>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="" maxReceivedMessageSize="2147483647">
<readerQuotas maxDepth="64" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true" />
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true" />
<dataContractSerializer maxItemsInObjectGraph="2147483646" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<directoryBrowse enabled="true" />
</system.webServer>
<system.diagnostics>
<sources>
<source name="System.ServiceModel"
switchValue="Information, ActivityTracing"
propagateActivity="true">
<listeners>
<add name="traceListener"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData= "c:\Temp\WCFTracesTTCWestel.svclog" />
</listeners>
</source>
</sources>
</system.diagnostics>
</configuration>
Any help appreciated!!
Thanks
Micclo

ASP.NET Membership Provider authentication not working authenticating WCF Service

I have a SqlMembershipProvider store with Roles enabled. This is configured and has the user "devtest" in the roles "xxUser" and "xxAdmin".
I also have a WCF service, which I want to authenticate and authorize against. My problem is that:
the authorisation is not
happening, code just executes
despite the policy attribute
I don't get any identity or security
context so do not know who is
calling the service
I need:
to know which user is calling the
method
some degree of rejecting
users if permissions don't match up
(ideally this should be performed
within the
RoleProvider/MembershipProvider/WCF
but can do it myself if I have to)
SSL in transport
I have my service contract set up thus:
[ServiceContract]
public interface ISupportService
{
[OperationContract]
[PrincipalPermission(SecurityAction.Demand, Role = "ThisRoleDoesNotExist")]
List<BaseInterestRate> GetAllBaseInterestRates();
}
the code is simple enough:
public class SupportService : ISupportService
{
public List<BaseInterestRate> GetAllBaseInterestRates()
{
OperationContext operationContext = OperationContext.Current;
ServiceSecurityContext serviceSecurityContext = ServiceSecurityContext.Current; // is always null
using (xxxEntities entities = new xxxEntities())
{
return new List<BaseInterestRate>(entities.BaseInterestRates);
}
}}
My service configuration is thus:
-->
<behaviors>
<serviceBehaviors>
<behavior name="SupportServiceBehavior">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
<serviceAuthorization principalPermissionMode="UseAspNetRoles" roleProviderName="AspNetSqlRoleProvider" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="MembershipProvider"
membershipProviderName="SqlMembershipProvider" />
</serviceCredentials>
</behavior>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
Having already configured the MembershipProvider:
<membership defaultProvider="SqlMembershipProvider" >
<providers>
<clear/>
<add name="SqlMembershipProvider"
connectionStringName="SqlMembershipProvider"
applicationName="xxx"
type="System.Web.Security.SqlMembershipProvider" />
</providers>
</membership>
<roleManager enabled="true">
<providers>
<clear />
<add connectionStringName="SqlMembershipProvider" applicationName="xxx"
name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" />
<add applicationName="xxx" name="AspNetWindowsTokenRoleProvider"
type="System.Web.Security.WindowsTokenRoleProvider" />
</providers>
</roleManager>
I have followed the instructions at these pages to the letter:
How to: Use the SQL Server Role Provider with Windows Authentication in WCF Calling from Windows Forms (MSDN)
How to: Create and Install Temporary Client Certificates in WCF During Development (MSDN)
How to: Use wsHttpBinding with Username Authentication and TransportWithMessageCredentials in WCF Calling from Windows Forms (MSDN)
Also quite useful found via SO: Use Asp.Net Membership provider with a WCF .svc service (Alkampfer's Place)
I would at lest expect an issue with certificates/transport/etc. to fail with exceptions, but I can debug right in and over the WCF call. I have no security context/ user context available to me and when I use a user not in the two mentioned roles (which I do in the code example above), I don't get "kicked out".
My client app is currently a Web App, but will ultimately also serve a Windows Forms app and Test suite. I'm currently using the ASP.NET WebDev server and am running .NET 4.0.
Am I missing something?
I'm a little new to WCF Rest services, but during my own testing I ran into a similar problem to this. I came across this video, which helped a bit (even if it wasn't quite what I was trying to do):
http://channel9.msdn.com/blogs/rojacobs/endpointtv-securing-restful-services-with-aspnet-membership
Essentially the problem was that under the asp.net configuration I had to disable anonymous access in order for it to use the MembershipProvider authentication:
system.web>
<authorization>
<deny users="?" />
</authorization>
...
I don't think you can set the principal permission on the interface.
I bet if you move it onto the service implementation method it will work
or at least start breaking for a different reason (I am currently stuck at that point - I get access denied exceptions - hopefully you dont!)
(I first tried to put them on the contract interface also)
this is the correct configuration for wcf service self-hosted with SSL:
<?xml version="1.0"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
<connectionStrings>
<add name="mySqlConnection" connectionString="Data Source=.\SQLEXPRESS2012;Integrated Security=SSPI;Initial Catalog=aspnetdb;"/>
</connectionStrings>
<system.web>
<compilation debug="true"/>
<!-- Configure the Sql Membership Provider -->
<membership defaultProvider="MySqlMembershipProvider" userIsOnlineTimeWindow="15">
<providers>
<clear/>
<add name="MySqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="mySqlConnection" applicationName="UsersManagementNavigationApplication" enablePasswordRetrieval="false" enablePasswordReset="false" requiresQuestionAndAnswer="false" requiresUniqueEmail="true" passwordFormat="Hashed"/>
</providers>
</membership>
<!-- Configure the Sql Role Provider -->
<roleManager enabled="true" defaultProvider="MySqlRoleProvider">
<providers>
<clear/>
<add name="MySqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="mySqlConnection" applicationName="UsersManagementNavigationApplication"/>
</providers>
</roleManager>
</system.web>
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="webBinding">
<security mode="Transport">
<transport clientCredentialType="Basic"/>
</security>
</binding>
</webHttpBinding>
<basicHttpBinding>
<binding name="basicBindingConfiguration">
<security mode="Transport">
<transport clientCredentialType="Basic"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="webEndpointBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="webServiceBehavior">
<serviceMetadata httpsGetEnabled="true"/>
<serviceThrottling/>
<serviceDebug/>
</behavior>
<behavior name="myServiceBehavior">
<!-- Configure role based authorization to use the Role Provider -->
<serviceAuthorization principalPermissionMode="UseAspNetRoles" roleProviderName="MySqlRoleProvider">
</serviceAuthorization>
<serviceCredentials>
<!-- Configure user name authentication to use the Membership Provider -->
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WcfServiceHTTPSSelfHosted.MyCustomValidator, WcfServiceHTTPSSelfHosted" />
</serviceCredentials>
<!-- To avoid disclosing metadata information, set the value below to false before deployment -->
<serviceMetadata httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="myServiceBehavior" name="WcfServiceHTTPSSelfHosted.WcfServiceHTTPSSelfHosted">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="basicBindingConfiguration" contract="WcfServiceHTTPSSelfHosted.IWcfServiceHTTPSSelfHosted"/>
<endpoint address="web" behaviorConfiguration="webEndpointBehavior" binding="webHttpBinding" bindingConfiguration="webBinding" contract="WcfServiceHTTPSSelfHosted.IWcfServiceHTTPSSelfHosted"/>
<endpoint address="mex" binding="mexHttpsBinding" bindingConfiguration="" contract="IMetadataExchange"/>
<host>
<baseAddresses>
<add baseAddress="https://localhost:50001/WcfServiceHTTPSSelfHosted/"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
if you want more info take a look this:
http://www.albertoschiassi.it/Home/tabid/55/EntryId/94/Use-ASP-NET-SqlMemberShipProvider-in-WCF-self-hosted-service.aspx
and
http://www.albertoschiassi.it/Home/tabid/55/EntryId/95/Use-ASP-NET-SqlMemberShipProvider-in-WCF-self-hosted-service-with-SSL.aspx

Resources