summaryrefslogtreecommitdiffstats
path: root/src/DotNetOpenAuth.OAuth/OAuth/WebConsumer.cs
blob: de37b80e9df8cd1859ab0f98507314d945c31053 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//-----------------------------------------------------------------------
// <copyright file="WebConsumer.cs" company="Andrew Arnott">
//     Copyright (c) Andrew Arnott. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace DotNetOpenAuth.OAuth {
	using System;
	using System.Collections.Generic;
	using System.Diagnostics.Contracts;
	using System.Web;
	using DotNetOpenAuth.Messaging;
	using DotNetOpenAuth.OAuth.ChannelElements;
	using DotNetOpenAuth.OAuth.Messages;
	using DotNetOpenAuth.OpenId.Extensions.OAuth;
	using DotNetOpenAuth.OpenId.RelyingParty;

	/// <summary>
	/// A website or application that uses OAuth to access the Service Provider on behalf of the User.
	/// </summary>
	/// <remarks>
	/// The methods on this class are thread-safe.  Provided the properties are set and not changed
	/// afterward, a single instance of this class may be used by an entire web application safely.
	/// </remarks>
	public class WebConsumer : ConsumerBase {
		/// <summary>
		/// Initializes a new instance of the <see cref="WebConsumer"/> class.
		/// </summary>
		/// <param name="serviceDescription">The endpoints and behavior of the Service Provider.</param>
		/// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param>
		public WebConsumer(ServiceProviderDescription serviceDescription, IConsumerTokenManager tokenManager)
			: base(serviceDescription, tokenManager) {
		}

		/// <summary>
		/// Begins an OAuth authorization request and redirects the user to the Service Provider
		/// to provide that authorization.  Upon successful authorization, the user is redirected
		/// back to the current page.
		/// </summary>
		/// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
		/// <remarks>
		/// Requires HttpContext.Current.
		/// </remarks>
		public UserAuthorizationRequest PrepareRequestUserAuthorization() {
			Uri callback = this.Channel.GetRequestFromContext().UrlBeforeRewriting.StripQueryArgumentsWithPrefix(Protocol.ParameterPrefix);
			return this.PrepareRequestUserAuthorization(callback, null, null);
		}

		/// <summary>
		/// Prepares an OAuth message that begins an authorization request that will 
		/// redirect the user to the Service Provider to provide that authorization.
		/// </summary>
		/// <param name="callback">
		/// An optional Consumer URL that the Service Provider should redirect the 
		/// User Agent to upon successful authorization.
		/// </param>
		/// <param name="requestParameters">Extra parameters to add to the request token message.  Optional.</param>
		/// <param name="redirectParameters">Extra parameters to add to the redirect to Service Provider message.  Optional.</param>
		/// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
		public UserAuthorizationRequest PrepareRequestUserAuthorization(Uri callback, IDictionary<string, string> requestParameters, IDictionary<string, string> redirectParameters) {
			string token;
			return this.PrepareRequestUserAuthorization(callback, requestParameters, redirectParameters, out token);
		}

		/// <summary>
		/// Processes an incoming authorization-granted message from an SP and obtains an access token.
		/// </summary>
		/// <returns>The access token, or null if no incoming authorization message was recognized.</returns>
		/// <remarks>
		/// Requires HttpContext.Current.
		/// </remarks>
		public AuthorizedTokenResponse ProcessUserAuthorization() {
			return this.ProcessUserAuthorization(this.Channel.GetRequestFromContext());
		}

		/// <summary>
		/// Attaches an OAuth authorization request to an outgoing OpenID authentication request.
		/// </summary>
		/// <param name="openIdAuthenticationRequest">The OpenID authentication request.</param>
		/// <param name="scope">The scope of access that is requested of the service provider.</param>
		public void AttachAuthorizationRequest(IAuthenticationRequest openIdAuthenticationRequest, string scope) {
			Contract.Requires<ArgumentNullException>(openIdAuthenticationRequest != null);

			var authorizationRequest = new AuthorizationRequest {
				Consumer = this.ConsumerKey,
				Scope = scope,
			};

			openIdAuthenticationRequest.AddExtension(authorizationRequest);
		}

		/// <summary>
		/// Processes an incoming authorization-granted message from an SP and obtains an access token.
		/// </summary>
		/// <param name="openIdAuthenticationResponse">The OpenID authentication response that may be carrying an authorized request token.</param>
		/// <returns>
		/// The access token, or null if OAuth authorization was denied by the user or service provider.
		/// </returns>
		/// <remarks>
		/// The access token, if granted, is automatically stored in the <see cref="ConsumerBase.TokenManager"/>.
		/// The token manager instance must implement <see cref="IOpenIdOAuthTokenManager"/>.
		/// </remarks>
		public AuthorizedTokenResponse ProcessUserAuthorization(IAuthenticationResponse openIdAuthenticationResponse) {
			Contract.Requires<ArgumentNullException>(openIdAuthenticationResponse != null);
			Contract.Requires<InvalidOperationException>(this.TokenManager is IOpenIdOAuthTokenManager);
			var openidTokenManager = this.TokenManager as IOpenIdOAuthTokenManager;
			ErrorUtilities.VerifyOperation(openidTokenManager != null, OAuthStrings.OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface, typeof(IOpenIdOAuthTokenManager).FullName);

			// The OAuth extension is only expected in positive assertion responses.
			if (openIdAuthenticationResponse.Status != AuthenticationStatus.Authenticated) {
				return null;
			}

			// Retrieve the OAuth extension
			var positiveAuthorization = openIdAuthenticationResponse.GetExtension<AuthorizationApprovedResponse>();
			if (positiveAuthorization == null) {
				return null;
			}

			// Prepare a message to exchange the request token for an access token.
			// We are careful to use a v1.0 message version so that the oauth_verifier is not required.
			var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint, Protocol.V10.Version) {
				RequestToken = positiveAuthorization.RequestToken,
				ConsumerKey = this.ConsumerKey,
			};

			// Retrieve the access token and store it in the token manager.
			openidTokenManager.StoreOpenIdAuthorizedRequestToken(this.ConsumerKey, positiveAuthorization);
			var grantAccess = this.Channel.Request<AuthorizedTokenResponse>(requestAccess);
			this.TokenManager.ExpireRequestTokenAndStoreNewAccessToken(this.ConsumerKey, positiveAuthorization.RequestToken, grantAccess.AccessToken, grantAccess.TokenSecret);

			// Provide the caller with the access token so it may be associated with the user
			// that is logging in.
			return grantAccess;
		}

		/// <summary>
		/// Processes an incoming authorization-granted message from an SP and obtains an access token.
		/// </summary>
		/// <param name="request">The incoming HTTP request.</param>
		/// <returns>The access token, or null if no incoming authorization message was recognized.</returns>
		public AuthorizedTokenResponse ProcessUserAuthorization(HttpRequestInfo request) {
			Contract.Requires<ArgumentNullException>(request != null);

			UserAuthorizationResponse authorizationMessage;
			if (this.Channel.TryReadFromRequest<UserAuthorizationResponse>(request, out authorizationMessage)) {
				string requestToken = authorizationMessage.RequestToken;
				string verifier = authorizationMessage.VerificationCode;
				return this.ProcessUserAuthorization(requestToken, verifier);
			} else {
				return null;
			}
		}
	}
}