views:

107

answers:

1

I'm working on a webapplication which runs central at a company. This webapplication needs to make a call to a service which is part of a second webapplication.

In the central webapplication i have this piece of code;

var clientUri = "http://website.localhost/Services/Info.svc/account";
var uri = new Uri(clientUri);
var networkCredentials = new NetworkCredential(_configuration.ServiceUserName, _configuration.ServicePassword);
var httpClient = new HttpClient();

httpClient.DefaultHeaders.Authorization = Credential.CreateBasic(_configuration.ServiceUserName,                                                                                   _configuration.ServicePassword);
httpClient.TransportSettings.PreAuthenticate = true;

HttpResponseMessage respone = httpClient.Get(uri);
HttpContent content = respone.Content;

In the webservice in the other application, which is (Info.svc), i have the following code in the Constructor of the service.

var validator = new UserNamePasswordValidator();
var cred = System.Net.CredentialCache.DefaultCredentials; //this one is empty, maybe a solution?
validator.Validate("Username", "Password");

if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
//This throws a 401 unauthorize, which is shown as a 500 error in the central application
throw new WebProtocolException(HttpStatusCode.Unauthorized, "You must be authorized to perform this action.", null);
}
else
{
_userIsAbleToUseService = true;
}

Instead of Username and Password in the validate function i want to use the Network Credentials sent over from the other webservice, can this be achieved? And how? Any other suggestions are welcome! I can harcode the password now in the validate function, but this isn't what i want.

--UPDATE-- This is the configuration in the web.config for the central application

<authorization>
            <allow roles="administrators"/>
            <deny roles="datareaders"/>
            <deny users="?"/>
        </authorization>

<authentication mode="Forms">
            <forms loginUrl="~/Logon/Logon" timeout="2880"/>
        </authentication>
        <membership>
            <providers>
                <clear/>
                <add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="ApplicationServices" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression="" applicationName="CentralApplication"/>
            </providers>
        </membership>

This part is for the web.config in the second webapplication

<authentication mode="Forms">
      <forms name="AllPages" loginUrl="~/Logon/" timeout="360" enableCrossAppRedirects="false" />
    </authentication>
    <authorization>
      <!-- NOTE: See Web.config under Private folder for specifics on secure pages. -->
      <deny users="?" />
    </authorization>

<membership defaultProvider="NHMembershipProvider">
      <providers>
        <clear />
        <add name="NHMembershipProvider" applicationName="Website" type="Website.Security.Authentication.Membership.CmsMembershipProvider" description="Stores and retrieves membership data from SQL server using Nhibernate" connectionStringName="NHibernate" enablePasswordRetrieval="true" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="true" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression="" />
      </providers>
    </membership>
    <roleManager enabled="true" defaultProvider="NHRoleProvider">
      <providers>
        <clear />
        <add name="NHRoleProvider" applicationName="Website" type="Website.Security.Authentication.Membership.CmsRoleProvider" />
      </providers>
    </roleManager>

Already being logged to the central application using the membership provider i'm trying to call the webservice from there using a simple hyperlink (just for now). The webservice called in the central admin gathers some data and sends this over to the url for the webservice of the second application. Both application are webapplications (containing webservices) so that is why the authentication mode is set to Forms.

A: 

Took me nearly 2 days, but now i got everything working as i want. I found the WcfRestContrib Which allowed me to implement authentication on the services by modifying my webconfig a bit and using a few attributes from this class. I build a quick testapplication with an service and a console application and the authentication worked.

I added the section below to my web.config;

<system.serviceModel>
    <extensions>
      <behaviorExtensions>
        <add name="webAuthentication" type="WcfRestContrib.ServiceModel.Configuration.WebAuthentication.ConfigurationBehaviorElement, WcfRestContrib, Version=1.0.6.107, Culture=neutral, PublicKeyToken=89183999a8dc93b5"/>
        <add name="errorHandler" type="WcfRestContrib.ServiceModel.Configuration.ErrorHandler.BehaviorElement, WcfRestContrib, Version=1.0.6.107, Culture=neutral, PublicKeyToken=89183999a8dc93b5"/>
        <add name="webErrorHandler" type="WcfRestContrib.ServiceModel.Configuration.WebErrorHandler.ConfigurationBehaviorElement, WcfRestContrib, Version=1.0.6.107, Culture=neutral, PublicKeyToken=89183999a8dc93b5"/>
      </behaviorExtensions>
    </extensions>
    <behaviors>
      <serviceBehaviors>
        <behavior name="Rest">
          <webAuthentication requireSecureTransport="false" authenticationHandlerType="WcfRestContrib.ServiceModel.Dispatcher.WebBasicAuthenticationHandler, WcfRestContrib" usernamePasswordValidatorType="CMS.Backend.Services.SecurityValidator, CMS.Backend" source="CMS.Backend"/>
          <!--<webAuthentication requireSecureTransport="false" authenticationHandlerType="CMS.Backend.Services.WebBasicAuthenticationHandler, CMS.Backend" usernamePasswordValidatorType="CMS.Backend.Services.SecurityValidator, Website.Backend" source="CMS.Backend"/>-->
          <errorHandler errorHandlerType="WcfRestContrib.ServiceModel.Web.WebErrorHandler, WcfRestContrib"/>
          <webErrorHandler returnRawException="true" logHandlerType="Website.Backend.Services.LogHandler, Website.Backend" unhandledErrorMessage="An error has occured processing your request. Please contact technical support for further assistance."/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
  </system.serviceModel>

After that i modified my service interface to look like this;

[ServiceContract]
public interface ICmsInfo
{
    [OperationContract]
    [OperationAuthentication]
    [WebInvoke(UriTemplate = "account", Method = "GET")]
    AccountDTO GetAccountInfo();

The final step i took was adding 2 attritubes to the service itself;

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceConfiguration("Rest", true)]
public class Cmsinfo : ICmsInfo
{
//foo
}

The WcfRestContrib helped me a lot by adding this authentication technique and it works great. I just had to make sure i used the authorization headers aswell.

After this i ran into a problem while authenticating the webservice in my webapplication which is using Forms Authentication. This problem is specified in another topic.

Rob