Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What OpenID solution is really used by Stack Overflow?

Tags:

oauth

openid

I know there are other questions about this, but they are outdated, and I cannot find the answer that would be reliable anywhere.

What is really used by Stack Overflow to autentificate users? The site DotNetOpenAuth claims it is. But to me the most (visually) similar looks to be OAuth C# Library.

So what does it really use? Or how can I mimic the same UI?

I want to create the very same OpenID authentication using ASP.NET MVC.

like image 673
Rasto Avatar asked Feb 26 '11 19:02

Rasto


People also ask

What is OpenID stackoverflow?

In OpenID we simply give a certificate symbolizing the person as legal and give access to whole database. Since stackoverflow or askubuntu belong to same domain they can exchange certificates with full access to user databases.

Is OpenID Connect dead?

Is OpenID Dead? Yes, OpenID is an obsolete standard that is no longer supported by the OpenID Foundation.

What is OAuth stack overflow?

OAuth is simply a secure authorization protocol that deals with the authorization of third-party applications to access the user data without exposing their password. (e.g. login with Facebook, gPlus, Twitter in many websites) all work under this protocol.

Which of the below mentioned companies support OpenID?

In mid-January 2008, Yahoo! announced initial OpenID 2.0 support, both as a provider and as a relying party, releasing the provider service by the end of the month. In early February, Google, IBM, Microsoft, VeriSign and Yahoo! joined the OpenID Foundation as corporate board members.


2 Answers

StackOverflow uses DotNetOpenAuth.

From the blog:

but fortunately we have an excellent dialog with Andrew Arnott, the primary author of the open source DotNetOpenAuth library we use

like image 76
Oded Avatar answered Sep 28 '22 10:09

Oded


I was able to get OpenID authentication with DotNetOpenAuth on my web site (www.mydevarmy.com) in a fairly short time (note that I am a total noob to ASP.NET, MVC, DotNetOpenAuth, etc).

DotNetOpenAuth comes with various samples and they even have an ASP.NET MVC sample, but they only provide a View and Controller in that sample and they don't actually have a Model which is the M in MVC :). Subsequently I asked the following question on SO:

What are the responsibilities of the components in an MVC pattern for a simple login

So how would a VERY SIMPLE OpenID login look like in MVC? Well, let's take a look...

1. You will need a Model:

public class User
{
    [DisplayName("User ID")]
    public int UserID{ get; set; }

    [Required]
    [DisplayName("OpenID")]
    public string OpenID { get; set; }
}

public class FormsAuthenticationService : IFormsAuthenticationService
{
    public void SignIn(string openID, bool createPersistentCookie)
    {
        if (String.IsNullOrEmpty(openID)) throw new ArgumentException("OpenID cannot be null or empty.", "OpenID");

        FormsAuthentication.SetAuthCookie(openID, createPersistentCookie);
    }

    public void SignOut()
    {
        FormsAuthentication.SignOut();
    }
}  

2. You will need a Controller:

[HandleError]
public class UserController : Controller
{
    private static OpenIdRelyingParty openid = new OpenIdRelyingParty();
    public IFormsAuthenticationService FormsService { get; set; }

    protected override void Initialize(RequestContext requestContext)
    {
        if (FormsService == null) 
        {
            FormsService = new FormsAuthenticationService(); 
        }

        base.Initialize(requestContext);
    }

    // **************************************
    // URL: /User/LogIn
    // **************************************
    public ActionResult LogIn()
    {
        if (User.Identity.IsAuthenticated)
        {
            return RedirectToAction("Profile", "User");
        }

        Identifier openID;
        if (Identifier.TryParse(Request.QueryString["dnoa.userSuppliedIdentifier"], out openID))
        {
            return LogIn(new User { OpenID = openID }, Request.QueryString["ReturnUrl"]);
        }
        else
        {
            return View();
        }
    }

