Dotnetopenauth single sign on with custom identity provider

后端 未结 1 1673
滥情空心
滥情空心 2021-02-02 18:07

I\'m trying to setup the DotNetOpenAuth samples to have a working SSO solution with a custom provider. I\'m using OpenIdProviderMvc sample project which appears to be working fi

相关标签:
1条回答
  • 2021-02-02 18:13

    Let us start :

    1- Open Visual Studio 2010 go to File > New > Project > Web > ASP.NET MVC 3 Application:

    enter image description here

    then Choose Internet Application be sure to have Razor as your View engine and Click Ok:

    enter image description here

    2- Download Assets folder , it contains DotNetOpenAuth dll and OpenID-Selector files that we will use ,

    Feel free if you want to go to these projects and discover them in more details.

    Extract it to the folder you want

      a - Add the DotNetOpenAuth.dll to references in your site.
    
      b- Delete all files/folders in Site Content folder.
    
      c- Copy Assets Content files/folders to the site Content .
    
      d- Copy the  Assets Script files to the site Script.
    

    .

    Your project will look like this :

    enter image description here

    3- Go to Views > Shared > _Layout.cshtml and replace the with this new head , we just added the new styles and scripts:

    <head>
        <title>@ViewBag.Title</title>
        <link href="@Url.Content("~/Content/Site.css")" 
         rel="stylesheet" type="text/css" />
        <script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")"
         type="text/javascript"></script>
        <link href="@Url.Content("~/Content/openid-shadow.css")"
         rel="stylesheet" type="text/css" />
        <link href="@Url.Content("~/Content/openid.css")" 
         rel="stylesheet" type="text/css" />
        <script src="@Url.Content("~/Scripts/openid-en.js")" 
         type="text/javascript"></script>
        <script src="@Url.Content("~/Scripts/openid-jquery.js")" 
         type="text/javascript"></script>
        <script type="text/javascript">
            $(document).ready(function () {
                openid.init('openid_identifier');
            });
        </script>
    </head>
    

    4- Go to Models > AccountModels.cs , navigate to public class LogOnModel

    and Add OpenID attribute that we will use it to hold the returned OpenID from OpenID-Selector

    your class will look like this:

    public class LogOnModel
    {
        [Display(Name = "OpenID")]
        public string OpenID { get; set; }
    
        [Required]
        [Display(Name = "User name")]
        public string UserName { get; set; }
    
        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }
    
        [Display(Name = "Remember me?")]
        public bool RememberMe { get; set; }
    }
    

    navigate to public class RegisterModel and Add OpenID attribute

    public class RegisterModel
    {
    
        [Display(Name = "OpenID")]
        public string OpenID { get; set; }
    
        [Required]
        [Display(Name = "User name")]
        public string UserName { get; set; }
    
        [Required]
        [DataType(DataType.EmailAddress)]
        [Display(Name = "Email address")]
        public string Email { get; set; }
    
        [Required]
        [ValidatePasswordLength]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }
    
        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage =
        "The password and confirmation password do not match.")]
        public string ConfirmPassword { get; set; }
    }
    

    Then go to Services section in AccountModels.cs

    and Modify the CreateUser and Add GetUser to get the user by OpenID , your Interface

    will looks like this:

    public interface IMembershipService
    {
        int MinPasswordLength { get; }
        bool ValidateUser(string userName, string password);
        MembershipCreateStatus CreateUser(string userName, string password,
                                          string email, string OpenID);
        bool ChangePassword(string userName, string oldPassword, string newPassword);
        MembershipUser GetUser(string OpenID);
    }
    

    Add these using to AccountModels.cs

    using System.Security.Cryptography;
    using System.Text;
    

    Then Add This Function to the AccountModels.cs , this function will be used to convert the OpenID to GUID

    Note: feel free to use better hashing to your system , MD5 had some collision issues.

    public Guid StringToGUID(string value)
    {
        // Create a new instance of the MD5CryptoServiceProvider object.
        MD5 md5Hasher = MD5.Create();
        // Convert the input string to a byte array and compute the hash.
        byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(value));
        return new Guid(data);
    }
    

    Also Modify The CreateUser Function to look like this:

    public MembershipCreateStatus CreateUser(string userName, string password, 
                                             string email , string OpenID)
    {
        if (String.IsNullOrEmpty(userName)) throw 
        new ArgumentException("Value cannot be null or empty.", "userName");
        if (String.IsNullOrEmpty(password)) throw 
        new ArgumentException("Value cannot be null or empty.", "password");
        if (String.IsNullOrEmpty(email)) throw
        new ArgumentException("Value cannot be null or empty.", "email");
    
        MembershipCreateStatus status;
        _provider.CreateUser(userName, password, email, null, null, true,
                                StringToGUID(OpenID), out status);
        return status;
    }
    

    Here we are using the MemberShip ProviderUserKey to store the OpenID and the trick here that we convert the OpenID string to GUID to be used by CreateUser and GetUser methods.

    Now let us add this function to AccountModels.cs that will get the user by OpenID:

    public MembershipUser GetUser(string OpenID)
    {
        return _provider.GetUser(StringToGUID(OpenID), true);
    }
    

    5- go to Views > Account > LogOn.cshtml

    replace all the markup with this one ,we are integrating OpenID-Selector to LogOn View:

    @model OpenIDMVC3.Models.LogOnModel
    @{
        ViewBag.Title = "Log On";
    }
    <h2>
        Log On</h2>
    <p>
        Please enter your username and password. @Html.ActionLink("Register", "Register")
        if you don't have an account.
    </p>
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript">
    </script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"
       type="text/javascript"></script>
    <form action=
    "Authenticate?ReturnUrl=@HttpUtility.UrlEncode(Request.QueryString["ReturnUrl"])"
     method="post" id="openid_form">
    <input type="hidden" name="action" value="verify" />
    <div>
        <fieldset>
            <legend>Login using OpenID</legend>
            <div class="openid_choice">
                <p>
                    Please click your account provider:</p>
                <div id="openid_btns">
                </div>
            </div>
            <div id="openid_input_area">
                @Html.TextBox("openid_identifier")
                <input type="submit" value="Log On" />
            </div>
            <noscript>
                <p>
                    OpenID is service that allows you to log-on to many different websites 
                    using a single indentity. Find out <a href="http://openid.net/what/">
                     more about OpenID</a>and <a href="http://openid.net/get/">
                     how to get an OpenID enabled account</a>.</p>
            </noscript>
            <div>
                @if (Model != null)
                {
                    if (String.IsNullOrEmpty(Model.UserName))
                    {
                    <div class="editor-label">
                        @Html.LabelFor(model => model.OpenID)
                    </div>
                    <div class="editor-field">
                        @Html.DisplayFor(model => model.OpenID)
                    </div>
                    <p class="button">
                        @Html.ActionLink("New User ,Register", "Register", 
                                             new { OpenID = Model.OpenID })
                    </p>
                    }
                    else
                    {
                        //user exist 
                    <p class="buttonGreen">
                        <a href="@Url.Action("Index", "Home")">Welcome , @Model.UserName, 
                        Continue..." </a>
                    </p>
    
                    }
                }
            </div>
        </fieldset>
    </div>
    </form>
    
    @Html.ValidationSummary(true, "Login was unsuccessful. Please correct the errors 
                                                        and try again.")
    @using (Html.BeginForm())
    {
        <div>
            <fieldset>
                <legend>Or Login Normally</legend>
                <div class="editor-label">
                    @Html.LabelFor(m => m.UserName)
                </div>
                <div class="editor-field">
                    @Html.TextBoxFor(m => m.UserName)
                    @Html.ValidationMessageFor(m => m.UserName)
                </div>
                <div class="editor-label">
                    @Html.LabelFor(m => m.Password)
                </div>
                <div class="editor-field">
                    @Html.PasswordFor(m => m.Password)
                    @Html.ValidationMessageFor(m => m.Password)
                </div>
                <div class="editor-label">
                    @Html.CheckBoxFor(m => m.RememberMe)
                    @Html.LabelFor(m => m.RememberMe)
                </div>
                <p>
                    <input type="submit" value="Log On" />
                </p>
            </fieldset>
        </div>
    }
    

    6- Now let us run the project , then click the [Log On] link , you will get like this page:

    ws

    7- Go to Controllers > AccountController.cs and Add these using:

    using DotNetOpenAuth.Messaging;
    using DotNetOpenAuth.OpenId;
    using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
    using DotNetOpenAuth.OpenId.RelyingParty;
    using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
    

    Then Add this Attribute to AccountController.cs:

    private static OpenIdRelyingParty openid = new OpenIdRelyingParty();
    

    Then Add this Function to AccountController.cs:

    [ValidateInput(false)]
    public ActionResult Authenticate(string returnUrl)
    {
        var response = openid.GetResponse();
        if (response == null)
        {
            //Let us submit the request to OpenID provider
            Identifier id;
            if (Identifier.TryParse(Request.Form["openid_identifier"], out id))
            {
                try
                {
                    var request = openid.CreateRequest(
                                            Request.Form["openid_identifier"]);
                    return request.RedirectingResponse.AsActionResult();
                }
                catch (ProtocolException ex)
                {
                    ViewBag.Message = ex.Message;
                    return View("LogOn");
                }
            }
    
            ViewBag.Message = "Invalid identifier";
            return View("LogOn");
        }
    
        //Let us check the response
        switch (response.Status)
        {
    
            case AuthenticationStatus.Authenticated:
                LogOnModel lm = new LogOnModel();
                lm.OpenID = response.ClaimedIdentifier;
                // check if user exist
                MembershipUser user = MembershipService.GetUser(lm.OpenID);
                if (user != null)
                {
                    lm.UserName = user.UserName;
                    FormsService.SignIn(user.UserName, false);
                }
    
                return View("LogOn", lm);
    
            case AuthenticationStatus.Canceled:
                ViewBag.Message = "Canceled at provider";
                return View("LogOn");
            case AuthenticationStatus.Failed:
                ViewBag.Message = response.Exception.Message;
                return View("LogOn");
        }
    
        return new EmptyResult();
    }
    

    8 - Now run the project click [Log On] link and click a provider like Google

    it may ask you to sign in or ask you to allow access to your information

    you will get a page like this :

    enter image description here

    As you can see it displays your OpenID and a button that indicate that this is a new user not registered yet,

    before hitting [New User ,Register] button we need to modify the Register view and controller to access OpenID information.

    9- Go to controllers > AccountController.cs replace the [ActionResult Register ()] by this :

    public ActionResult Register(string OpenID)
    {
        ViewBag.PasswordLength = MembershipService.MinPasswordLength;
        ViewBag.OpenID = OpenID;
        return View();
    }
    

    And Modify the [ActionResult Register(RegisterModel model)] to use OpenID when

    creating users:

    [HttpPost]
    public ActionResult Register(RegisterModel model)
    {
        if (ModelState.IsValid)
        {
            // Attempt to register the user
            MembershipCreateStatus createStatus =
            MembershipService.CreateUser(model.UserName, model.Password, 
                                            model.Email,model.OpenID);
    
            if (createStatus == MembershipCreateStatus.Success)
            {
                FormsService.SignIn(model.UserName, false);
                return RedirectToAction("Index", "Home");
            }
            else
            {
                ModelState.AddModelError("",
                AccountValidation.ErrorCodeToString(createStatus));
            }
        }
    
        // If we got this far, something failed, redisplay form
        ViewBag.PasswordLength = MembershipService.MinPasswordLength;
        return View(model);
    }
    

    10- Go to Views > Account > Register.cshtml , replace the markup by this :

    @model OpenIDMVC3.Models.RegisterModel
    @{
        ViewBag.Title = "Register";
    }
    
    <h2>Create a New Account</h2>
    <p>
        Use the form below to create a new account. 
    </p>
    <p>
        Passwords are required to be a minimum of @ViewBag.PasswordLength 
        characters in length.
    </p>
    
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" 
      type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"
       type="text/javascript"></script>
    
    @using (Html.BeginForm()) {
        @Html.ValidationSummary(true, "Account creation was unsuccessful.
                                                Please correct the errors and try again.")
        <div>
            <fieldset>
                <legend>Account Information</legend>
                @if (ViewData["OpenID"] != null)
                {
                <div class="editor-label">
                    @Html.Label("OpenID")
                </div>
                <div class="editor-label">
                    @Html.Label((string)ViewBag.OpenID)
                </div>
                }
                <div class="editor-label">
                    @Html.LabelFor(m => m.UserName)
                </div>
                <div class="editor-field">
                    @Html.TextBoxFor(m => m.UserName)
                    @Html.ValidationMessageFor(m => m.UserName)
                </div>
    
                <div class="editor-label">
                    @Html.LabelFor(m => m.Email)
                </div>
                <div class="editor-field">
                    @Html.TextBoxFor(m => m.Email)
                    @Html.ValidationMessageFor(m => m.Email)
                </div>
    
                <div class="editor-label">
                    @Html.LabelFor(m => m.Password)
                </div>
                <div class="editor-field">
                    @Html.PasswordFor(m => m.Password)
                    @Html.ValidationMessageFor(m => m.Password)
                </div>
    
                <div class="editor-label">
                    @Html.LabelFor(m => m.ConfirmPassword)
                </div>
                <div class="editor-field">
                    @Html.PasswordFor(m => m.ConfirmPassword)
                    @Html.ValidationMessageFor(m => m.ConfirmPassword)
                </div>
    
                <p>
                    <input type="submit" value="Register" />
                </p>
            </fieldset>
        </div>
    }
    

    11- Go to step 8 and let us hit [New User ,Register] button , you will get this :

    enter image description here

    12- Register any account you want you will get like this page :

    enter image description here

    13- Click [Log Off] and login again using the same OpenID , you will get like this page:

    enter image description here

    As you can see the welcome green button detect that this user is registered .

    14- Click the green button you will get a page like this :

    enter image description here

    Congratulation ! , now you had integrated OpenID to your project.

    Reference

    0 讨论(0)
提交回复
热议问题