Rectangle 27 12

I've recently created a NuGet package that adds auth with Facebook, Twitter, Google to any ASP.NET MVC 3 page - in a very simple and extensible manner - and it's based on DotNetOpenAuth. Take a look:

and the source is on github:

@GreyCloud some guys are working on an MVC4 branch, check it on GitHub

DotNetOpenAuth oAuth in ASP.NET MVC - Stack Overflow

asp.net-mvc oauth dotnetopenauth
Rectangle 27 12

Have a look at Rob Conery's TekPub ASP.Net MVC 2.0 Starter Site. He has setup DotNetOpenAuth as part of that. It should point you in the right direction

EDIT My assumption was based on the the fact the OpenID and OAuth are the same. They are not the same but complimentary ideas. OpenId is about Authentication where as OAuth is about Authorization.

While not directly related to ASP.NET MVC the TweetSharp Library has an OAuth implementation written in C# that may be of some use.

Thanks for the link Nathan, however it doesn't appear that Rob has any oAuth implementation in the Starter Site.

@nikmd23 I did make the assumtion that OpenId and OAuth are the same. My apologies if that is not the case. Have a look in the Controllers/SessionController Authenticate method and the Views/Session/Create.aspx file. The implementation that I was referring to is there.

Added a couple of links that may be of value

DotNetOpenAuth oAuth in ASP.NET MVC - Stack Overflow

asp.net-mvc oauth dotnetopenauth
Rectangle 27 1

Look at the Samples/OpenIdRelyingPartyMvc dir (a simple ASP.NET MVC website using OpenId authentication). You can start from copying Home/User Controllers/Views and settings from web.config into your project. I think it is the fastest way to give your site OpenId authentication. Then, as Alexander Prokofyev said, there is very useful post at the Andrew Arnott's (DotNetOpenAuth/DotNetOpenId author) blog - Add OpenID login support to your ASP.NET MVC site

dotnetopenauth - Request for Tutorial to add Openid Support to NerdDin...

asp.net-mvc dotnetopenauth nerddinner
Rectangle 27 5

I found your question when I was searching for solution to a similar problem. I solved it by making 2 new classes, which you can read about in this coderwall post.

I'll also copy and paste the full post here:

DotNetOpenAuth.AspNet 401 Unauthorized Error and Persistent Access Token Secret Fix

When designing QuietThyme, our Cloud Ebook Manager, we knew that everyone hates creating new accounts just as much as we do. We started looking for OAuth and OpenId libraries that we could leverage to allow for social login. We ended up using the DotNetOpenAuth.AspNet library for user authentication, because it supports Microsoft, Twitter, Facebook, LinkedIn and Yahoo, and many others right out of the bow. While we had some issues setting it all up, in the end we only needed to do a few small customizations to get most of it working (described in a previous coderwall post). We noticed that, unlike all the others, the LinkedIn client would not authenticate, returning a 401 Unauthorized Error from DotNetOpenAuth. It quickly became apparent that this was due to a signature issue, and after looking at the source we were able to determine that the retrieved AccessToken secret is not being used with the authenticated profile info request.

It acutally makes sense, the reason that OAuthClient class doesn't include the retrieved access token secret is that it's normally not needed for authentication purposes, which is the primary purpose of the ASP.NET OAuth library.

We needed to make authenticated requests against the api, after the user has logged in, to retrieve some standard profile information, including email address and full name. We were able to solve this issue by making use of an InMemoryOAuthTokenManager temporarily.

public class LinkedInCustomClient : OAuthClient
{
    private static XDocument LoadXDocumentFromStream(Stream stream)
    {
        var settings = new XmlReaderSettings
        {
            MaxCharactersInDocument = 65536L
        };
        return XDocument.Load(XmlReader.Create(stream, settings));
    }

    /// Describes the OAuth service provider endpoints for LinkedIn.
    private static readonly ServiceProviderDescription LinkedInServiceDescription =
            new ServiceProviderDescription
            {
                AccessTokenEndpoint =
                        new MessageReceivingEndpoint("https://api.linkedin.com/uas/oauth/accessToken",
                        HttpDeliveryMethods.PostRequest),
                RequestTokenEndpoint =
                        new MessageReceivingEndpoint("https://api.linkedin.com/uas/oauth/requestToken?scope=r_basicprofile+r_emailaddress",
                        HttpDeliveryMethods.PostRequest),
                UserAuthorizationEndpoint =
                        new MessageReceivingEndpoint("https://www.linkedin.com/uas/oauth/authorize",
                        HttpDeliveryMethods.PostRequest),
                TamperProtectionElements =
                        new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
                //ProtocolVersion = ProtocolVersion.V10a
            };

    private string ConsumerKey { get; set; }
    private string ConsumerSecret { get; set; }

