WCF RIA Security

Dernièrement, j’ai un peu joué avec les services WCF RIA. Une des premières questions que je me suis posés était : « OK, c’est bien. Mais comment on sécurise tout ça ? ».

Je vais expliquer les bases the l’authentification (déterminer qui est l’appelant) ou de l’autorisation (déterminer ce que l’appelant peut faire)

Je ne vais pas expliquer comment faire des services WCF RIA, il y a déjà assez de (bons) tutoriaux comme ça sur le net.

En fin d’article il y a un lien avec le code source de mon appli test.

Etape1 : Authentification

Serveur:

D’abord, nous ajoutons un Authentication Domain Service :

 

Nous devons maintenant implémenter les méthodes. Les deux qui nous intéressent sont : ValidateUser et GetAuthenticatedUser :

protected override bool ValidateUser(string userName, string password)
{
    // Obviously, it is quite basic user validation...

    if ((userName == "user") && (password == "user"))
        return true;

    if ((userName == "admin") && (password == "admin"))
        return true;

    return false;
}

// To enable Forms/Windows Authentication for the Web Application, edit the appropriate section of web.config file.
protected override User GetAuthenticatedUser(System.Security.Principal.IPrincipal principal)
{
    return new User { Name = principal.Identity.Name };
}
 

Il faut maintenant changer le web.config pour dire que nous voulons utiliser l’identification par formulaire (Note : si vous avez IIS, vous devez activer l’authentification anonyme et par formulaire. Voir la fin de l’article pour des liens à ce propos)


    

 

Vous pouvez maintenant ajouter l’attribut [RequiresAuthentication] à tous les services  (Dans DomainServices.cs) qui en ont besoin :

[RequiresAuthentication]
public IQueryable GetCustomers()
{
    return this.ObjectContext.Customers;
}

Client:

La partie serveur étant prête, il nous rester à créer un mécanisme de login sur le client.

Premièrement, on ajoute dans le constructeur App.cs la création d’un nouveau WebContext qui utilise Form Authentication :

public App()
{
    Startup += Application_Startup;
    Exit += Application_Exit;
    UnhandledException += Application_UnhandledException;

    InitializeComponent();


    // Create WebContext
    var webcontext = new WebContext();
    // We use form authentication
    webcontext.Authentication = new FormsAuthentication();
    // Add to ApplicationLifetimeObjects
    ApplicationLifetimeObjects.Add(webcontext);
}

 

Maintenant, il reste juste à appeler la méthode login du WebContext :

WebContext.Current.Authentication.Login(new LoginParameters(User, Password), lo =>
     {
         // set busy indicator
         IsBusy = false;
         RaisePropertyChanged("IsBusy");
    
         //Check if error
         if (lo.HasError)
         {
             // Show error, mark it handled and return
             MessageBox.Show(lo.Error.Message);
             lo.MarkErrorAsHandled();
             return;
         }
         // Check if login sucessful
         if (!lo.LoginSuccess)
             MessageBox.Show("User and/or password incorrect.");
    
         RaisePropertyChanged("IsLogged");
    
         
    
     }, null);

Et voila! Simple, mais efficace.

User non authentifié:

User authentifié:

Etape 2 : Autorisation

Authentifier un utilisateur est bien, mais pas assez. A moins que tous les utilisateurs aient les mêmes droits, il faut distinguer les rôles que possède un utilisateur. C’est le pourquoi de cette étape. Tout a lieu du côté serveur. Je ne vais pas créer un Role Provider custom, mais je vais utiliser le ASPRoleProvider.

Premièrement, il faut créer une nouvelle classe et la faire dérivés de RoleProvider. La seule méthode que nous devons implémenter est GetRolesForUser :

public class MyCustomRoleProvider : RoleProvider
{
    public override string[] GetRolesForUser(string username)
    {
        if (username == "user")
            return new[]{"users"};

        if (username == "admin")
            return new[] { "administrators" };

        return null;
    }

    #region Not Implemented
    //...
    #endregion
}

 

Deuxièmement, ajouter quelques lignes dans le web.config pour dire au service WCF que nous voulons utiliser notre MyCustomRoleProvider :


    
      
        
          
        
      
    
    


    
    
      
        
      
    

 

Maintenant, il nous reste a décorer nos services (dans DomainService.cs) avec l’attribut  [RequiresRole].

[RequiresRole("administrators")]
public IQueryable GetCustomers()
{
    return this.ObjectContext.Customers;
}

 

Utilisateur régulier :

Administrateur :

One more thing…

C’est bien, mais le problème est que les services WCF RIA utilisent HTTP. Donc, toutes les données (mot de passe inclus) passent en clair. Donc, pour être vraiment sécurisé, il faut passer à HTTPS.

Si nous remplaçons l’attribut [EnableClientAccess]  sur le  Domain et le  Authentication Service avec [EnableClientAccess(RequiresSecureEndpoint = true)], nous forçons WCF RIA à utiliser HTTPS. Il suffit juste (hum) de configurer IIS pour utiliser HTTPS.

Pour installer un certificat auto signé pour le développement :

http://weblogs.asp.net/scottgu/archive/2007/04/06/tip-trick-enabling-ssl-on-iis7-using-self-signed-certificates.aspx

http://www.robbagby.com/iis/self-signed-certificates-on-iis-7-the-easy-way-and-the-most-effective-way/

J’ai eu la (in)fameuse erreur “Not found error” lors du passage de Cassini à IIS. Quelques solutions :

http://todormihailov.com/2010/12/03/silverilght-with-net-4-0-wcf-ria-services-and-iis-error-notfound-%E2%80%93-a-deploy-nightmare/

http://blogs.objectsharp.com/cs/blogs/dan/archive/2010/04/13/wcf-ria-services-not-found-error-message.aspx

 

Sources

The following two tabs change content below.
Olivier

Olivier

Mobile Engineer chez Arcana Studio
Développeur Freelance. Passionné par le développement mobile et l'internet des objets. Expert en WinRT et Xamarin. MVP Windows Platform Development Nokia Developer Champion