Beveiligen van de OData webservice

Het heeft even geduurd, maar ik ben er eindelijk aan toe gekomen om een nieuwe blogpost te maken. Dit keer over het beveiligen van een OData webservice. Een aantal posts geleden heb ik het gehad over het maken van een OData webservice. Daarna hoe je een Windows Phone 7 app kan laten connecten met die webservice om data uit een database te halen en ook in weg te schrijven. Zeker bij dat laatste is het belangrijk dat, mocht dat wenselijk zijn, je je webservice kan beveiligen. Hierdoor kan je controleren wie wel en wie niet de data mag benaderen om te lezen en/of te schrijven.

Even op een rijtje, wat ik aan neem is het volgende:

  • Je hebt een database gemaakt
  • Deze database heb je gekoppeld aan een WCF Data Service via het Entity Framework
  • De WCF Data Service wordt gebruikt als end point voor de data in de database

Wat we nu willen gaan doen, is het zorgen dat we door middel van het aanroepen van een authenticatie methode de gebruiker kunnen inloggen en hierdoor meer rechten krijgen. Deze Authenticate methode maken we in een normale webservice. Het afvangen van de rechten van de ingelogde gebruiker doen we in een Intercept methode in de WCF Data Service.

Authenticate

Om een gebruiker in te kunnen loggen hebben we een methode nodig die we kunnen aanroepen om dit voor elkaar te krijgen. Hiervoor hebben we een normale webservice nodig waar we die methode in kunnen zetten. Maak in je webservice project een nieuwe Web Service aan. Deze kan je bijvoorbeeld Auth.asmx noemen.

In deze nieuwe normale webservice gaan we een methode aanmaken die we gaan gebruiken om de username en password te checken en de gebruiker te authenticeren. Ook maak ik een methode aan waarmee we kunnen checken of de gebruiker geauthenticeerd is of niet. Hiervoor gebruiken we de HttpContext.Current.User. Deze is van het type IPrincipal en dadelijk maken we een generieke GenericPrincipal aan die we hieraan koppelen. In deze principal zit de naam van de gebruiker en de rollen. Deze rollen kunnen we later weer gebruiken om te checken of de gebruiker iets wel of niet mag aan de hand van in welke rollen hij of zijn zit.

/// <summary>
/// Summary description for Auth
/// </summary>
[WebService(Namespace = "http://testwebservice.apphb.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class Auth : System.Web.Services.WebService
{
    [WebMethod]
    public bool Authenticate(string username, string password)
    {
        // to do: check username and password in database or something using Linq and the Entity Framework
        System.Security.Principal.GenericIdentity identity = new GenericIdentity(username, "Generic");
        System.Security.Principal.GenericPrincipal principal = new GenericPrincipal(identity, new string[] { "generic" });
        HttpContext.Current.User = principal;

        // setting the cookie is important to remember the credentials, it's mot persistant, so when the browser
        // is closed, the cookie is deleted
        FormsAuthentication.SetAuthCookie(username, false);

        return HttpContext.Current.User.Identity.IsAuthenticated;
    }

    [WebMethod]
    public bool IsAuthenticated()
    {
        if (HttpContext.Current.User != null)
        {
            return HttpContext.Current.User.Identity.IsAuthenticated;
        }
        else
        {
            return false;
        }
    }
}

In de bovenstaande methode wordt een test domain gezet. Deze kan je zelf zetten naar je eigen domein waar je webservice draait. In de code zie je de twee methodes. In de Authenticate methode check ik nog niet of de username en password correct zijn, dat mag je zelf doen :). De andere methode IsAuthenticated kan worden gebruikt om te checken of de huidige gebruiker geauthenticeerd is. Deze hoeft je niet perse te implementeren natuurlijk, maar kan wel handig zijn.

Vervolgens moeten we zorgen dat we zeker zijn dat we deze manier van authenticatie gebruiken. Als we FormsAuthentication gebruiken, dan kunnen we de authenticatie helemaal zelf regelen. Om dit af te dwingen moeten we dit specificeren in de Web.config. In de  <system.web> maken we een <authentication mode=”Forms” /> aan.

Bijvoorbeeld:

<system.web>
  <compilation debug="true" targetFramework="4.0">
    <assemblies>
      <add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    </assemblies>
  </compilation>
  <authentication mode="Forms" />
</system.web>

Nu hebben we voor elkaar dat we moeten authenticeren door middel van een HTTP POST naar deze webservice. Om te testen kan je de webservice lokaal draaien en dan krijg je meteen ook een Invoke optie om te testen. Let op, als je het via de app doet straks, dan moet het door middel van een HTTP POST.

Interceptors

De volgende stap is het afvangen van het aanroepen van de data. Hier willen we eerst weten of de gebruiker geauthenticeerd is en eventueel of deze in de juiste rol zit om bij de data te mogen.

De Interceptors komen in twee varianten; QueryInterceptors en ChangeInterceptors. De QueryInterceptors gebruiken we om queries af te vangen en de ChangeInterceptors voor alle veranderingen zoals inserts, updates en deletes. Nu hou ik het even bij een QueryInterceptor. Deze kan je als volgt implementeren.

[QueryInterceptor("Words")]
public Expression<Func<Words, bool>> OnQueryWords()
{
    if (HttpContext.Current.User.Identity.IsAuthenticated)
    {
        return w => w.Word == w.Word;
    }
    else
    {
        throw new AuthenticationException("The current user is not authorized to query this object.");
    }
}

Hier gebruik ik een voorbeeld met de entity Words. Op het moment dat ik nu mijn Data Service aanroep door bijvoorbeeld http://domein/service.svc/Words dan krijg ik geen toegang tot de data. Dit krijg ik alleen als ik eerst Authenticate() gebruik, waaarna ik als user bekend ben in de Context van de webserver. Daarna kan ik wel gewoon bij de data.

Het voorbeeld van beveiligen in deze post een hele simpele methode. Er zijn veel nettere methodes om te beveiligen. Deze maken bijvoorbeeld gebruiken van een eigen AuthenticationProvider en waarmee je kan laten checken op domainusers en eventueel automatische in een ASP.NET user database.

  1 comment for “Beveiligen van de OData webservice

  1. 9 augustus 2012 at 09:15

    Leuk stuk. Gevoelsmatig zou ik er toch een laagje tussen frunnikken dmv bijvoorbeeld een servicebus, zodat je ook user-context-data kan tonen. Ene gebruikert mag meer dan een ander. Tevens kun je dan eindpoints van de servicebus in een beveiligde omgeving (DMZ) plaatjes. Maar dat zijn weer keuzes, keuzes en keuzes… :)

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Verplichte velden zijn gemarkeerd met *

Zoals de meeste websites gebruiken we cookies om een meer persoonlijke en snelle service te bieden.

Wij gebruiken cookies zodat onze website meer efficiënt kan functioneren, om de prestaties te verbeteren en, eventueel, om op maat reclame van onze partners aan te bieden. Als u doorgaat gaan we ervan uit dat u akkoord gaat alle cookies te krijgen van onze website.

To accept cookies please Click To Continue