ページ

Sunday, July 18, 2010

Authentication in silverlight 4

Here is a preparation for Very Very Verrrrry simple Login System for Silverlight.


I. Server Side ( XXXXX.Web project )

1. Membership Provider class

namespace XXXXX.Web.Providers
{
    public class CustomMembershipProvider : MembershipProvider
    {
        public override string ApplicationName { set; get; }
        public override MembershipUser GetUser(string username, bool userIsOnline)
        {
            return new MembershipUser("CustomMembershipProvider",
                username,
                null,
                null,
                null,
                null,
                false,
                false,
                new DateTime(),
                new DateTime(),
                new DateTime(),
                new DateTime(),
                new DateTime());
        }

        public override bool ValidateUser(string username, string password)
        {
            if(username == "admin")
                return password == "password";
            if(username == "user")
                return password == "password";
            return false;
        }

        public override bool EnablePasswordReset { get { throw new NotImplementedException(); } }

        public override bool EnablePasswordRetrieval { get { throw new NotImplementedException(); } }

        public override bool RequiresQuestionAndAnswer { get { throw new NotImplementedException(); } }

        public override int MaxInvalidPasswordAttempts { get { throw new NotImplementedException(); } }

        public override int PasswordAttemptWindow { get { throw new NotImplementedException(); } }

        public override bool RequiresUniqueEmail { get { throw new NotImplementedException(); } }

        public override MembershipPasswordFormat PasswordFormat { get { throw new NotImplementedException(); } }

        public override int MinRequiredPasswordLength { get { throw new NotImplementedException(); } }

        public override int MinRequiredNonAlphanumericCharacters { get { throw new NotImplementedException(); } }

        public override string PasswordStrengthRegularExpression { get { throw new NotImplementedException(); } }

        public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) { throw new NotImplementedException(); }

        public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) { throw new NotImplementedException(); }

        public override string GetPassword(string username, string answer) { throw new NotImplementedException(); }

        public override bool ChangePassword(string username, string oldPassword, string newPassword) { throw new NotImplementedException(); }

        public override string ResetPassword(string username, string answer) { throw new NotImplementedException(); }

        public override void UpdateUser(MembershipUser user) { throw new NotImplementedException(); }

        public override bool UnlockUser(string userName) { throw new NotImplementedException(); }

        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline) { throw new NotImplementedException(); }

        public override string GetUserNameByEmail(string email) { throw new NotImplementedException(); }

        public override bool DeleteUser(string username, bool deleteAllRelatedData) { throw new NotImplementedException(); }

        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords) { throw new NotImplementedException(); }

        public override int GetNumberOfUsersOnline() { throw new NotImplementedException(); }

        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) { throw new NotImplementedException(); }

        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) { throw new NotImplementedException(); }
    }
}


2. Profile Provider class

namespace XXXXX.Web.Providers
{
    public class CustomProfileProvider : ProfileProvider
    {
        public override string ApplicationName { set; get; }

        public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
        {
            var username = context["UserName"];
            var result = new SettingsPropertyValueCollection();

            result.Add(new SettingsPropertyValue(
                new SettingsProperty(
                    "WelcomeMessage",
                    typeof(string),
                    null,
                    true,
                    string.Format("ようこそ {0}さん", username),
                    SettingsSerializeAs.String,
                    null,
                    false,
                    false)));
            return result;
        }

        public override int DeleteProfiles(ProfileInfoCollection profiles) { throw new NotImplementedException(); }

        public override int DeleteProfiles(string[] usernames) { throw new NotImplementedException(); }

        public override int DeleteInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate) { throw new NotImplementedException(); }

        public override int GetNumberOfInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate) { throw new NotImplementedException(); }

        public override ProfileInfoCollection GetAllProfiles(ProfileAuthenticationOption authenticationOption, int pageIndex, int pageSize, out int totalRecords) { throw new NotImplementedException(); }

        public override ProfileInfoCollection GetAllInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords) { throw new NotImplementedException(); }

        public override ProfileInfoCollection FindProfilesByUserName(ProfileAuthenticationOption authenticationOption, string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) { throw new NotImplementedException(); }

        public override ProfileInfoCollection FindInactiveProfilesByUserName(ProfileAuthenticationOption authenticationOption, string usernameToMatch, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords) { throw new NotImplementedException(); }

        public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection) { throw new NotImplementedException(); }
    }
}


3. Role Provider class

namespace XXXXX.Web.Providers
{
    public class CustomRoleProvider : RoleProvider
    {
        public override string ApplicationName { set; get; }

        public override string[] GetRolesForUser(string username)
        {
            if (username == "admin")
                return new[] { "Administrators", "Users" };
            if (username == "user")
                return new[] { "Users" };
            return null;
        }