    public LinkedInCustomClient(string consumerKey, string consumerSecret)
        : this(consumerKey, consumerSecret, new AuthenticationOnlyCookieOAuthTokenManager()) { }

    public LinkedInCustomClient(string consumerKey, string consumerSecret, IOAuthTokenManager tokenManager)
        : base("linkedIn", LinkedInServiceDescription, new SimpleConsumerTokenManager(consumerKey, consumerSecret, tokenManager))
    {
        ConsumerKey = consumerKey;
        ConsumerSecret = consumerSecret;
    }

    //public LinkedInCustomClient(string consumerKey, string consumerSecret) :
    //    base("linkedIn", LinkedInServiceDescription, consumerKey, consumerSecret) { }

    /// Check if authentication succeeded after user is redirected back from the service provider.
    /// The response token returned from service provider authentication result. 
    [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
        Justification = "We don't care if the request fails.")]
    protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
    {
        // See here for Field Selectors API http://developer.linkedin.com/docs/DOC-1014
        const string profileRequestUrl =
            "https://api.linkedin.com/v1/people/~:(id,first-name,last-name,headline,industry,summary,email-address)";

        string accessToken = response.AccessToken;

        var profileEndpoint =
            new MessageReceivingEndpoint(profileRequestUrl, HttpDeliveryMethods.GetRequest);

        try
        {
            InMemoryOAuthTokenManager imoatm = new InMemoryOAuthTokenManager(ConsumerKey, ConsumerSecret);
            imoatm.ExpireRequestTokenAndStoreNewAccessToken(String.Empty, String.Empty, accessToken, (response as ITokenSecretContainingMessage).TokenSecret);
            WebConsumer w = new WebConsumer(LinkedInServiceDescription, imoatm);

            HttpWebRequest request = w.PrepareAuthorizedRequest(profileEndpoint, accessToken);

            using (WebResponse profileResponse = request.GetResponse())
            {
                using (Stream responseStream = profileResponse.GetResponseStream())
                {
                    XDocument document = LoadXDocumentFromStream(responseStream);
                    string userId = document.Root.Element("id").Value;

                    string firstName = document.Root.Element("first-name").Value;
                    string lastName = document.Root.Element("last-name").Value;
                    string userName = firstName + " " + lastName;

                    string email = String.Empty;
                    try
                    {
                        email = document.Root.Element("email-address").Value;
                    }
                    catch(Exception)
                    {
                    }

                    var extraData = new Dictionary<string, string>();
                    extraData.Add("accesstoken", accessToken);
                    extraData.Add("name", userName);
                    extraData.AddDataIfNotEmpty(document, "headline");
                    extraData.AddDataIfNotEmpty(document, "summary");
                    extraData.AddDataIfNotEmpty(document, "industry");

                    if(!String.IsNullOrEmpty(email))
                    {
                        extraData.Add("email",email);
                    }

                    return new AuthenticationResult(
                        isSuccessful: true, provider: this.ProviderName, providerUserId: userId, userName: userName, extraData: extraData);
                }
            }
        }
        catch (Exception exception)
        {
            return new AuthenticationResult(exception);
        }
    }
}

Here's the section that has changed from the base LinkedIn client written by Microsoft.

InMemoryOAuthTokenManager imoatm = new InMemoryOAuthTokenManager(ConsumerKey, ConsumerSecret);
imoatm.ExpireRequestTokenAndStoreNewAccessToken(String.Empty, String.Empty, accessToken, (response as ITokenSecretContainingMessage).TokenSecret);
WebConsumer w = new WebConsumer(LinkedInServiceDescription, imoatm);

HttpWebRequest request = w.PrepareAuthorizedRequest(profileEndpoint, accessToken);

Unfortunately, the IOAuthTOkenManger.ReplaceRequestTokenWithAccessToken(..) method does not get executed until after the VerifyAuthentication() method returns, so we instead have to create a new TokenManager and and create a WebConsumer and HttpWebRequest using the AccessToken credentials we just retrieved.

Now what happens if you would like to persist the AccessToken credentials after the authentication process? This could be useful for a DropBox client for instance, where you would like to sync files to a user's DropBox asyncronously. The issue goes back to the way the AspNet library was written, it was assumed that DotNetOpenAuth would only be used for user authethentication, not as a basis for futher OAuth api calls. Thankfully the fix was fairly simple, all I had to do was modify the base AuthetnicationOnlyCookieOAuthTokenManger so that the ReplaceRequestTokenWithAccessToken(..) method stored the new AccessToken key and secrets.

/// <summary>
/// Stores OAuth tokens in the current request's cookie
/// </summary>
public class PersistentCookieOAuthTokenManagerCustom : AuthenticationOnlyCookieOAuthTokenManager
{
    /// <summary>
    /// Key used for token cookie
    /// </summary>
    private const string TokenCookieKey = "OAuthTokenSecret";

