diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2010-05-17 22:22:05 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2010-05-17 22:22:05 -0700 |
commit | 18c5c8fd2a715086674654e461608fa6fc33ba1a (patch) | |
tree | a08b37e5e12bf0146f976136cd8c8aa64f9efc4b /src | |
parent | 4013c20c2f2dbcd4ba93179c075b7edd1cc1f75e (diff) | |
download | DotNetOpenAuth-18c5c8fd2a715086674654e461608fa6fc33ba1a.zip DotNetOpenAuth-18c5c8fd2a715086674654e461608fa6fc33ba1a.tar.gz DotNetOpenAuth-18c5c8fd2a715086674654e461608fa6fc33ba1a.tar.bz2 |
Work toward an auth server side of the web server flow in OAuth 2.0
Diffstat (limited to 'src')
7 files changed, 414 insertions, 1 deletions
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index bd38345..dd95d3a 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -306,7 +306,9 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="Mvc\OpenIdHelper.cs" /> <Compile Include="Mvc\OpenIdAjaxOptions.cs" /> <Compile Include="Messaging\StandardMessageFactory.cs" /> + <Compile Include="OAuthWrap\AuthorizationServerBase.cs" /> <Compile Include="OAuthWrap\AuthorizationState.cs" /> + <Compile Include="OAuthWrap\IAuthorizationServer.cs" /> <Compile Include="OAuthWrap\IAuthorizationState.cs" /> <Compile Include="OAuthWrap\IClientTokenManager.cs" /> <Compile Include="OAuthWrap\Messages\Assertion\AssertionRequest.cs" /> @@ -327,6 +329,7 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="OAuthWrap\Messages\UserAgent\UserAgentSuccessResponse.cs" /> <Compile Include="OAuthWrap\Messages\UsernameAndPassword\UserNamePasswordCaptchaResponse.cs" /> <Compile Include="OAuthWrap\Messages\UsernameAndPassword\UserNamePasswordVerificationResponse.cs" /> + <Compile Include="OAuthWrap\WebAppAuthorizationServer.cs" /> <Compile Include="OAuthWrap\WrapUtilities.cs" /> <Compile Include="OAuth\ChannelElements\ICombinedOpenIdProviderTokenManager.cs" /> <Compile Include="OAuth\ChannelElements\IConsumerDescription.cs" /> diff --git a/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs.orig b/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs.orig new file mode 100644 index 0000000..a17b379 --- /dev/null +++ b/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs.orig @@ -0,0 +1,222 @@ +//----------------------------------------------------------------------- +// <copyright file="IDirectWebRequestHandler.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Messaging { + using System.Collections.Generic; + using System.IO; + using System.Net; + using DotNetOpenAuth.Messaging; + + /// <summary> + /// A contract for <see cref="HttpWebRequest"/> handling. + /// </summary> + /// <remarks> + /// Implementations of this interface must be thread safe. + /// </remarks> + public interface IDirectWebRequestHandler { + /// <summary> + /// Determines whether this instance can support the specified options. + /// </summary> + /// <param name="options">The set of options that might be given in a subsequent web request.</param> + /// <returns> + /// <c>true</c> if this instance can support the specified options; otherwise, <c>false</c>. + /// </returns> + bool CanSupport(DirectWebRequestOptions options); + + /// <summary> + /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity. + /// </summary> + /// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param> + /// <returns> + /// The stream the caller should write out the entity data to. + /// </returns> + /// <exception cref="ProtocolException">Thrown for any network error.</exception> + /// <remarks> + /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/> + /// and any other appropriate properties <i>before</i> calling this method. + /// Callers <i>must</i> close and dispose of the request stream when they are done + /// writing to it to avoid taking up the connection too long and causing long waits on + /// subsequent requests.</para> + /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a + /// <see cref="ProtocolException"/> to abstract away the transport and provide + /// a single exception type for hosts to catch.</para> + /// </remarks> + Stream GetRequestStream(HttpWebRequest request); + + /// <summary> + /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity. + /// </summary> + /// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param> + /// <param name="options">The options to apply to this web request.</param> + /// <returns> + /// The stream the caller should write out the entity data to. + /// </returns> + /// <exception cref="ProtocolException">Thrown for any network error.</exception> + /// <remarks> + /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/> + /// and any other appropriate properties <i>before</i> calling this method. + /// Callers <i>must</i> close and dispose of the request stream when they are done + /// writing to it to avoid taking up the connection too long and causing long waits on + /// subsequent requests.</para> + /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a + /// <see cref="ProtocolException"/> to abstract away the transport and provide + /// a single exception type for hosts to catch.</para> + /// </remarks> + Stream GetRequestStream(HttpWebRequest request, DirectWebRequestOptions options); + + /// <summary> + /// Processes an <see cref="HttpWebRequest"/> and converts the + /// <see cref="HttpWebResponse"/> to a <see cref="IncomingWebResponse"/> instance. + /// </summary> + /// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param> + /// <returns>An instance of <see cref="IncomingWebResponse"/> describing the response.</returns> + /// <exception cref="ProtocolException">Thrown for any network error.</exception> + /// <remarks> + /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a + /// <see cref="ProtocolException"/> to abstract away the transport and provide + /// a single exception type for hosts to catch. The <see cref="WebException.Response"/> + /// value, if set, should be Closed before throwing.</para> + /// </remarks> + IncomingWebResponse GetResponse(HttpWebRequest request); + + /// <summary> + /// Processes an <see cref="HttpWebRequest"/> and converts the + /// <see cref="HttpWebResponse"/> to a <see cref="IncomingWebResponse"/> instance. + /// </summary> + /// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param> + /// <param name="options">The options to apply to this web request.</param> + /// <returns>An instance of <see cref="IncomingWebResponse"/> describing the response.</returns> + /// <exception cref="ProtocolException">Thrown for any network error.</exception> + /// <remarks> + /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a + /// <see cref="ProtocolException"/> to abstract away the transport and provide + /// a single exception type for hosts to catch. The <see cref="WebException.Response"/> + /// value, if set, should be Closed before throwing.</para> + /// </remarks> + IncomingWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options); + } +<<<<<<< HEAD +======= + + /// <summary> + /// Code contract for the <see cref="IDirectWebRequestHandler"/> type. + /// </summary> + [ContractClassFor(typeof(IDirectWebRequestHandler))] + internal abstract class IDirectWebRequestHandlerContract : IDirectWebRequestHandler { + #region IDirectWebRequestHandler Members + + /// <summary> + /// Determines whether this instance can support the specified options. + /// </summary> + /// <param name="options">The set of options that might be given in a subsequent web request.</param> + /// <returns> + /// <c>true</c> if this instance can support the specified options; otherwise, <c>false</c>. + /// </returns> + bool IDirectWebRequestHandler.CanSupport(DirectWebRequestOptions options) { + throw new System.NotImplementedException(); + } + + /// <summary> + /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity. + /// </summary> + /// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param> + /// <returns> + /// The stream the caller should write out the entity data to. + /// </returns> + /// <exception cref="ProtocolException">Thrown for any network error.</exception> + /// <remarks> + /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/> + /// and any other appropriate properties <i>before</i> calling this method. + /// Callers <i>must</i> close and dispose of the request stream when they are done + /// writing to it to avoid taking up the connection too long and causing long waits on + /// subsequent requests.</para> + /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a + /// <see cref="ProtocolException"/> to abstract away the transport and provide + /// a single exception type for hosts to catch.</para> + /// </remarks> + Stream IDirectWebRequestHandler.GetRequestStream(HttpWebRequest request) { + Contract.Requires<ArgumentNullException>(request != null); + throw new System.NotImplementedException(); + } + + /// <summary> + /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity. + /// </summary> + /// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param> + /// <param name="options">The options to apply to this web request.</param> + /// <returns> + /// The stream the caller should write out the entity data to. + /// </returns> + /// <exception cref="ProtocolException">Thrown for any network error.</exception> + /// <remarks> + /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/> + /// and any other appropriate properties <i>before</i> calling this method. + /// Callers <i>must</i> close and dispose of the request stream when they are done + /// writing to it to avoid taking up the connection too long and causing long waits on + /// subsequent requests.</para> + /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a + /// <see cref="ProtocolException"/> to abstract away the transport and provide + /// a single exception type for hosts to catch.</para> + /// </remarks> + Stream IDirectWebRequestHandler.GetRequestStream(HttpWebRequest request, DirectWebRequestOptions options) { + Contract.Requires<ArgumentNullException>(request != null); + Contract.Requires<NotSupportedException>(((IDirectWebRequestHandler)this).CanSupport(options), MessagingStrings.DirectWebRequestOptionsNotSupported); + ////ErrorUtilities.VerifySupported(((IDirectWebRequestHandler)this).CanSupport(options), string.Format(MessagingStrings.DirectWebRequestOptionsNotSupported, options, this.GetType().Name)); + throw new System.NotImplementedException(); + } + + /// <summary> + /// Processes an <see cref="HttpWebRequest"/> and converts the + /// <see cref="HttpWebResponse"/> to a <see cref="IncomingWebResponse"/> instance. + /// </summary> + /// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param> + /// <returns> + /// An instance of <see cref="IncomingWebResponse"/> describing the response. + /// </returns> + /// <exception cref="ProtocolException">Thrown for any network error.</exception> + /// <remarks> + /// Implementations should catch <see cref="WebException"/> and wrap it in a + /// <see cref="ProtocolException"/> to abstract away the transport and provide + /// a single exception type for hosts to catch. The <see cref="WebException.Response"/> + /// value, if set, should be Closed before throwing. + /// </remarks> + IncomingWebResponse IDirectWebRequestHandler.GetResponse(HttpWebRequest request) { + Contract.Requires<ArgumentNullException>(request != null); + Contract.Ensures(Contract.Result<IncomingWebResponse>() != null); + Contract.Ensures(Contract.Result<IncomingWebResponse>().ResponseStream != null); + throw new System.NotImplementedException(); + } + + /// <summary> + /// Processes an <see cref="HttpWebRequest"/> and converts the + /// <see cref="HttpWebResponse"/> to a <see cref="IncomingWebResponse"/> instance. + /// </summary> + /// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param> + /// <param name="options">The options to apply to this web request.</param> + /// <returns> + /// An instance of <see cref="IncomingWebResponse"/> describing the response. + /// </returns> + /// <exception cref="ProtocolException">Thrown for any network error.</exception> + /// <remarks> + /// Implementations should catch <see cref="WebException"/> and wrap it in a + /// <see cref="ProtocolException"/> to abstract away the transport and provide + /// a single exception type for hosts to catch. The <see cref="WebException.Response"/> + /// value, if set, should be Closed before throwing. + /// </remarks> + IncomingWebResponse IDirectWebRequestHandler.GetResponse(HttpWebRequest request, DirectWebRequestOptions options) { + Contract.Requires<ArgumentNullException>(request != null); + Contract.Requires<NotSupportedException>(((IDirectWebRequestHandler)this).CanSupport(options), MessagingStrings.DirectWebRequestOptionsNotSupported); + Contract.Ensures(Contract.Result<IncomingWebResponse>() != null); + Contract.Ensures(Contract.Result<IncomingWebResponse>().ResponseStream != null); + + ////ErrorUtilities.VerifySupported(((IDirectWebRequestHandler)this).CanSupport(options), string.Format(MessagingStrings.DirectWebRequestOptionsNotSupported, options, this.GetType().Name)); + throw new System.NotImplementedException(); + } + + #endregion + } +>>>>>>> 884bcec... Fixed typo in comments. +} diff --git a/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs b/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs new file mode 100644 index 0000000..cfbd80f --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------- +// <copyright file="AuthorizationServerBase.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +using System.Diagnostics.Contracts; +using DotNetOpenAuth.OAuth.ChannelElements; + +namespace DotNetOpenAuth.OAuthWrap { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using DotNetOpenAuth.Messaging; + + public abstract class AuthorizationServerBase { + protected AuthorizationServerBase(IAuthorizationServer authorizationServer) { + Contract.Requires<ArgumentNullException>(authorizationServer != null, "authorizationServer"); + this.AuthorizationServer = authorizationServer; + } + + public Channel Channel { get; set; } + + public IAuthorizationServer AuthorizationServer { get; set; } + + protected IConsumerDescription GetClient(string clientIdentifier) { + Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(clientIdentifier)); + Contract.Ensures(Contract.Result<IConsumerDescription>() != null); + + try { + return this.AuthorizationServer.GetClient(clientIdentifier); + } catch (KeyNotFoundException ex) { + throw ErrorUtilities.Wrap(ex, DotNetOpenAuth.OAuth.OAuthStrings.ConsumerOrTokenSecretNotFound); + } + } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs b/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs new file mode 100644 index 0000000..ba328b1 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------- +// <copyright file="IAuthorizationServer.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +using System.Diagnostics.Contracts; + +namespace DotNetOpenAuth.OAuthWrap { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using DotNetOpenAuth.OAuth.ChannelElements; + + [ContractClass(typeof(IAuthorizationServerContract))] + public interface IAuthorizationServer { + IConsumerDescription GetClient(string clientIdentifier); + } + + [ContractClassFor(typeof(IAuthorizationServer))] + internal abstract class IAuthorizationServerContract : IAuthorizationServer { + private IAuthorizationServerContract() { + } + + IConsumerDescription IAuthorizationServer.GetClient(string clientIdentifier) { + Contract.Ensures(Contract.Result<IConsumerDescription>() != null); + throw new NotImplementedException(); + } + } + +} diff --git a/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.Designer.cs b/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.Designer.cs index 326beb6..d5836ff 100644 --- a/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.Designer.cs +++ b/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:4.0.30128.0 +// Runtime Version:4.0.30426.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -86,5 +86,14 @@ namespace DotNetOpenAuth.OAuthWrap { return ResourceManager.GetString("InvalidClientCredentials", resourceCulture); } } + + /// <summary> + /// Looks up a localized string similar to No callback URI was available for this request.. + /// </summary> + internal static string NoCallback { + get { + return ResourceManager.GetString("NoCallback", resourceCulture); + } + } } } diff --git a/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.resx b/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.resx index d488810..2e70624 100644 --- a/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.resx +++ b/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.resx @@ -126,4 +126,7 @@ <data name="InvalidClientCredentials" xml:space="preserve"> <value>Failed to obtain access token due to invalid Client Identifier or Client Secret.</value> </data> + <data name="NoCallback" xml:space="preserve"> + <value>No callback URI was available for this request.</value> + </data> </root>
\ No newline at end of file diff --git a/src/DotNetOpenAuth/OAuthWrap/WebAppAuthorizationServer.cs b/src/DotNetOpenAuth/OAuthWrap/WebAppAuthorizationServer.cs new file mode 100644 index 0000000..91ce097 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/WebAppAuthorizationServer.cs @@ -0,0 +1,106 @@ +//----------------------------------------------------------------------- +// <copyright file="WebAppAuthorizationServer.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuthWrap { + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.Linq; + using System.Text; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuthWrap.Messages; + + public class WebAppAuthorizationServer : AuthorizationServerBase { + public WebAppAuthorizationServer(IAuthorizationServer authorizationServer) + : base(authorizationServer) { + Contract.Requires<ArgumentNullException>(authorizationServer != null, "authorizationServer"); + } + + /// <summary> + /// Reads in a client's request for the Authorization Server to obtain permission from + /// the user to authorize the Client's access of some protected resource(s). + /// </summary> + /// <returns>The incoming request, or null if no OAuth message was attached.</returns> + /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> + /// <remarks> + /// Requires HttpContext.Current. + /// </remarks> + public WebAppRequest ReadAuthorizationRequest() { + return this.ReadAuthorizationRequest(this.Channel.GetRequestFromContext()); + } + + /// <summary> + /// Reads in a client's request for the Authorization Server to obtain permission from + /// the user to authorize the Client's access of some protected resource(s). + /// </summary> + /// <param name="request">The HTTP request to read from.</param> + /// <returns>The incoming request, or null if no OAuth message was attached.</returns> + /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> + public WebAppRequest ReadAuthorizationRequest(HttpRequestInfo request) { + Contract.Requires<ArgumentNullException>(request != null); + WebAppRequest message; + this.Channel.TryReadFromRequest(request, out message); + return message; + } + + public OutgoingWebResponse ApproveAuthorizationRequest(WebAppRequest authorizationRequest) { + Contract.Requires<ArgumentNullException>(authorizationRequest != null, "authorizationRequest"); + Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null); + + return ApproveAuthorizationRequest(authorizationRequest, this.GetCallback(authorizationRequest)); + } + + public OutgoingWebResponse ApproveAuthorizationRequest(WebAppRequest authorizationRequest, Uri callback) { + Contract.Requires<ArgumentNullException>(authorizationRequest != null, "authorizationRequest"); + Contract.Requires<ArgumentNullException>(callback != null, "callback"); + Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null); + + var client = GetClient(authorizationRequest.ClientIdentifier); + var response = new WebAppSuccessResponse(callback, ((IMessage)authorizationRequest).Version) { + ClientState = authorizationRequest.ClientState, + VerificationCode = OAuth.ServiceProvider.CreateVerificationCode(client.VerificationCodeFormat, client.VerificationCodeLength), + }; + + return this.Channel.PrepareResponse(response); + } + + public OutgoingWebResponse RejectAuthorizationRequest(WebAppRequest authorizationRequest) { + Contract.Requires<ArgumentNullException>(authorizationRequest != null, "authorizationRequest"); + Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null); + + return this.RejectAuthorizationRequest(authorizationRequest, GetCallback(authorizationRequest)); + } + + public OutgoingWebResponse RejectAuthorizationRequest(WebAppRequest authorizationRequest, Uri callback) { + Contract.Requires<ArgumentNullException>(authorizationRequest != null, "authorizationRequest"); + Contract.Requires<ArgumentNullException>(callback != null, "callback"); + Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null); + + var response = new WebAppFailedResponse(callback, ((IMessage)authorizationRequest).Version) { + ClientState = authorizationRequest.ClientState, + }; + + return this.Channel.PrepareResponse(response); + } + + protected Uri GetCallback(WebAppRequest authorizationRequest) { + Contract.Requires<ArgumentNullException>(authorizationRequest != null, "authorizationRequest"); + Contract.Ensures(Contract.Result<Uri>() != null); + + // Prefer a request-specific callback to the pre-registered one (if any). + if (authorizationRequest.Callback != null) { + return authorizationRequest.Callback; + } + + var client = this.AuthorizationServer.GetClient(authorizationRequest.ClientIdentifier); + if (client.Callback != null) { + return client.Callback; + } + + throw ErrorUtilities.ThrowProtocol(OAuthWrapStrings.NoCallback); + } + } +} |