        public override bool IsUserInRole(string username, string roleName)
        {
            if(username == "admin")
                return roleName == "Administrators" || roleName == "Users";
            if(username == "user")
                return roleName == "Users";
            return false;
        }

        public override void CreateRole(string roleName) { throw new NotImplementedException(); }

        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole) { throw new NotImplementedException(); }

        public override bool RoleExists(string roleName) { throw new NotImplementedException(); }

        public override void AddUsersToRoles(string[] usernames, string[] roleNames) { throw new NotImplementedException(); }

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames) { throw new NotImplementedException(); }

        public override string[] GetUsersInRole(string roleName) { throw new NotImplementedException(); }

        public override string[] GetAllRoles() { throw new NotImplementedException(); }

        public override string[] FindUsersInRole(string roleName, string usernameToMatch) { throw new NotImplementedException(); }
    }
}


4. User class

namespace XXXXX.Web.Providers
{
    public class User : UserBase
    {
        public string WelcomeMessage { set; get; }
    }
}


5. Authentication Domain Service class

    [EnableClientAccess]
    public class AuthenticationService : AuthenticationBase<User> { }


6. Web.Config

  <system.web>
    <authentication mode="Forms" />
    <membership defaultProvider="customProvider">
      <providers>
        <clear />
        <add name="customProvider" type="XXXXX.Web.Providers.CustomMembershipProvider"/>
      </providers>
    </membership>
    <profile enabled="true" defaultProvider="customProvider">
      <properties>
        <add name="WelcomeMessage" allowAnonymous="false"/>
      </properties>
      <providers>
        <clear />
        <add name="customProvider" type="XXXXX.Web.Providers.CustomProfileProvider"/>
      </providers>
    </profile>
    <roleManager enabled="true" defaultProvider="customProvider">
      <providers>
        <clear />
        <add name="customProvider" type="XXXXX.Web.Providers.CustomRoleProvider"/>
      </providers>
    </roleManager>
  </system.web>



II. Client side ( XXXXX project )

7. App.xaml.cs

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

            InitializeComponent();

            WebContext webcontext = new WebContext();
            webcontext.Authentication = new FormsAuthentication();
            this.ApplicationLifetimeObjects.Add(webcontext);
        }

        private void Application_Startup(object sender, StartupEventArgs e)
        {
            this.Resources.Add("WebContext", WebContext.Current);
            //this.RootVisual = new MainPage();
            this.RootVisual = new BasePage();
        }


Here is the pages for the simple login system.

8. BasePage.xaml

<UserControl x:Class="XXXXX.BasePage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

</UserControl>


9.BasePage.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace XXXXX
{
    using XXXXX.Web;

    public partial class BasePage : UserControl
    {
        public BasePage()
        {
            InitializeComponent();
            if (this.Content == null)
            {
                this.Content = new LoginPage();
            }
            else
            {
                var user = WebContext.Current.User;
                if (user.IsAuthenticated)
                    ((BasePage)Parent).ShowMainPage();
                else
                    ((BasePage)Parent).ShowLoginPage();
            }
        }

        public void ShowLoginPage()
        {
            this.Content = new LoginPage();
        }

        public void ShowMainPage()
        {
            this.Content = new MainPage();
        }
    }
}



** [IMPORTANT] **

10. MainPage.xaml.cs

add following code in the constructor.

var user = WebContext.Current.User;
if (!user.IsAuthenticated)
    ((BasePage)Parent).ShowLoginPage();




I know that using "business application template" is easier than the above way, but I hope it would be more customizable.

Change UserAgent on UIWebView ( Undocumented method )

I was looking for how to change the User Agent on UIWebView for iPhone/iPad.
then I found the solution... but it's undocumented method, which means it would be quite hard to get the approval from app store.


UIWebView+UserAgent.h

@interface UIWebView(UserAgent)
    -(void)setUserAgent:(NSString *)userAgent;
@end


UIWebView+UserAgent.m

#import "UIWebView+UserAgent.h"
#import <objc/runtime.h>

@implementation UIWebView(UserAgent)
-(void)setUserAgent:(NSString *)userAgent
{
    id webDocumentView;
    id newWebView;
    webDocumentView = objc_msgSend(self, @selector(_documentView));
    object_getInstanceVariable(webDocumentView, "_webView", (void**)&newWebView);
    objc_msgSend(newWebView, @selector(setCustomUserAgent:),userAgent);
}
@end


This would make the log analysis simpler by distinguishing precisely between Safari and the application on iPhone/iPad.
Plus, Providing some special services only for the application users on website is available by use of the UserAgent.