summaryrefslogtreecommitdiffstats
path: root/src/DotNetOpenAuth.OAuth2.Client
diff options
context:
space:
mode:
Diffstat (limited to 'src/DotNetOpenAuth.OAuth2.Client')
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/Configuration/OAuth2ClientSection.cs36
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj16
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/AuthorizationServerDescription.cs62
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/AuthorizationState.cs93
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/IOAuth2ChannelWithClient.cs27
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs159
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs68
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs169
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientStrings.Designer.cs17
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientStrings.resx11
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/IAuthorizationState.cs67
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/IClientAuthorizationTracker.cs53
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/AccessTokenAuthorizationCodeRequestC.cs27
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/AccessTokenRefreshRequestC.cs26
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/EndUserAuthorizationImplicitRequestC.cs28
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/EndUserAuthorizationRequestC.cs31
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs33
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs23
18 files changed, 916 insertions, 30 deletions
diff --git a/src/DotNetOpenAuth.OAuth2.Client/Configuration/OAuth2ClientSection.cs b/src/DotNetOpenAuth.OAuth2.Client/Configuration/OAuth2ClientSection.cs
new file mode 100644
index 0000000..1ee5aa5
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2.Client/Configuration/OAuth2ClientSection.cs
@@ -0,0 +1,36 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuth2ClientSection.cs" company="Outercurve Foundation">
+// Copyright (c) Outercurve Foundation. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Configuration {
+ using System.Configuration;
+ using System.Diagnostics.Contracts;
+
+ /// <summary>
+ /// Represents the &lt;oauth2/client&gt; section in the host's .config file.
+ /// </summary>
+ internal class OAuth2ClientSection : ConfigurationSection {
+ /// <summary>
+ /// The name of the oauth2/client section.
+ /// </summary>
+ private const string SectionName = OAuth2SectionGroup.SectionName + "/client";
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuth2ClientSection"/> class.
+ /// </summary>
+ internal OAuth2ClientSection() {
+ }
+
+ /// <summary>
+ /// Gets the configuration section from the .config file.
+ /// </summary>
+ internal static OAuth2ClientSection Configuration {
+ get {
+ Contract.Ensures(Contract.Result<OAuth2ClientSection>() != null);
+ return (OAuth2ClientSection)ConfigurationManager.GetSection(SectionName) ?? new OAuth2ClientSection();
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj b/src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj
index 3625e54..e72ee1a 100644
--- a/src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj
+++ b/src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj
@@ -18,6 +18,18 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
</PropertyGroup>
<ItemGroup>
+ <Compile Include="Configuration\OAuth2ClientSection.cs" />
+ <Compile Include="OAuth2\AuthorizationServerDescription.cs" />
+ <Compile Include="OAuth2\AuthorizationState.cs" />
+ <Compile Include="OAuth2\ChannelElements\IOAuth2ChannelWithClient.cs" />
+ <Compile Include="OAuth2\ChannelElements\OAuth2ClientChannel.cs" />
+ <Compile Include="OAuth2\ClientCredentialApplicator.cs" />
+ <Compile Include="OAuth2\IAuthorizationState.cs" />
+ <Compile Include="OAuth2\IClientAuthorizationTracker.cs" />
+ <Compile Include="OAuth2\Messages\AccessTokenAuthorizationCodeRequestC.cs" />
+ <Compile Include="OAuth2\Messages\AccessTokenRefreshRequestC.cs" />
+ <Compile Include="OAuth2\Messages\EndUserAuthorizationImplicitRequestC.cs" />
+ <Compile Include="OAuth2\Messages\EndUserAuthorizationRequestC.cs" />
<Compile Include="OAuth2\ClientStrings.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
@@ -33,6 +45,10 @@
<Project>{60426312-6AE5-4835-8667-37EDEA670222}</Project>
<Name>DotNetOpenAuth.Core</Name>
</ProjectReference>
+ <ProjectReference Include="..\DotNetOpenAuth.OAuth2.ClientAuthorization\DotNetOpenAuth.OAuth2.ClientAuthorization.csproj">
+ <Project>{CCF3728A-B3D7-404A-9BC6-75197135F2D7}</Project>
+ <Name>DotNetOpenAuth.OAuth2.ClientAuthorization</Name>
+ </ProjectReference>
<ProjectReference Include="..\DotNetOpenAuth.OAuth2\DotNetOpenAuth.OAuth2.csproj">
<Project>{56459A6C-6BA2-4BAC-A9C0-27E3BD961FA6}</Project>
<Name>DotNetOpenAuth.OAuth2</Name>
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/AuthorizationServerDescription.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/AuthorizationServerDescription.cs
new file mode 100644
index 0000000..38a9ff9
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/AuthorizationServerDescription.cs
@@ -0,0 +1,62 @@
+//-----------------------------------------------------------------------
+// <copyright file="AuthorizationServerDescription.cs" company="Outercurve Foundation">
+// Copyright (c) Outercurve Foundation. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth2 {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// A description of an OAuth Authorization Server as seen by an OAuth Client.
+ /// </summary>
+ public class AuthorizationServerDescription {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AuthorizationServerDescription"/> class.
+ /// </summary>
+ public AuthorizationServerDescription() {
+ this.ProtocolVersion = Protocol.Default.ProtocolVersion;
+ }
+
+ /// <summary>
+ /// Gets or sets the Authorization Server URL from which an Access Token is requested by the Client.
+ /// </summary>
+ /// <value>An HTTPS URL.</value>
+ /// <remarks>
+ /// <para>After obtaining authorization from the resource owner, clients request an access token from the authorization server's token endpoint.</para>
+ /// <para>The URI of the token endpoint can be found in the service documentation, or can be obtained by the client by making an unauthorized protected resource request (from the WWW-Authenticate response header token-uri (The 'authorization-uri' Attribute) attribute).</para>
+ /// <para>The token endpoint advertised by the resource server MAY include a query component as defined by [RFC3986] (Berners-Lee, T., Fielding, R., and L. Masinter, “Uniform Resource Identifier (URI): Generic Syntax,” January 2005.) section 3.</para>
+ /// <para>Since requests to the token endpoint result in the transmission of plain text credentials in the HTTP request and response, the authorization server MUST require the use of a transport-layer mechanism such as TLS/SSL (or a secure channel with equivalent protections) when sending requests to the token endpoints. </para>
+ /// </remarks>
+ public Uri TokenEndpoint { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Authorization Server URL where the Client (re)directs the User
+ /// to make an authorization request.
+ /// </summary>
+ /// <value>An HTTPS URL.</value>
+ /// <remarks>
+ /// <para>Clients direct the resource owner to the authorization endpoint to approve their access request. Before granting access, the resource owner first authenticates with the authorization server. The way in which the authorization server authenticates the end-user (e.g. username and password login, OpenID, session cookies) and in which the authorization server obtains the end-user's authorization, including whether it uses a secure channel such as TLS/SSL, is beyond the scope of this specification. However, the authorization server MUST first verify the identity of the end-user.</para>
+ /// <para>The URI of the authorization endpoint can be found in the service documentation, or can be obtained by the client by making an unauthorized protected resource request (from the WWW-Authenticate response header auth-uri (The 'authorization-uri' Attribute) attribute).</para>
+ /// <para>The authorization endpoint advertised by the resource server MAY include a query component as defined by [RFC3986] (Berners-Lee, T., Fielding, R., and L. Masinter, “Uniform Resource Identifier (URI): Generic Syntax,” January 2005.) section 3.</para>
+ /// <para>Since requests to the authorization endpoint result in user authentication and the transmission of sensitive values, the authorization server SHOULD require the use of a transport-layer mechanism such as TLS/SSL (or a secure channel with equivalent protections) when sending requests to the authorization endpoints.</para>
+ /// </remarks>
+ public Uri AuthorizationEndpoint { get; set; }
+
+ /// <summary>
+ /// Gets or sets the OAuth version supported by the Authorization Server.
+ /// </summary>
+ public ProtocolVersion ProtocolVersion { get; set; }
+
+ /// <summary>
+ /// Gets the version of the OAuth protocol to use with this Authorization Server.
+ /// </summary>
+ /// <value>The version.</value>
+ internal Version Version {
+ get { return Protocol.Lookup(this.ProtocolVersion).Version; }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/AuthorizationState.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/AuthorizationState.cs
new file mode 100644
index 0000000..4117b3c
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/AuthorizationState.cs
@@ -0,0 +1,93 @@
+//-----------------------------------------------------------------------
+// <copyright file="AuthorizationState.cs" company="Outercurve Foundation">
+// Copyright (c) Outercurve Foundation. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth2 {
+ using System;
+ using System.Collections.Generic;
+
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A simple in-memory copy of an authorization state.
+ /// </summary>
+ [Serializable]
+ public class AuthorizationState : IAuthorizationState {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AuthorizationState"/> class.
+ /// </summary>
+ /// <param name="scopes">The scopes of access being requested or that was obtained.</param>
+ public AuthorizationState(IEnumerable<string> scopes = null) {
+ this.Scope = new HashSet<string>(OAuthUtilities.ScopeStringComparer);
+ if (scopes != null) {
+ this.Scope.AddRange(scopes);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the callback URL used to obtain authorization.
+ /// </summary>
+ /// <value>The callback URL.</value>
+ public Uri Callback { get; set; }
+
+ /// <summary>
+ /// Gets or sets the long-lived token used to renew the short-lived <see cref="AccessToken"/>.
+ /// </summary>
+ /// <value>The refresh token.</value>
+ public string RefreshToken { get; set; }
+
+ /// <summary>
+ /// Gets or sets the access token.
+ /// </summary>
+ /// <value>The access token.</value>
+ public string AccessToken { get; set; }
+
+ /// <summary>
+ /// Gets or sets the access token UTC expiration date.
+ /// </summary>
+ /// <value></value>
+ public DateTime? AccessTokenExpirationUtc { get; set; }
+
+ /// <summary>
+ /// Gets or sets the access token issue date UTC.
+ /// </summary>
+ /// <value>The access token issue date UTC.</value>
+ public DateTime? AccessTokenIssueDateUtc { get; set; }
+
+ /// <summary>
+ /// Gets the scope the token is (to be) authorized for.
+ /// </summary>
+ /// <value>The scope.</value>
+ public HashSet<string> Scope { get; private set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance is deleted.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this instance is deleted; otherwise, <c>false</c>.
+ /// </value>
+ public bool IsDeleted { get; set; }
+
+ /// <summary>
+ /// Deletes this authorization, including access token and refresh token where applicable.
+ /// </summary>
+ /// <remarks>
+ /// This method is invoked when an authorization attempt fails, is rejected, is revoked, or
+ /// expires and cannot be renewed.
+ /// </remarks>
+ public virtual void Delete() {
+ this.IsDeleted = true;
+ }
+
+ /// <summary>
+ /// Saves any changes made to this authorization object's properties.
+ /// </summary>
+ /// <remarks>
+ /// This method is invoked after DotNetOpenAuth changes any property.
+ /// </remarks>
+ public virtual void SaveChanges() {
+ }
+ }
+} \ No newline at end of file
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/IOAuth2ChannelWithClient.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/IOAuth2ChannelWithClient.cs
new file mode 100644
index 0000000..c802be6
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/IOAuth2ChannelWithClient.cs
@@ -0,0 +1,27 @@
+//-----------------------------------------------------------------------
+// <copyright file="IOAuth2ChannelWithClient.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth2.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// An interface that defines the OAuth2 client specific channel additions.
+ /// </summary>
+ internal interface IOAuth2ChannelWithClient {
+ /// <summary>
+ /// Gets or sets the identifier by which this client is known to the Authorization Server.
+ /// </summary>
+ string ClientIdentifier { get; set; }
+
+ /// <summary>
+ /// Gets or sets the client credentials applicator extension to use.
+ /// </summary>
+ ClientCredentialApplicator ClientCredentialApplicator { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs
new file mode 100644
index 0000000..8ad2ed9
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs
@@ -0,0 +1,159 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuth2ClientChannel.cs" company="Outercurve Foundation">
+// Copyright (c) Outercurve Foundation. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth2.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using System.Diagnostics.Contracts;
+ using System.Net;
+ using System.Web;
+
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuth2.Messages;
+
+ /// <summary>
+ /// The messaging channel used by OAuth 2.0 Clients.
+ /// </summary>
+ internal class OAuth2ClientChannel : OAuth2ChannelBase, IOAuth2ChannelWithClient {
+ /// <summary>
+ /// The messages receivable by this channel.
+ /// </summary>
+ private static readonly Type[] MessageTypes = new Type[] {
+ typeof(AccessTokenSuccessResponse),
+ typeof(AccessTokenFailedResponse),
+ typeof(EndUserAuthorizationSuccessAuthCodeResponse),
+ typeof(EndUserAuthorizationSuccessAccessTokenResponse),
+ typeof(EndUserAuthorizationFailedResponse),
+ typeof(UnauthorizedResponse),
+ };
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuth2ClientChannel"/> class.
+ /// </summary>
+ internal OAuth2ClientChannel()
+ : base(MessageTypes) {
+ }
+
+ /// <summary>
+ /// Gets or sets the identifier by which this client is known to the Authorization Server.
+ /// </summary>
+ public string ClientIdentifier { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tool to use to apply client credentials to authenticated requests to the Authorization Server.
+ /// </summary>
+ /// <value>May be <c>null</c> if this client has no client secret.</value>
+ public ClientCredentialApplicator ClientCredentialApplicator { get; set; }
+
+ /// <summary>
+ /// Prepares an HTTP request that carries a given message.
+ /// </summary>
+ /// <param name="request">The message to send.</param>
+ /// <returns>
+ /// The <see cref="HttpWebRequest"/> prepared to send the request.
+ /// </returns>
+ /// <remarks>
+ /// This method must be overridden by a derived class, unless the <see cref="Channel.RequestCore"/> method
+ /// is overridden and does not require this method.
+ /// </remarks>
+ protected override HttpWebRequest CreateHttpRequest(IDirectedProtocolMessage request) {
+ HttpWebRequest httpRequest;
+ if ((request.HttpMethods & HttpDeliveryMethods.GetRequest) != 0) {
+ httpRequest = InitializeRequestAsGet(request);
+ } else if ((request.HttpMethods & HttpDeliveryMethods.PostRequest) != 0) {
+ httpRequest = InitializeRequestAsPost(request);
+ } else {
+ throw new NotSupportedException();
+ }
+
+ return httpRequest;
+ }
+
+ /// <summary>
+ /// Gets the protocol message that may be in the given HTTP response.
+ /// </summary>
+ /// <param name="response">The response that is anticipated to contain an protocol message.</param>
+ /// <returns>
+ /// The deserialized message parts, if found. Null otherwise.
+ /// </returns>
+ /// <exception cref="ProtocolException">Thrown when the response is not valid.</exception>
+ protected override IDictionary<string, string> ReadFromResponseCore(IncomingWebResponse response) {
+ // The spec says direct responses should be JSON objects, but Facebook uses HttpFormUrlEncoded instead, calling it text/plain
+ // Others return text/javascript. Again bad.
+ string body = response.GetResponseReader().ReadToEnd();
+ if (response.ContentType.MediaType == JsonEncoded || response.ContentType.MediaType == JsonTextEncoded) {
+ return this.DeserializeFromJson(body);
+ } else if (response.ContentType.MediaType == HttpFormUrlEncoded || response.ContentType.MediaType == PlainTextEncoded) {
+ return HttpUtility.ParseQueryString(body).ToDictionary();
+ } else {
+ throw ErrorUtilities.ThrowProtocol(ClientStrings.UnexpectedResponseContentType, response.ContentType.MediaType);
+ }
+ }
+
+ /// <summary>
+ /// Gets the protocol message that may be embedded in the given HTTP request.
+ /// </summary>
+ /// <param name="request">The request to search for an embedded message.</param>
+ /// <returns>
+ /// The deserialized message, if one is found. Null otherwise.
+ /// </returns>
+ protected override IDirectedProtocolMessage ReadFromRequestCore(HttpRequestBase request) {
+ Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.HttpMethod, request.GetPublicFacingUrl().AbsoluteUri);
+
+ var fields = request.GetQueryStringBeforeRewriting().ToDictionary();
+
+ // Also read parameters from the fragment, if it's available.
+ // Typically the fragment is not available because the browser doesn't send it to a web server
+ // but this request may have been fabricated by an installed desktop app, in which case
+ // the fragment is available.
+ string fragment = request.GetPublicFacingUrl().Fragment;
+ if (!string.IsNullOrEmpty(fragment)) {
+ foreach (var pair in HttpUtility.ParseQueryString(fragment.Substring(1)).ToDictionary()) {
+ fields.Add(pair.Key, pair.Value);
+ }
+ }
+
+ MessageReceivingEndpoint recipient;
+ try {
+ recipient = request.GetRecipient();
+ } catch (ArgumentException ex) {
+ Logger.Messaging.WarnFormat("Unrecognized HTTP request: ", ex);
+ return null;
+ }
+
+ return (IDirectedProtocolMessage)this.Receive(fields, recipient);
+ }
+
+ /// <summary>
+ /// Queues a message for sending in the response stream where the fields
+ /// are sent in the response stream in querystring style.
+ /// </summary>
+ /// <param name="response">The message to send as a response.</param>
+ /// <returns>
+ /// The pending user agent redirect based message to be sent as an HttpResponse.
+ /// </returns>
+ /// <remarks>
+ /// This method implements spec OAuth V1.0 section 5.3.
+ /// </remarks>
+ protected override OutgoingWebResponse PrepareDirectResponse(IProtocolMessage response) {
+ // Clients don't ever send direct responses.
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Performs additional processing on an outgoing web request before it is sent to the remote server.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ protected override void PrepareHttpWebRequest(HttpWebRequest request) {
+ base.PrepareHttpWebRequest(request);
+
+ if (this.ClientCredentialApplicator != null) {
+ this.ClientCredentialApplicator.ApplyClientCredential(this.ClientIdentifier, request);
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs
index eda6bc1..5f377ae 100644
--- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs
@@ -26,13 +26,16 @@ namespace DotNetOpenAuth.OAuth2 {
/// </summary>
/// <param name="authorizationServer">The token issuer.</param>
/// <param name="clientIdentifier">The client identifier.</param>
- /// <param name="clientSecret">The client secret.</param>
- protected ClientBase(AuthorizationServerDescription authorizationServer, string clientIdentifier = null, string clientSecret = null) {
+ /// <param name="clientCredentialApplicator">
+ /// The tool to use to apply client credentials to authenticated requests to the Authorization Server.
+ /// May be <c>null</c> for clients with no secret or other means of authentication.
+ /// </param>
+ protected ClientBase(AuthorizationServerDescription authorizationServer, string clientIdentifier = null, ClientCredentialApplicator clientCredentialApplicator = null) {
Requires.NotNull(authorizationServer, "authorizationServer");
this.AuthorizationServer = authorizationServer;
this.Channel = new OAuth2ClientChannel();
this.ClientIdentifier = clientIdentifier;
- this.ClientSecret = clientSecret;
+ this.ClientCredentialApplicator = clientCredentialApplicator;
}
/// <summary>
@@ -50,12 +53,26 @@ namespace DotNetOpenAuth.OAuth2 {
/// <summary>
/// Gets or sets the identifier by which this client is known to the Authorization Server.
/// </summary>
- public string ClientIdentifier { get; set; }
+ public string ClientIdentifier {
+ get { return this.OAuthChannel.ClientIdentifier; }
+ set { this.OAuthChannel.ClientIdentifier = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the tool to use to apply client credentials to authenticated requests to the Authorization Server.
+ /// </summary>
+ /// <value>May be <c>null</c> if this client has no client secret.</value>
+ public ClientCredentialApplicator ClientCredentialApplicator {
+ get { return this.OAuthChannel.ClientCredentialApplicator; }
+ set { this.OAuthChannel.ClientCredentialApplicator = value; }
+ }
/// <summary>
- /// Gets or sets the client secret shared with the Authorization Server.
+ /// Gets the OAuth client channel.
/// </summary>
- public string ClientSecret { get; set; }
+ internal IOAuth2ChannelWithClient OAuthChannel {
+ get { return (IOAuth2ChannelWithClient)this.Channel; }
+ }
/// <summary>
/// Adds the necessary HTTP Authorization header to an HTTP request for protected resources
@@ -116,12 +133,13 @@ namespace DotNetOpenAuth.OAuth2 {
}
}
- var request = new AccessTokenRefreshRequest(this.AuthorizationServer) {
+ var request = new AccessTokenRefreshRequestC(this.AuthorizationServer) {
ClientIdentifier = this.ClientIdentifier,
- ClientSecret = this.ClientSecret,
RefreshToken = authorization.RefreshToken,
};
+ this.ApplyClientCredential(request);
+
var response = this.Channel.Request<AccessTokenSuccessResponse>(request);
UpdateAuthorizationWithResponse(authorization, response);
return true;
@@ -143,12 +161,13 @@ namespace DotNetOpenAuth.OAuth2 {
Requires.NotNull(scope, "scope");
Contract.Ensures(Contract.Result<IAuthorizationState>() != null);
- var request = new AccessTokenRefreshRequest(this.AuthorizationServer) {
+ var request = new AccessTokenRefreshRequestC(this.AuthorizationServer) {
ClientIdentifier = this.ClientIdentifier,
- ClientSecret = this.ClientSecret,
RefreshToken = refreshToken,
};
+ this.ApplyClientCredential(request);
+
var response = this.Channel.Request<AccessTokenSuccessResponse>(request);
var authorization = new AuthorizationState();
UpdateAuthorizationWithResponse(authorization, response);
@@ -248,12 +267,12 @@ namespace DotNetOpenAuth.OAuth2 {
Requires.NotNull(authorizationState, "authorizationState");
Requires.NotNull(authorizationSuccess, "authorizationSuccess");
- var accessTokenRequest = new AccessTokenAuthorizationCodeRequest(this.AuthorizationServer) {
+ var accessTokenRequest = new AccessTokenAuthorizationCodeRequestC(this.AuthorizationServer) {
ClientIdentifier = this.ClientIdentifier,
- ClientSecret = this.ClientSecret,
Callback = authorizationState.Callback,
AuthorizationCode = authorizationSuccess.AuthorizationCode,
};
+ this.ApplyClientCredential(accessTokenRequest);
IProtocolMessage accessTokenResponse = this.Channel.Request(accessTokenRequest);
var accessTokenSuccess = accessTokenResponse as AccessTokenSuccessResponse;
var failedAccessTokenResponse = accessTokenResponse as AccessTokenFailedResponse;
@@ -262,7 +281,28 @@ namespace DotNetOpenAuth.OAuth2 {
} else {
authorizationState.Delete();
string error = failedAccessTokenResponse != null ? failedAccessTokenResponse.Error : "(unknown)";
- ErrorUtilities.ThrowProtocol(OAuthStrings.CannotObtainAccessTokenWithReason, error);
+ ErrorUtilities.ThrowProtocol(ClientStrings.CannotObtainAccessTokenWithReason, error);
+ }
+ }
+
+ /// <summary>
+ /// Applies the default client authentication mechanism given a client secret.
+ /// </summary>
+ /// <param name="secret">The client secret. May be <c>null</c></param>
+ /// <returns>The client credential applicator.</returns>
+ protected static ClientCredentialApplicator DefaultSecretApplicator(string secret) {
+ return secret == null ? ClientCredentialApplicator.NoSecret() : ClientCredentialApplicator.NetworkCredential(secret);
+ }
+
+ /// <summary>
+ /// Applies any applicable client credential to an authenticated outbound request to the authorization server.
+ /// </summary>
+ /// <param name="request">The request to apply authentication information to.</param>
+ protected void ApplyClientCredential(AuthenticatedClientRequestBase request) {
+ Requires.NotNull(request, "request");
+
+ if (this.ClientCredentialApplicator != null) {
+ this.ClientCredentialApplicator.ApplyClientCredential(this.ClientIdentifier, request);
}
}
@@ -295,7 +335,7 @@ namespace DotNetOpenAuth.OAuth2 {
var authorizationState = new AuthorizationState(scopes);
request.ClientIdentifier = this.ClientIdentifier;
- request.ClientSecret = this.ClientSecret;
+ this.ApplyClientCredential(request);
request.Scope.UnionWith(authorizationState.Scope);
var response = this.Channel.Request(request);
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs
new file mode 100644
index 0000000..415c893
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs
@@ -0,0 +1,169 @@
+//-----------------------------------------------------------------------
+// <copyright file="ClientCredentialApplicator.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth2 {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuth2.Messages;
+
+ /// <summary>
+ /// A base class for extensions that apply client authentication to messages for the authorization server in specific ways.
+ /// </summary>
+ public abstract class ClientCredentialApplicator {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClientCredentialApplicator"/> class.
+ /// </summary>
+ protected ClientCredentialApplicator() {
+ }
+
+ /// <summary>
+ /// Transmits the secret the client shares with the authorization server as a parameter in the POST entity payload.
+ /// </summary>
+ /// <param name="clientSecret">The secret the client shares with the authorization server.</param>
+ /// <returns>The credential applicator to provide to the <see cref="ClientBase"/> instance.</returns>
+ public static ClientCredentialApplicator PostParameter(string clientSecret) {
+ Requires.NotNullOrEmpty(clientSecret, "clientSecret");
+ return new PostParameterApplicator(clientSecret);
+ }
+
+ /// <summary>
+ /// Transmits the client identifier and secret in the HTTP Authorization header via HTTP Basic authentication.
+ /// </summary>
+ /// <param name="credential">The client id and secret.</param>
+ /// <returns>The credential applicator to provide to the <see cref="ClientBase"/> instance.</returns>
+ public static ClientCredentialApplicator NetworkCredential(NetworkCredential credential) {
+ Requires.NotNull(credential, "credential");
+ return new NetworkCredentialApplicator(credential);
+ }
+
+ /// <summary>
+ /// Transmits the client identifier and secret in the HTTP Authorization header via HTTP Basic authentication.
+ /// </summary>
+ /// <param name="clientSecret">The secret the client shares with the authorization server.</param>
+ /// <returns>The credential applicator to provide to the <see cref="ClientBase"/> instance.</returns>
+ public static ClientCredentialApplicator NetworkCredential(string clientSecret) {
+ Requires.NotNullOrEmpty(clientSecret, "clientSecret");
+ return new NetworkCredentialApplicator(clientSecret);
+ }
+
+ /// <summary>
+ /// Never transmits a secret. Useful for anonymous clients or clients unable to keep a secret.
+ /// </summary>
+ /// <returns>The credential applicator to provide to the <see cref="ClientBase"/> instance.</returns>
+ public static ClientCredentialApplicator NoSecret() {
+ return null;
+ }
+
+ /// <summary>
+ /// Applies the client identifier and (when applicable) the client authentication to an outbound message.
+ /// </summary>
+ /// <param name="clientIdentifier">The identifier by which the authorization server should recognize this client.</param>
+ /// <param name="request">The outbound message to apply authentication information to.</param>
+ public virtual void ApplyClientCredential(string clientIdentifier, AuthenticatedClientRequestBase request) {
+ }
+
+ /// <summary>
+ /// Applies the client identifier and (when applicable) the client authentication to an outbound message.
+ /// </summary>
+ /// <param name="clientIdentifier">The identifier by which the authorization server should recognize this client.</param>
+ /// <param name="request">The outbound message to apply authentication information to.</param>
+ public virtual void ApplyClientCredential(string clientIdentifier, HttpWebRequest request) {
+ }
+
+ /// <summary>
+ /// Authenticates the client via HTTP Basic.
+ /// </summary>
+ private class NetworkCredentialApplicator : ClientCredentialApplicator {
+ /// <summary>
+ /// The client identifier and secret.
+ /// </summary>
+ private readonly NetworkCredential credential;
+
+ /// <summary>
+ /// The client secret.
+ /// </summary>
+ private readonly string clientSecret;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NetworkCredentialApplicator"/> class.
+ /// </summary>
+ /// <param name="clientSecret">The client secret.</param>
+ internal NetworkCredentialApplicator(string clientSecret) {
+ Requires.NotNullOrEmpty(clientSecret, "clientSecret");
+ this.clientSecret = clientSecret;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NetworkCredentialApplicator"/> class.
+ /// </summary>
+ /// <param name="credential">The client credential.</param>
+ internal NetworkCredentialApplicator(NetworkCredential credential) {
+ Requires.NotNull(credential, "credential");
+ this.credential = credential;
+ }
+
+ /// <summary>
+ /// Applies the client identifier and (when applicable) the client authentication to an outbound message.
+ /// </summary>
+ /// <param name="clientIdentifier">The identifier by which the authorization server should recognize this client.</param>
+ /// <param name="request">The outbound message to apply authentication information to.</param>
+ public override void ApplyClientCredential(string clientIdentifier, AuthenticatedClientRequestBase request) {
+ // When using network credentials, the client authentication is not done as standard message parts.
+ request.ClientIdentifier = null;
+ request.ClientSecret = null;
+ }
+
+ /// <summary>
+ /// Applies the client identifier and (when applicable) the client authentication to an outbound message.
+ /// </summary>
+ /// <param name="clientIdentifier">The identifier by which the authorization server should recognize this client.</param>
+ /// <param name="request">The outbound message to apply authentication information to.</param>
+ public override void ApplyClientCredential(string clientIdentifier, HttpWebRequest request) {
+ if (clientIdentifier != null) {
+ if (this.credential != null && this.credential.UserName == clientIdentifier) {
+ ErrorUtilities.VerifyHost(false, "Client identifiers \"{0}\" and \"{1}\" do not match.", this.credential.UserName, clientIdentifier);
+ }
+
+ request.Credentials = this.credential ?? new NetworkCredential(clientIdentifier, this.clientSecret);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Authenticates the client via a client_secret parameter in the message.
+ /// </summary>
+ private class PostParameterApplicator : ClientCredentialApplicator {
+ /// <summary>
+ /// The client secret.
+ /// </summary>
+ private readonly string secret;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PostParameterApplicator"/> class.
+ /// </summary>
+ /// <param name="clientSecret">The client secret.</param>
+ internal PostParameterApplicator(string clientSecret) {
+ Requires.NotNullOrEmpty(clientSecret, "clientSecret");
+ this.secret = clientSecret;
+ }
+
+ /// <summary>
+ /// Applies the client identifier and (when applicable) the client authentication to an outbound message.
+ /// </summary>
+ /// <param name="clientIdentifier">The identifier by which the authorization server should recognize this client.</param>
+ /// <param name="request">The outbound message to apply authentication information to.</param>
+ public override void ApplyClientCredential(string clientIdentifier, AuthenticatedClientRequestBase request) {
+ if (clientIdentifier != null) {
+ request.ClientSecret = this.secret;
+ }
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientStrings.Designer.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientStrings.Designer.cs
index 9564704..87acfdf 100644
--- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientStrings.Designer.cs
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientStrings.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
-// Runtime Version:4.0.30319.17614
+// Runtime Version:4.0.30319.17622
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -88,11 +88,20 @@ namespace DotNetOpenAuth.OAuth2 {
}
/// <summary>
- /// Looks up a localized string similar to The property {0} must be set before this operation is allowed..
+ /// Looks up a localized string similar to Failed to obtain access token. Authorization Server reports reason: {0}.
/// </summary>
- internal static string RequiredPropertyNotYetPreset {
+ internal static string CannotObtainAccessTokenWithReason {
get {
- return ResourceManager.GetString("RequiredPropertyNotYetPreset", resourceCulture);
+ return ResourceManager.GetString("CannotObtainAccessTokenWithReason", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Unexpected response Content-Type {0}.
+ /// </summary>
+ internal static string UnexpectedResponseContentType {
+ get {
+ return ResourceManager.GetString("UnexpectedResponseContentType", resourceCulture);
}
}
}
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientStrings.resx b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientStrings.resx
index 0a41e42..5facbc4 100644
--- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientStrings.resx
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientStrings.resx
@@ -112,10 +112,10 @@
<value>2.0</value>
</resheader>
<resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AccessTokenRefreshFailed" xml:space="preserve">
<value>Access token has expired and cannot be automatically refreshed.</value>
@@ -127,7 +127,10 @@
<value>Unexpected OAuth authorization response received with callback and client state that does not match an expected value.</value>
<comment>The error message generated when detecting a mismatch between the state sent to the authorization server originally and what we got back with successful authorization, or that the user sessions were not identical between the two requests, suggesting XSRF or other attack on the user (victim).</comment>
</data>
- <data name="RequiredPropertyNotYetPreset" xml:space="preserve">
- <value>The property {0} must be set before this operation is allowed.</value>
+ <data name="CannotObtainAccessTokenWithReason" xml:space="preserve">
+ <value>Failed to obtain access token. Authorization Server reports reason: {0}</value>
+ </data>
+ <data name="UnexpectedResponseContentType" xml:space="preserve">
+ <value>Unexpected response Content-Type {0}</value>
</data>
</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/IAuthorizationState.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/IAuthorizationState.cs
new file mode 100644
index 0000000..f38df9a
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/IAuthorizationState.cs
@@ -0,0 +1,67 @@
+//-----------------------------------------------------------------------
+// <copyright file="IAuthorizationState.cs" company="Outercurve Foundation">
+// Copyright (c) Outercurve Foundation. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth2 {
+ using System;
+ using System.Collections.Generic;
+
+ /// <summary>
+ /// Provides access to a persistent object that tracks the state of an authorization.
+ /// </summary>
+ public interface IAuthorizationState {
+ /// <summary>
+ /// Gets or sets the callback URL used to obtain authorization.
+ /// </summary>
+ /// <value>The callback URL.</value>
+ Uri Callback { get; set; }
+
+ /// <summary>
+ /// Gets or sets the long-lived token used to renew the short-lived <see cref="AccessToken"/>.
+ /// </summary>
+ /// <value>The refresh token.</value>
+ string RefreshToken { get; set; }
+
+ /// <summary>
+ /// Gets or sets the access token.
+ /// </summary>
+ /// <value>The access token.</value>
+ string AccessToken { get; set; }
+
+ /// <summary>
+ /// Gets or sets the access token issue date UTC.
+ /// </summary>
+ /// <value>The access token issue date UTC.</value>
+ DateTime? AccessTokenIssueDateUtc { get; set; }
+
+ /// <summary>
+ /// Gets or sets the access token UTC expiration date.
+ /// </summary>
+ DateTime? AccessTokenExpirationUtc { get; set; }
+
+ /// <summary>
+ /// Gets the scope the token is (to be) authorized for.
+ /// </summary>
+ /// <value>The scope.</value>
+ HashSet<string> Scope { get; }
+
+ /// <summary>
+ /// Deletes this authorization, including access token and refresh token where applicable.
+ /// </summary>
+ /// <remarks>
+ /// This method is invoked when an authorization attempt fails, is rejected, is revoked, or
+ /// expires and cannot be renewed.
+ /// </remarks>
+ void Delete();
+
+ /// <summary>
+ /// Saves any changes made to this authorization object's properties.
+ /// </summary>
+ /// <remarks>
+ /// This method is invoked after DotNetOpenAuth changes any property.
+ /// </remarks>
+ void SaveChanges();
+ }
+} \ No newline at end of file
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/IClientAuthorizationTracker.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/IClientAuthorizationTracker.cs
new file mode 100644
index 0000000..73b7a44
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/IClientAuthorizationTracker.cs
@@ -0,0 +1,53 @@
+//-----------------------------------------------------------------------
+// <copyright file="IClientAuthorizationTracker.cs" company="Outercurve Foundation">
+// Copyright (c) Outercurve Foundation. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth2 {
+ using System;
+ using System.Diagnostics.Contracts;
+
+ /// <summary>
+ /// A token manager implemented by some clients to assist in tracking authorization state.
+ /// </summary>
+ [ContractClass(typeof(IClientAuthorizationTrackerContract))]
+ public interface IClientAuthorizationTracker {
+ /// <summary>
+ /// Gets the state of the authorization for a given callback URL and client state.
+ /// </summary>
+ /// <param name="callbackUrl">The callback URL.</param>
+ /// <param name="clientState">State of the client stored at the beginning of an authorization request.</param>
+ /// <returns>The authorization state; may be <c>null</c> if no authorization state matches.</returns>
+ IAuthorizationState GetAuthorizationState(Uri callbackUrl, string clientState);
+ }
+
+ /// <summary>
+ /// Contract class for the <see cref="IClientAuthorizationTracker"/> interface.
+ /// </summary>
+ [ContractClassFor(typeof(IClientAuthorizationTracker))]
+ internal abstract class IClientAuthorizationTrackerContract : IClientAuthorizationTracker {
+ /// <summary>
+ /// Prevents a default instance of the <see cref="IClientAuthorizationTrackerContract"/> class from being created.
+ /// </summary>
+ private IClientAuthorizationTrackerContract() {
+ }
+
+ #region IClientTokenManager Members
+
+ /// <summary>
+ /// Gets the state of the authorization for a given callback URL and client state.
+ /// </summary>
+ /// <param name="callbackUrl">The callback URL.</param>
+ /// <param name="clientState">State of the client stored at the beginning of an authorization request.</param>
+ /// <returns>
+ /// The authorization state; may be <c>null</c> if no authorization state matches.
+ /// </returns>
+ IAuthorizationState IClientAuthorizationTracker.GetAuthorizationState(Uri callbackUrl, string clientState) {
+ Requires.NotNull(callbackUrl, "callbackUrl");
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/AccessTokenAuthorizationCodeRequestC.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/AccessTokenAuthorizationCodeRequestC.cs
new file mode 100644
index 0000000..ebfb2e8
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/AccessTokenAuthorizationCodeRequestC.cs
@@ -0,0 +1,27 @@
+//-----------------------------------------------------------------------
+// <copyright file="AccessTokenAuthorizationCodeRequestC.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth2.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// A request from a Client to an Authorization Server to exchange an authorization code for an access token,
+ /// and (at the authorization server's option) a refresh token.
+ /// </summary>
+ internal class AccessTokenAuthorizationCodeRequestC : AccessTokenAuthorizationCodeRequest {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AccessTokenAuthorizationCodeRequestC"/> class.
+ /// </summary>
+ /// <param name="authorizationServer">The authorization server.</param>
+ internal AccessTokenAuthorizationCodeRequestC(AuthorizationServerDescription authorizationServer)
+ : base(authorizationServer.TokenEndpoint, authorizationServer.Version) {
+ Requires.NotNull(authorizationServer, "authorizationServer");
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/AccessTokenRefreshRequestC.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/AccessTokenRefreshRequestC.cs
new file mode 100644
index 0000000..25da3dc
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/AccessTokenRefreshRequestC.cs
@@ -0,0 +1,26 @@
+//-----------------------------------------------------------------------
+// <copyright file="AccessTokenRefreshRequestC.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth2.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// A request from the client to the token endpoint for a new access token
+ /// in exchange for a refresh token that the client has previously obtained.
+ /// </summary>
+ internal class AccessTokenRefreshRequestC : AccessTokenRefreshRequest {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AccessTokenRefreshRequestC"/> class.
+ /// </summary>
+ /// <param name="authorizationServer">The authorization server.</param>
+ internal AccessTokenRefreshRequestC(AuthorizationServerDescription authorizationServer)
+ : base(authorizationServer.TokenEndpoint, authorizationServer.Version) {
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/EndUserAuthorizationImplicitRequestC.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/EndUserAuthorizationImplicitRequestC.cs
new file mode 100644
index 0000000..78bf48e
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/EndUserAuthorizationImplicitRequestC.cs
@@ -0,0 +1,28 @@
+//-----------------------------------------------------------------------
+// <copyright file="EndUserAuthorizationImplicitRequestC.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth2.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// A message sent by a web application Client to the AuthorizationServer
+ /// via the user agent to obtain authorization from the user and prepare
+ /// to issue an access token to the client if permission is granted.
+ /// </summary>
+ [Serializable]
+ internal class EndUserAuthorizationImplicitRequestC : EndUserAuthorizationImplicitRequest {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EndUserAuthorizationImplicitRequestC"/> class.
+ /// </summary>
+ /// <param name="authorizationServer">The authorization server.</param>
+ internal EndUserAuthorizationImplicitRequestC(AuthorizationServerDescription authorizationServer)
+ : base(authorizationServer.AuthorizationEndpoint, authorizationServer.Version) {
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/EndUserAuthorizationRequestC.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/EndUserAuthorizationRequestC.cs
new file mode 100644
index 0000000..7c06897
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/EndUserAuthorizationRequestC.cs
@@ -0,0 +1,31 @@
+//-----------------------------------------------------------------------
+// <copyright file="EndUserAuthorizationRequestC.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth2.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// A message sent by a web application Client to the AuthorizationServer
+ /// via the user agent to obtain authorization from the user and prepare
+ /// to issue an access token to the client if permission is granted.
+ /// </summary>
+ [Serializable]
+ internal class EndUserAuthorizationRequestC : EndUserAuthorizationRequest {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EndUserAuthorizationRequestC"/> class.
+ /// </summary>
+ /// <param name="authorizationServer">The authorization server.</param>
+ internal EndUserAuthorizationRequestC(AuthorizationServerDescription authorizationServer)
+ : base(authorizationServer.AuthorizationEndpoint, authorizationServer.Version) {
+ Requires.NotNull(authorizationServer, "authorizationServer");
+ Requires.True(authorizationServer.Version != null, "authorizationServer");
+ Requires.True(authorizationServer.AuthorizationEndpoint != null, "authorizationServer");
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs
index c29d167..edde2a9 100644
--- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs
@@ -27,7 +27,7 @@ namespace DotNetOpenAuth.OAuth2 {
/// <param name="clientIdentifier">The client identifier.</param>
/// <param name="clientSecret">The client secret.</param>
public UserAgentClient(AuthorizationServerDescription authorizationServer, string clientIdentifier = null, string clientSecret = null)
- : base(authorizationServer, clientIdentifier, clientSecret) {
+ : this(authorizationServer, clientIdentifier, DefaultSecretApplicator(clientSecret)) {
}
/// <summary>
@@ -38,12 +38,39 @@ namespace DotNetOpenAuth.OAuth2 {
/// <param name="clientIdentifier">The client identifier.</param>
/// <param name="clientSecret">The client secret.</param>
public UserAgentClient(Uri authorizationEndpoint, Uri tokenEndpoint, string clientIdentifier = null, string clientSecret = null)
- : this(new AuthorizationServerDescription { AuthorizationEndpoint = authorizationEndpoint, TokenEndpoint = tokenEndpoint }, clientIdentifier, clientSecret) {
+ : this(authorizationEndpoint, tokenEndpoint, clientIdentifier, DefaultSecretApplicator(clientSecret)) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserAgentClient"/> class.
+ /// </summary>
+ /// <param name="authorizationEndpoint">The authorization endpoint.</param>
+ /// <param name="tokenEndpoint">The token endpoint.</param>
+ /// <param name="clientIdentifier">The client identifier.</param>
+ /// <param name="clientCredentialApplicator">
+ /// The tool to use to apply client credentials to authenticated requests to the Authorization Server.
+ /// May be <c>null</c> for clients with no secret or other means of authentication.
+ /// </param>
+ public UserAgentClient(Uri authorizationEndpoint, Uri tokenEndpoint, string clientIdentifier, ClientCredentialApplicator clientCredentialApplicator)
+ : this(new AuthorizationServerDescription { AuthorizationEndpoint = authorizationEndpoint, TokenEndpoint = tokenEndpoint }, clientIdentifier, clientCredentialApplicator) {
Requires.NotNull(authorizationEndpoint, "authorizationEndpoint");
Requires.NotNull(tokenEndpoint, "tokenEndpoint");
}
/// <summary>
+ /// Initializes a new instance of the <see cref="UserAgentClient"/> class.
+ /// </summary>
+ /// <param name="authorizationServer">The token issuer.</param>
+ /// <param name="clientIdentifier">The client identifier.</param>
+ /// <param name="clientCredentialApplicator">
+ /// The tool to use to apply client credentials to authenticated requests to the Authorization Server.
+ /// May be <c>null</c> for clients with no secret or other means of authentication.
+ /// </param>
+ public UserAgentClient(AuthorizationServerDescription authorizationServer, string clientIdentifier, ClientCredentialApplicator clientCredentialApplicator)
+ : base(authorizationServer, clientIdentifier, clientCredentialApplicator) {
+ }
+
+ /// <summary>
/// Generates a URL that the user's browser can be directed to in order to authorize
/// this client to access protected data at some resource server.
/// </summary>
@@ -151,7 +178,7 @@ namespace DotNetOpenAuth.OAuth2 {
authorization.Callback = new Uri("http://localhost/");
}
- var request = implicitResponseType ? new EndUserAuthorizationImplicitRequest(this.AuthorizationServer) : new EndUserAuthorizationRequest(this.AuthorizationServer);
+ var request = implicitResponseType ? (EndUserAuthorizationRequest)new EndUserAuthorizationImplicitRequestC(this.AuthorizationServer) : new EndUserAuthorizationRequestC(this.AuthorizationServer);
request.ClientIdentifier = this.ClientIdentifier;
request.Callback = authorization.Callback;
request.ClientState = state;
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs
index 0429dcb..939d1df 100644
--- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs
@@ -26,7 +26,20 @@ namespace DotNetOpenAuth.OAuth2 {
/// <param name="clientIdentifier">The client identifier.</param>
/// <param name="clientSecret">The client secret.</param>
public WebServerClient(AuthorizationServerDescription authorizationServer, string clientIdentifier = null, string clientSecret = null)
- : base(authorizationServer, clientIdentifier, clientSecret) {
+ : this(authorizationServer, clientIdentifier, DefaultSecretApplicator(clientSecret)) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="WebServerClient"/> class.
+ /// </summary>
+ /// <param name="authorizationServer">The authorization server.</param>
+ /// <param name="clientIdentifier">The client identifier.</param>
+ /// <param name="clientCredentialApplicator">
+ /// The tool to use to apply client credentials to authenticated requests to the Authorization Server.
+ /// May be <c>null</c> for clients with no secret or other means of authentication.
+ /// </param>
+ public WebServerClient(AuthorizationServerDescription authorizationServer, string clientIdentifier, ClientCredentialApplicator clientCredentialApplicator)
+ : base(authorizationServer, clientIdentifier, clientCredentialApplicator) {
}
/// <summary>
@@ -68,7 +81,7 @@ namespace DotNetOpenAuth.OAuth2 {
public OutgoingWebResponse PrepareRequestUserAuthorization(IAuthorizationState authorization) {
Requires.NotNull(authorization, "authorization");
Requires.ValidState(authorization.Callback != null || (HttpContext.Current != null && HttpContext.Current.Request != null), MessagingStrings.HttpContextRequired);
- Requires.ValidState(!string.IsNullOrEmpty(this.ClientIdentifier), ClientStrings.RequiredPropertyNotYetPreset, "ClientIdentifier");
+ Requires.ValidState(!string.IsNullOrEmpty(this.ClientIdentifier), Strings.RequiredPropertyNotYetPreset, "ClientIdentifier");
Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null);
if (authorization.Callback == null) {
@@ -78,7 +91,7 @@ namespace DotNetOpenAuth.OAuth2 {
authorization.SaveChanges();
}
- var request = new EndUserAuthorizationRequest(this.AuthorizationServer) {
+ var request = new EndUserAuthorizationRequestC(this.AuthorizationServer) {
ClientIdentifier = this.ClientIdentifier,
Callback = authorization.Callback,
};
@@ -105,8 +118,8 @@ namespace DotNetOpenAuth.OAuth2 {
/// <param name="request">The incoming HTTP request that may carry an authorization response.</param>
/// <returns>The authorization state that contains the details of the authorization.</returns>
public IAuthorizationState ProcessUserAuthorization(HttpRequestBase request = null) {
- Requires.ValidState(!string.IsNullOrEmpty(this.ClientIdentifier), ClientStrings.RequiredPropertyNotYetPreset, "ClientIdentifier");
- Requires.ValidState(!string.IsNullOrEmpty(this.ClientSecret), ClientStrings.RequiredPropertyNotYetPreset, "ClientSecret");
+ Requires.ValidState(!string.IsNullOrEmpty(this.ClientIdentifier), Strings.RequiredPropertyNotYetPreset, "ClientIdentifier");
+ Requires.ValidState(this.ClientCredentialApplicator != null, Strings.RequiredPropertyNotYetPreset, "ClientCredentialApplicator");
if (request == null) {
request = this.Channel.GetRequestFromContext();