    [HttpPost]
    public ActionResult LogIn(User model, string returnUrl)
    {
        string openID = ModelState.IsValid?model.OpenID:Request.Form["openid_identifier"];

        if (User.Identity.IsAuthenticated)
        {
            return RedirectToAction("Profile", "User");
        }
        else if (!string.IsNullOrEmpty(openID))
        {
            return Authenticate(openID, returnUrl);
        }
        else if(ModelState.IsValid)
        {
            ModelState.AddModelError("error", "The OpenID field is required.");
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

    // **************************************
    // URL: /User/LogOut
    // **************************************
    public ActionResult LogOut()
    {
        if (User.Identity.IsAuthenticated)
        {
            FormsService.SignOut();
        }

        return RedirectToAction("Index", "Home");
    }

    // **************************************
    // URL: /User/Profile
    // **************************************
    [Authorize]
    public ActionResult Profile(User model)
    {
        if (User.Identity.IsAuthenticated)
        {
            // ------- YOU CAN SKIP THIS SECTION ----------------
            model = /*some code to get the user from the repository*/;

            // If the user wasn't located in the database
            // then add the user to our database of users
            if (model == null)
            {
                model = RegisterNewUser(User.Identity.Name);
            }
            // --------------------------------------------------

            return View(model);
        }
        else
        {
            return RedirectToAction("LogIn");
        }
    }

    private User RegisterNewUser(string openID)
    {
        User user = new User{OpenID = openID};

        // Create a new user model

        // Submit the user to the database repository

        // Update the user model in order to get the UserID, 
        // which is automatically generated from the DB.
        // (you can use LINQ-to-SQL to map your model to the DB)

        return user;
    }

    [ValidateInput(false)]
    private ActionResult Authenticate(string openID, string returnUrl)
    {
        var response = openid.GetResponse();
        if (response == null)
        {
            // Stage 2: user submitting Identifier
            Identifier id;
            if (Identifier.TryParse(openID, out id))
            {
                try
                {
                    return openid.CreateRequest(openID).RedirectingResponse.AsActionResult();
                }
                catch (ProtocolException ex)
                {
                    ModelState.AddModelError("error", "Invalid OpenID.");

                    ModelState.AddModelError("error", ex.Message);
                    return View("LogIn");
                }
            }
            else
            {
                ModelState.AddModelError("error", "Invalid OpenID.");
                return View("LogIn");
            }
        }
        else
        {
            // Stage 3: OpenID Provider sending assertion response
            switch (response.Status)
            {
                case AuthenticationStatus.Authenticated:
                    Session["FriendlyIdentifier"] = response.FriendlyIdentifierForDisplay;
                    FormsAuthentication.SetAuthCookie(response.FriendlyIdentifierForDisplay, true);
                    if (!string.IsNullOrEmpty(returnUrl))
                    {
                        return Redirect(returnUrl);
                    }
                    else
                    {
                        return RedirectToAction("Profile", "User");
                    }
                case AuthenticationStatus.Canceled:
                    ModelState.AddModelError("error", "Authentication canceled at provider.");
                    return View("LogIn");
                case AuthenticationStatus.Failed:
                    ModelState.AddModelError("error", "Authentication failed: " + response.Exception.Message);
                    return View("LogIn");
            }
        }
        return new EmptyResult();
    }
}

3. You will need a view:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<YourProject.Models.User>" %>

<asp:Content ID="loginTitle" ContentPlaceHolderID="TitleContent" runat="server">
    Log in - YourWebSiteName
</asp:Content>
<asp:Content ID="loginContent" ContentPlaceHolderID="MainContent" runat="server">
        <p>
            <%--- If you have a domain, then you should sign up for an affiliate id with MyOpenID or something like that ---%>
            Please log in with your OpenID or <a href="https://www.myopenid.com/signup?affiliate_id=????">create an
                OpenID with myOpenID</a> if you don't have one.
        </p>
        <% 
        string returnURL = HttpUtility.UrlEncode(Request.QueryString["ReturnUrl"]);
        if (returnURL == null)
        {
            returnURL = string.Empty;
        }

        using (Html.BeginForm("LogIn", "User", returnURL))
        {%>
            <%= Html.LabelFor(m => m.OpenID)%>:
            <%= Html.TextBoxFor(m => m.OpenID)%>
            <input type="submit" value="Log in" />
        <% 
        } %>

        <%--- Display Errors ---%>
        <%= Html.ValidationSummary()%>
</asp:Content>

Note that I have not provided you with the Profile view, but that should be simple enough to figure out.

like image 45
Kiril Avatar answered Sep 28 '22 09:09

Kiril