diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2009-11-10 21:00:37 -0800 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2009-11-10 21:00:37 -0800 |
commit | 6df46c9875f82f5a5f94a082a0b699fde2978d6f (patch) | |
tree | 56df88c1a4fe02a363e5a0d7d087c1947322ab09 /projecttemplates/WebFormsRelyingParty/Code | |
parent | 5d4d80b4a83bb9d6af62cc5f6d78f6f7e541e67d (diff) | |
download | DotNetOpenAuth-6df46c9875f82f5a5f94a082a0b699fde2978d6f.zip DotNetOpenAuth-6df46c9875f82f5a5f94a082a0b699fde2978d6f.tar.gz DotNetOpenAuth-6df46c9875f82f5a5f94a082a0b699fde2978d6f.tar.bz2 |
Added a bunch more OAuth SP supporting code, but it's not done yet.
Diffstat (limited to 'projecttemplates/WebFormsRelyingParty/Code')
3 files changed, 208 insertions, 1 deletions
diff --git a/projecttemplates/WebFormsRelyingParty/Code/OAuthServiceProvider.cs b/projecttemplates/WebFormsRelyingParty/Code/OAuthServiceProvider.cs new file mode 100644 index 0000000..473b6d2 --- /dev/null +++ b/projecttemplates/WebFormsRelyingParty/Code/OAuthServiceProvider.cs @@ -0,0 +1,117 @@ +//----------------------------------------------------------------------- +// <copyright file="OAuthServiceProvider.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace WebFormsRelyingParty.Code { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Web; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth; + using DotNetOpenAuth.OAuth.ChannelElements; + using DotNetOpenAuth.OAuth.Messages; + + public class OAuthServiceProvider { + private const string PendingAuthorizationRequestSessionKey = "PendingAuthorizationRequest"; + + /// <summary> + /// The shared service description for this web site. + /// </summary> + private static ServiceProviderDescription serviceDescription; + + private static OAuthServiceProviderTokenManager tokenManager; + + /// <summary> + /// The shared service provider object. + /// </summary> + private static ServiceProvider serviceProvider; + + /// <summary> + /// The lock to synchronize initialization of the <see cref="serviceProvider"/> field. + /// </summary> + private static object initializerLock = new object(); + + /// <summary> + /// Gets the service provider. + /// </summary> + /// <value>The service provider.</value> + public static ServiceProvider ServiceProvider { + get { + EnsureInitialized(); + return serviceProvider; + } + } + + /// <summary> + /// Gets the service description. + /// </summary> + /// <value>The service description.</value> + public static ServiceProviderDescription ServiceDescription { + get { + EnsureInitialized(); + return serviceDescription; + } + } + + public static UserAuthorizationRequest PendingAuthorizationRequest { + get { return HttpContext.Current.Session[PendingAuthorizationRequestSessionKey] as UserAuthorizationRequest; } + set { HttpContext.Current.Session[PendingAuthorizationRequestSessionKey] = value; } + } + + public static WebFormsRelyingParty.Consumer PendingAuthorizationConsumer { + get { + ITokenContainingMessage message = PendingAuthorizationRequest; + if (message == null) { + throw new InvalidOperationException(); + } + + return Global.DataContext.IssuedToken.OfType<IssuedRequestToken>().First(t => t.Token == message.Token).Consumer; + } + } + + public static void AuthorizePendingRequestToken() { + var pendingRequest = PendingAuthorizationRequest; + if (pendingRequest == null) { + throw new InvalidOperationException("No pending authorization request to authorize."); + } + + ITokenContainingMessage msg = pendingRequest; + var token = Global.DataContext.IssuedToken.OfType<IssuedRequestToken>().First(t => t.Token == msg.Token); + token.Authorize(); + + var response = serviceProvider.PrepareAuthorizationResponse(pendingRequest); + serviceProvider.Channel.Send(response); + PendingAuthorizationRequest = null; + } + + /// <summary> + /// Initializes the <see cref="serviceProvider"/> field if it has not yet been initialized. + /// </summary> + private static void EnsureInitialized() { + if (serviceProvider == null) { + lock (initializerLock) { + if (serviceDescription == null) { + var endpoint = new MessageReceivingEndpoint(Utilities.ApplicationRoot + "OAuth.ashx", HttpDeliveryMethods.PostRequest); + serviceDescription = new ServiceProviderDescription { + TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() }, + RequestTokenEndpoint = endpoint, + AccessTokenEndpoint = endpoint, + UserAuthorizationEndpoint = endpoint, + }; + } + + if (tokenManager == null) { + tokenManager = new OAuthServiceProviderTokenManager(); + } + + if (serviceProvider == null) { + serviceProvider = new ServiceProvider(serviceDescription, tokenManager); + } + } + } + } + } +} diff --git a/projecttemplates/WebFormsRelyingParty/Code/OAuthTokenManager.cs b/projecttemplates/WebFormsRelyingParty/Code/OAuthTokenManager.cs index 653d791..d9c04bd 100644 --- a/projecttemplates/WebFormsRelyingParty/Code/OAuthTokenManager.cs +++ b/projecttemplates/WebFormsRelyingParty/Code/OAuthTokenManager.cs @@ -75,18 +75,42 @@ namespace WebFormsRelyingParty.Code { Global.DataContext.SaveChanges(); } + /// <summary> + /// Deletes a request token and its associated secret and stores a new access token and secret. + /// </summary> + /// <param name="consumerKey">The Consumer that is exchanging its request token for an access token.</param> + /// <param name="requestToken">The Consumer's request token that should be deleted/expired.</param> + /// <param name="accessToken">The new access token that is being issued to the Consumer.</param> + /// <param name="accessTokenSecret">The secret associated with the newly issued access token.</param> + /// <remarks> + /// <para> + /// Any scope of granted privileges associated with the request token from the + /// original call to <see cref="StoreNewRequestToken"/> should be carried over + /// to the new Access Token. + /// </para> + /// <para> + /// To associate a user account with the new access token, + /// <see cref="System.Web.HttpContext.User">HttpContext.Current.User</see> may be + /// useful in an ASP.NET web application within the implementation of this method. + /// Alternatively you may store the access token here without associating with a user account, + /// and wait until <see cref="WebConsumer.ProcessUserAuthorization()"/> or + /// <see cref="DesktopConsumer.ProcessUserAuthorization(string, string)"/> return the access + /// token to associate the access token with a user account at that point. + /// </para> + /// </remarks> public void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret) { var requestTokenEntity = Global.DataContext.IssuedToken.OfType<IssuedRequestToken>().First( t => t.Consumer.ConsumerKey == consumerKey && t.Token == requestToken); - Global.DataContext.DeleteObject(requestTokenEntity); var accessTokenEntity = new IssuedAccessToken { Token = accessToken, TokenSecret = accessTokenSecret, ExpirationDate = null, // currently, our access tokens don't expire CreatedOn = DateTime.Now, + User = requestTokenEntity.User, }; + Global.DataContext.DeleteObject(requestTokenEntity); Global.DataContext.AddToIssuedToken(accessTokenEntity); Global.DataContext.SaveChanges(); } diff --git a/projecttemplates/WebFormsRelyingParty/Code/Utilities.cs b/projecttemplates/WebFormsRelyingParty/Code/Utilities.cs new file mode 100644 index 0000000..b9c9f43 --- /dev/null +++ b/projecttemplates/WebFormsRelyingParty/Code/Utilities.cs @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------- +// <copyright file="Utilities.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace WebFormsRelyingParty.Code { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Security.Cryptography; + using System.Web; + + public static class Utilities { + private static readonly RandomNumberGenerator CryptoRandomDataGenerator = new RNGCryptoServiceProvider(); + + public static string ApplicationRoot { + get { + string appRoot = HttpContext.Current.Request.ApplicationPath; + if (!appRoot.EndsWith("/", StringComparison.Ordinal)) { + appRoot += "/"; + } + + return appRoot; + } + } + + public static string SetCsrfCookie() { + // Generate an unpredictable secret that goes to the user agent and must come back + // with authorization to guarantee the user interacted with this page rather than + // being scripted by an evil Consumer. + byte[] randomData = new byte[8]; + CryptoRandomDataGenerator.GetBytes(randomData); + string secret = Convert.ToBase64String(randomData); + + // Send the secret down as a cookie... + var cookie = new HttpCookie("CsrfCookie", secret) { + Path = HttpContext.Current.Request.Path, + HttpOnly = true, + Expires = DateTime.Now.AddMinutes(30), + }; + HttpContext.Current.Response.SetCookie(cookie); + + // ...and also return the secret so the caller can save it as a hidden form field. + return secret; + } + + public static void VerifyCsrfCookie(string secret) { + var cookie = HttpContext.Current.Request.Cookies["CsrfCookie"]; + if (cookie != null) { + if (cookie.Value == secret) { + // Valid CSRF check. Clear the cookie and return. + cookie.Expires = DateTime.Now.Subtract(TimeSpan.FromDays(1)); + cookie.Value = string.Empty; + if (HttpContext.Current.Request.Browser["supportsEmptyStringInCookieValue"] == "false") { + cookie.Value = "NoCookie"; + } + HttpContext.Current.Response.SetCookie(cookie); + return; + } + } + + throw new InvalidOperationException("Invalid CSRF check."); + } + } +} |