    /// <summary>
    /// Primary request context.
    /// </summary>
    private readonly HttpContextBase primaryContext;

    /// <summary>
    /// Initializes a new instance of the <see cref="AuthenticationOnlyCookieOAuthTokenManager"/> class.
    /// </summary>
    public PersistentCookieOAuthTokenManagerCustom() : base()
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="AuthenticationOnlyCookieOAuthTokenManager"/> class.
    /// </summary>
    /// <param name="context">The current request context.</param>
    public PersistentCookieOAuthTokenManagerCustom(HttpContextBase context) : base(context)
    {
        this.primaryContext = context;
    }

    /// <summary>
    /// Gets the effective HttpContext object to use.
    /// </summary>
    private HttpContextBase Context
    {
        get
        {
            return this.primaryContext ?? new HttpContextWrapper(HttpContext.Current);
        }
    }


    /// <summary>
    /// Replaces the request token with access token.
    /// </summary>
    /// <param name="requestToken">The request token.</param>
    /// <param name="accessToken">The access token.</param>
    /// <param name="accessTokenSecret">The access token secret.</param>
    public new void ReplaceRequestTokenWithAccessToken(string requestToken, string accessToken, string accessTokenSecret)
    {
        //remove old requestToken Cookie
        //var cookie = new HttpCookie(TokenCookieKey)
        //{
        //    Value = string.Empty,
        //    Expires = DateTime.UtcNow.AddDays(-5)
        //};
        //this.Context.Response.Cookies.Set(cookie);

        //Add new AccessToken + secret Cookie
        StoreRequestToken(accessToken, accessTokenSecret);

    }

}

Then to use this PersistentCookieOAuthTokenManager all you need to do is modify your DropboxClient constructor, or any other client where you would like to persist the AccessToken Secret

public DropBoxCustomClient(string consumerKey, string consumerSecret)
        : this(consumerKey, consumerSecret, new PersistentCookieOAuthTokenManager()) { }

    public DropBoxCustomClient(string consumerKey, string consumerSecret, IOAuthTokenManager tokenManager)
        : base("dropBox", DropBoxServiceDescription, new SimpleConsumerTokenManager(consumerKey, consumerSecret, tokenManager))
    {}

I ended up solving this by not using the stuff built into ASP.NET and going back to straight DNOA, but I like this approach too.

asp.net mvc 4 - Custom OAuth client in MVC4 / DotNetOpenAuth - missing...

asp.net-mvc-4 dotnetopenauth
Rectangle 27 0

Look at the Samples/OpenIdRelyingPartyMvc dir (a simple ASP.NET MVC website using OpenId authentication). You can start from copying Home/User Controllers/Views and settings from web.config into your project. I think it is the fastest way to give your site OpenId authentication. Then, as Alexander Prokofyev said, there is very useful post at the Andrew Arnott's (DotNetOpenAuth/DotNetOpenId author) blog - Add OpenID login support to your ASP.NET MVC site

dotnetopenauth - Request for Tutorial to add Openid Support to NerdDin...

asp.net-mvc dotnetopenauth nerddinner
Rectangle 27 0

DotNetOpenAuth does not store access or refresh tokens, but relies on the app to do that. Since you already do, adding DotNetOpenAuth should be fairly easy. You may need to implement a fairly simple IAuthorizationState interface when you pass your refresh token into DotNetOpenAuth, but that would be all.

c# - DotNetOpenAuth and Refresh Tokens - Stack Overflow

c# asp.net-mvc-4 dotnetopenauth
Rectangle 27 0

I don't know how to make it more clear than this. And if you like the NerdDinner sample, it's available as open source as well, so check out what they do.

Thank you, Andrew. This mixed with the information I found here weblogs.asp.net/haithamkhedre/archive/2011/03/13/ helped me put it all together. Thanks for the guidance.

asp.net mvc - DotNetOpenAuth Simple Demo, MVC & Razor - Stack Overflow

asp.net-mvc openid dotnetopenauth membership
Rectangle 27 0

I've recently created a NuGet package that adds auth with Facebook, Twitter, Google to any ASP.NET MVC 3 page - in a very simple and extensible manner - and it's based on DotNetOpenAuth. Take a look:

and the source is on github:

@GreyCloud some guys are working on an MVC4 branch, check it on GitHub

DotNetOpenAuth oAuth in ASP.NET MVC - Stack Overflow

asp.net-mvc oauth dotnetopenauth
Rectangle 27 0

For the DotNetOpenAuth MVC examples, the Open Id Identifier url to use is http://localhost:4864/User/Identity (where OpenIdProviderMvc is configured to run on port 4864 on localhost)

c# - DotNetOpenAuth: How to implement a simple OpenId provider? - Stac...

c# .net dotnetopenauth