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 | |
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.
15 files changed, 485 insertions, 7 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."); + } + } +} diff --git a/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx b/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx new file mode 100644 index 0000000..720c4b2 --- /dev/null +++ b/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx @@ -0,0 +1,20 @@ +<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" + CodeBehind="OAuthAuthorize.aspx.cs" Inherits="WebFormsRelyingParty.Members.OAuthAuthorize" %> + +<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server"> +</asp:Content> +<asp:Content ID="Content2" ContentPlaceHolderID="Body" runat="server"> + <h2> + Client authorization</h2> + <p> + The + <asp:Label ID="consumerNameLabel" runat="server" Text="(app name)" /> + application is requesting to access the private data in your account here. Is that + alright with you? + </p> + <asp:Button ID="yesButton" runat="server" Text="Yes" + onclick="yesButton_Click" /> + <asp:Button ID="noButton" runat="server" Text="No" + onclick="noButton_Click" /> + <asp:HiddenField runat="server" ID="csrfCheck" EnableViewState="false" /> +</asp:Content> diff --git a/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx.cs b/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx.cs new file mode 100644 index 0000000..4ffb9b8 --- /dev/null +++ b/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx.cs @@ -0,0 +1,41 @@ +//----------------------------------------------------------------------- +// <copyright file="OAuthAuthorize.aspx.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace WebFormsRelyingParty.Members { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Web; + using System.Web.UI; + using System.Web.UI.WebControls; + using DotNetOpenAuth.OAuth; + using DotNetOpenAuth.OAuth.Messages; + using WebFormsRelyingParty.Code; + + public partial class OAuthAuthorize : System.Web.UI.Page { + protected void Page_Load(object sender, EventArgs e) { + if (!IsPostBack) { + if (OAuthServiceProvider.PendingAuthorizationRequest == null) { + Response.Redirect("~/"); + } + + this.csrfCheck.Value = Utilities.SetCsrfCookie(); + this.consumerNameLabel.Text = HttpUtility.HtmlEncode(OAuthServiceProvider.PendingAuthorizationConsumer.Name); + } else { + Utilities.VerifyCsrfCookie(this.csrfCheck.Value); + } + } + + protected void yesButton_Click(object sender, EventArgs e) { + OAuthServiceProvider.AuthorizePendingRequestToken(); + } + + protected void noButton_Click(object sender, EventArgs e) { + OAuthServiceProvider.PendingAuthorizationRequest = null; + Response.Redirect("~/"); + } + } +} diff --git a/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx.designer.cs b/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx.designer.cs new file mode 100644 index 0000000..719a853 --- /dev/null +++ b/projecttemplates/WebFormsRelyingParty/Members/OAuthAuthorize.aspx.designer.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:2.0.50727.4927 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace WebFormsRelyingParty.Members { + + + public partial class OAuthAuthorize { + + /// <summary> + /// consumerNameLabel control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.Label consumerNameLabel; + + /// <summary> + /// yesButton control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.Button yesButton; + + /// <summary> + /// noButton control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.Button noButton; + + /// <summary> + /// csrfCheck control. + /// </summary> + /// <remarks> + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// </remarks> + protected global::System.Web.UI.WebControls.HiddenField csrfCheck; + } +} diff --git a/projecttemplates/WebFormsRelyingParty/Model.Consumer.cs b/projecttemplates/WebFormsRelyingParty/Model.Consumer.cs index 20a1ccc..5076d6d 100644 --- a/projecttemplates/WebFormsRelyingParty/Model.Consumer.cs +++ b/projecttemplates/WebFormsRelyingParty/Model.Consumer.cs @@ -1,4 +1,10 @@ -namespace WebFormsRelyingParty { +//----------------------------------------------------------------------- +// <copyright file="Model.Consumer.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace WebFormsRelyingParty { using System; using System.Collections.Generic; using System.Linq; diff --git a/projecttemplates/WebFormsRelyingParty/Model.Designer.cs b/projecttemplates/WebFormsRelyingParty/Model.Designer.cs index 95ba3d3..85bd476 100644 --- a/projecttemplates/WebFormsRelyingParty/Model.Designer.cs +++ b/projecttemplates/WebFormsRelyingParty/Model.Designer.cs @@ -15,7 +15,7 @@ [assembly: global::System.Data.Objects.DataClasses.EdmRelationshipAttribute("DatabaseModel", "FK_IssuedToken_User", "User", global::System.Data.Metadata.Edm.RelationshipMultiplicity.ZeroOrOne, typeof(WebFormsRelyingParty.User), "IssuedTokens", global::System.Data.Metadata.Edm.RelationshipMultiplicity.Many, typeof(WebFormsRelyingParty.IssuedToken))] // Original file name: -// Generation date: 11/10/2009 8:47:09 AM +// Generation date: 11/10/2009 8:48:49 PM namespace WebFormsRelyingParty { @@ -772,6 +772,29 @@ namespace WebFormsRelyingParty partial void OnConsumerIdChanging(int value); partial void OnConsumerIdChanged(); /// <summary> + /// There are no comments for Property Name in the schema. + /// </summary> + [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute()] + [global::System.Runtime.Serialization.DataMemberAttribute()] + public string Name + { + get + { + return this._Name; + } + set + { + this.OnNameChanging(value); + this.ReportPropertyChanging("Name"); + this._Name = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value, true); + this.ReportPropertyChanged("Name"); + this.OnNameChanged(); + } + } + private string _Name; + partial void OnNameChanging(string value); + partial void OnNameChanged(); + /// <summary> /// There are no comments for IssuedToken in the schema. /// </summary> [global::System.Data.Objects.DataClasses.EdmRelationshipNavigationPropertyAttribute("DatabaseModel", "FK_IssuedToken_Consumer", "IssuedTokens")] diff --git a/projecttemplates/WebFormsRelyingParty/Model.IssuedAccessToken.cs b/projecttemplates/WebFormsRelyingParty/Model.IssuedAccessToken.cs index e47a9de..b2eb132 100644 --- a/projecttemplates/WebFormsRelyingParty/Model.IssuedAccessToken.cs +++ b/projecttemplates/WebFormsRelyingParty/Model.IssuedAccessToken.cs @@ -1,4 +1,10 @@ -namespace WebFormsRelyingParty { +//----------------------------------------------------------------------- +// <copyright file="Model.IssuedAccessToken.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace WebFormsRelyingParty { using System; using System.Collections.Generic; using System.Linq; diff --git a/projecttemplates/WebFormsRelyingParty/Model.IssuedRequestToken.cs b/projecttemplates/WebFormsRelyingParty/Model.IssuedRequestToken.cs index 08378de..1352e54 100644 --- a/projecttemplates/WebFormsRelyingParty/Model.IssuedRequestToken.cs +++ b/projecttemplates/WebFormsRelyingParty/Model.IssuedRequestToken.cs @@ -1,4 +1,10 @@ -namespace WebFormsRelyingParty { +//----------------------------------------------------------------------- +// <copyright file="Model.IssuedRequestToken.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace WebFormsRelyingParty { using System; using System.Collections.Generic; using System.Linq; @@ -6,18 +12,45 @@ using DotNetOpenAuth.OAuth.ChannelElements; public partial class IssuedRequestToken : IServiceProviderRequestToken { + /// <summary> + /// Gets or sets the callback associated specifically with this token, if any. + /// </summary> + /// <value> + /// The callback URI; or <c>null</c> if no callback was specifically assigned to this token. + /// </value> public Uri Callback { get { return this.CallbackAsString != null ? new Uri(this.CallbackAsString) : null; } set { this.CallbackAsString = value != null ? value.AbsoluteUri : null; } } + /// <summary> + /// Gets or sets the version of the Consumer that requested this token. + /// </summary> + /// <remarks> + /// This property is used to determine whether a <see cref="VerificationCode"/> must be + /// generated when the user authorizes the Consumer or not. + /// </remarks> Version IServiceProviderRequestToken.ConsumerVersion { get { return this.ConsumerVersionAsString != null ? new Version(this.ConsumerVersionAsString) : null; } set { this.ConsumerVersionAsString = value != null ? value.ToString() : null; } } + /// <summary> + /// Gets the consumer key that requested this token. + /// </summary> string IServiceProviderRequestToken.ConsumerKey { get { return this.Consumer.ConsumerKey; } } + + /// <summary> + /// Authorizes this request token to allow exchange for an access token. + /// </summary> + /// <remarks> + /// Call this method when the user has completed web-based authorization. + /// </remarks> + public void Authorize() { + this.User = Global.LoggedInUser; + Global.DataContext.SaveChanges(); + } } } diff --git a/projecttemplates/WebFormsRelyingParty/Model.User.cs b/projecttemplates/WebFormsRelyingParty/Model.User.cs index b2ea2f4..1493603 100644 --- a/projecttemplates/WebFormsRelyingParty/Model.User.cs +++ b/projecttemplates/WebFormsRelyingParty/Model.User.cs @@ -1,4 +1,10 @@ -namespace WebFormsRelyingParty { +//----------------------------------------------------------------------- +// <copyright file="Model.User.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace WebFormsRelyingParty { using System; using System.Collections.Generic; using System.Linq; diff --git a/projecttemplates/WebFormsRelyingParty/Model.edmx b/projecttemplates/WebFormsRelyingParty/Model.edmx index d2eed5a..bae6c17 100644 --- a/projecttemplates/WebFormsRelyingParty/Model.edmx +++ b/projecttemplates/WebFormsRelyingParty/Model.edmx @@ -53,6 +53,7 @@ <Property Name="Callback" Type="nvarchar" MaxLength="2048" /> <Property Name="VerificationCodeFormat" Type="int" Nullable="false" /> <Property Name="VerificationCodeLength" Type="int" Nullable="false" /> + <Property Name="Name" Type="nvarchar" MaxLength="50" /> </EntityType> <EntityType Name="IssuedToken"> <Key> @@ -237,7 +238,8 @@ <Property Name="VerificationCodeFormatAsInt" Type="Int32" Nullable="false" /> <Property Name="VerificationCodeLength" Type="Int32" Nullable="false" /> <Property Name="ConsumerId" Type="Int32" Nullable="false" /> - <NavigationProperty Name="IssuedToken" Relationship="DatabaseModel.FK_IssuedToken_Consumer" FromRole="Consumer" ToRole="IssuedTokens" /></EntityType> + <NavigationProperty Name="IssuedToken" Relationship="DatabaseModel.FK_IssuedToken_Consumer" FromRole="Consumer" ToRole="IssuedTokens" /> + <Property Name="Name" Type="String" Nullable="true" /></EntityType> <EntityType Name="IssuedToken" Abstract="true"> <Key> <PropertyRef Name="TokenId" /></Key> @@ -307,6 +309,7 @@ <EntitySetMapping Name="Consumer"> <EntityTypeMapping TypeName="IsTypeOf(DatabaseModel.Consumer)"> <MappingFragment StoreEntitySet="Consumer"> + <ScalarProperty Name="Name" ColumnName="Name" /> <ScalarProperty Name="ConsumerId" ColumnName="ConsumerId" /> <ScalarProperty Name="VerificationCodeLength" ColumnName="VerificationCodeLength" /> <ScalarProperty Name="VerificationCodeFormatAsInt" ColumnName="VerificationCodeFormat" /> diff --git a/projecttemplates/WebFormsRelyingParty/OAuth.ashx b/projecttemplates/WebFormsRelyingParty/OAuth.ashx new file mode 100644 index 0000000..6176757 --- /dev/null +++ b/projecttemplates/WebFormsRelyingParty/OAuth.ashx @@ -0,0 +1 @@ +<%@ WebHandler Language="C#" CodeBehind="OAuth.ashx.cs" Class="WebFormsRelyingParty.OAuth" %> diff --git a/projecttemplates/WebFormsRelyingParty/OAuth.ashx.cs b/projecttemplates/WebFormsRelyingParty/OAuth.ashx.cs new file mode 100644 index 0000000..faf0888 --- /dev/null +++ b/projecttemplates/WebFormsRelyingParty/OAuth.ashx.cs @@ -0,0 +1,65 @@ +//----------------------------------------------------------------------- +// <copyright file="OAuth.ashx.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace WebFormsRelyingParty { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Web; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth; + using DotNetOpenAuth.OAuth.Messages; + using WebFormsRelyingParty.Code; + + /// <summary> + /// Responds to incoming OAuth Service Provider messages. + /// </summary> + public class OAuth : IHttpHandler { + /// <summary> + /// Initializes a new instance of the <see cref="OAuth"/> class. + /// </summary> + public OAuth() { + } + + /// <summary> + /// Gets a value indicating whether another request can use the <see cref="T:System.Web.IHttpHandler"/> instance. + /// </summary> + /// <returns> + /// true if the <see cref="T:System.Web.IHttpHandler"/> instance is reusable; otherwise, false. + /// </returns> + public bool IsReusable { + get { return true; } + } + + /// <summary> + /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"/> interface. + /// </summary> + /// <param name="context">An <see cref="T:System.Web.HttpContext"/> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param> + public void ProcessRequest(HttpContext context) { + var serviceProvider = OAuthServiceProvider.ServiceProvider; + var requestMessage = serviceProvider.ReadRequest(new HttpRequestInfo(context.Request)); + + UnauthorizedTokenRequest unauthorizedTokenRequestMessage; + AuthorizedTokenRequest authorizedTokenRequestMessage; + UserAuthorizationRequest userAuthorizationRequest; + if ((unauthorizedTokenRequestMessage = requestMessage as UnauthorizedTokenRequest) != null) { + var response = serviceProvider.PrepareUnauthorizedTokenMessage(unauthorizedTokenRequestMessage); + serviceProvider.Channel.Send(response); + } else if ((authorizedTokenRequestMessage = requestMessage as AuthorizedTokenRequest) != null) { + var response = serviceProvider.PrepareAccessTokenMessage(authorizedTokenRequestMessage); + serviceProvider.Channel.Send(response); + } else if ((userAuthorizationRequest = requestMessage as UserAuthorizationRequest) != null) { + // This is a browser opening to allow the user to authorize a request token, + // so redirect to the authorization page, which will automatically redirect + // to have the user log in if necessary. + OAuthServiceProvider.PendingAuthorizationRequest = userAuthorizationRequest; + HttpContext.Current.Response.Redirect("~/Members/OAuthAuthorize.aspx"); + } else { + throw new InvalidOperationException(); + } + } + } +} diff --git a/projecttemplates/WebFormsRelyingParty/WebFormsRelyingParty.csproj b/projecttemplates/WebFormsRelyingParty/WebFormsRelyingParty.csproj index afd2cec..0129010 100644 --- a/projecttemplates/WebFormsRelyingParty/WebFormsRelyingParty.csproj +++ b/projecttemplates/WebFormsRelyingParty/WebFormsRelyingParty.csproj @@ -88,9 +88,18 @@ </ItemGroup> <ItemGroup> <Compile Include="Code\OAuthConsumerTokenManager.cs" /> + <Compile Include="Code\OAuthServiceProvider.cs" /> <Compile Include="Code\OAuthServiceProviderTokenManager.cs" /> <Compile Include="Code\OAuthTokenManager.cs" /> <Compile Include="Code\Policies.cs" /> + <Compile Include="Code\Utilities.cs" /> + <Compile Include="Members\OAuthAuthorize.aspx.cs"> + <DependentUpon>OAuthAuthorize.aspx</DependentUpon> + <SubType>ASPXCodeBehind</SubType> + </Compile> + <Compile Include="Members\OAuthAuthorize.aspx.designer.cs"> + <DependentUpon>OAuthAuthorize.aspx</DependentUpon> + </Compile> <Compile Include="Model.IssuedRequestToken.cs" /> <Compile Include="Model.IssuedAccessToken.cs" /> <Compile Include="Model.Consumer.cs" /> @@ -160,6 +169,9 @@ <DesignTime>True</DesignTime> <DependentUpon>Model.edmx</DependentUpon> </Compile> + <Compile Include="OAuth.ashx.cs"> + <DependentUpon>OAuth.ashx</DependentUpon> + </Compile> <Compile Include="Properties\AssemblyInfo.cs" /> <EntityDeploy Include="Model.edmx"> <Generator>EntityModelCodeGenerator</Generator> @@ -251,6 +263,9 @@ </ItemGroup> <ItemGroup> <None Include="Admin\CreateDatabase.sql" /> + <Content Include="bin\Microsoft.Contracts.dll" /> + <Content Include="Members\OAuthAuthorize.aspx" /> + <Content Include="OAuth.ashx" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\src\DotNetOpenAuth\DotNetOpenAuth.csproj"> |