Added strong-typed request token message to sample.
[dotnetoauth.git] / src / DotNetOAuth / Consumer.cs
blob881eec8b58e85ce8dee0b26417c2e059a823033d
1 //-----------------------------------------------------------------------
2 // <copyright file="Consumer.cs" company="Andrew Arnott">
3 // Copyright (c) Andrew Arnott. All rights reserved.
4 // </copyright>
5 //-----------------------------------------------------------------------
7 namespace DotNetOAuth {
8 using System;
9 using System.Collections.Generic;
10 using System.Net;
11 using System.Web;
12 using DotNetOAuth.ChannelElements;
13 using DotNetOAuth.Messages;
14 using DotNetOAuth.Messaging;
15 using DotNetOAuth.Messaging.Bindings;
17 /// <summary>
18 /// A website or application that uses OAuth to access the Service Provider on behalf of the User.
19 /// </summary>
20 /// <remarks>
21 /// The methods on this class are thread-safe. Provided the properties are set and not changed
22 /// afterward, a single instance of this class may be used by an entire web application safely.
23 /// </remarks>
24 public class Consumer {
25 /// <summary>
26 /// Initializes a new instance of the <see cref="Consumer"/> class.
27 /// </summary>
28 /// <param name="serviceDescription">The endpoints and behavior of the Service Provider.</param>
29 /// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param>
30 public Consumer(ServiceProviderDescription serviceDescription, ITokenManager tokenManager) {
31 if (serviceDescription == null) {
32 throw new ArgumentNullException("serviceDescription");
34 if (tokenManager == null) {
35 throw new ArgumentNullException("tokenManager");
38 this.WebRequestHandler = new StandardWebRequestHandler();
39 ITamperProtectionChannelBindingElement signingElement = serviceDescription.CreateTamperProtectionElement();
40 INonceStore store = new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge);
41 this.Channel = new OAuthChannel(signingElement, store, new OAuthConsumerMessageTypeProvider(tokenManager), this.WebRequestHandler);
42 this.ServiceProvider = serviceDescription;
43 this.TokenManager = tokenManager;
46 /// <summary>
47 /// Gets or sets the Consumer Key used to communicate with the Service Provider.
48 /// </summary>
49 public string ConsumerKey { get; set; }
51 /// <summary>
52 /// Gets or sets the Consumer Secret used to communicate with the Service Provider.
53 /// </summary>
54 public string ConsumerSecret { get; set; }
56 /// <summary>
57 /// Gets the Service Provider that will be accessed.
58 /// </summary>
59 public ServiceProviderDescription ServiceProvider { get; private set; }
61 /// <summary>
62 /// Gets the persistence store for tokens and secrets.
63 /// </summary>
64 public ITokenManager TokenManager { get; private set; }
66 /// <summary>
67 /// Gets or sets the object that processes <see cref="HttpWebRequest"/>s.
68 /// </summary>
69 /// <remarks>
70 /// This defaults to a straightforward implementation, but can be set
71 /// to a mock object for testing purposes.
72 /// </remarks>
73 internal IWebRequestHandler WebRequestHandler { get; set; }
75 /// <summary>
76 /// Gets or sets the channel to use for sending/receiving messages.
77 /// </summary>
78 internal OAuthChannel Channel { get; set; }
80 /// <summary>
81 /// Begins an OAuth authorization request and redirects the user to the Service Provider
82 /// to provide that authorization. Upon successful authorization, the user is redirected
83 /// back to the current page.
84 /// </summary>
85 /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
86 /// <remarks>
87 /// Requires HttpContext.Current.
88 /// </remarks>
89 public Response RequestUserAuthorization() {
90 Uri callback = MessagingUtilities.GetRequestUrlFromContext().StripQueryArgumentsWithPrefix(Protocol.Default.ParameterPrefix);
91 return this.RequestUserAuthorization(callback, null, null);
94 /// <summary>
95 /// Begins an OAuth authorization request and redirects the user to the Service Provider
96 /// to provide that authorization.
97 /// </summary>
98 /// <param name="callback">
99 /// An optional Consumer URL that the Service Provider should redirect the
100 /// User Agent to upon successful authorization.
101 /// </param>
102 /// <param name="requestParameters">Extra parameters to add to the request token message. Optional.</param>
103 /// <param name="redirectParameters">Extra parameters to add to the redirect to Service Provider message. Optional.</param>
104 /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
105 public Response RequestUserAuthorization(Uri callback, IDictionary<string, string> requestParameters, IDictionary<string, string> redirectParameters) {
106 string token;
107 return this.RequestUserAuthorization(callback, requestParameters, redirectParameters, out token);
110 /// <summary>
111 /// Begins an OAuth authorization request from a desktop client app.
112 /// </summary>
113 /// <param name="requestParameters">Extra parameters to add to the request token message. Optional.</param>
114 /// <param name="redirectParameters">Extra parameters to add to the redirect to Service Provider message. Optional.</param>
115 /// <param name="requestToken">The request token that must be exchanged for an access token after the user has provided authorization.</param>
116 /// <returns>The URL to open a browser window to allow the user to provide authorization.</returns>
117 public Uri RequestUserAuthorization(IDictionary<string, string> requestParameters, IDictionary<string, string> redirectParameters, out string requestToken) {
118 return this.RequestUserAuthorization(null, requestParameters, redirectParameters, out requestToken).DirectUriRequest;
121 /// <summary>
122 /// Processes an incoming authorization-granted message from an SP and obtains an access token.
123 /// </summary>
124 /// <returns>The access token, or null if no incoming authorization message was recognized.</returns>
125 /// <remarks>
126 /// Requires HttpContext.Current.
127 /// </remarks>
128 public GrantAccessTokenMessage ProcessUserAuthorization() {
129 return this.ProcessUserAuthorization(this.Channel.GetRequestFromContext());
132 /// <summary>
133 /// Processes an incoming authorization-granted message from an SP and obtains an access token.
134 /// </summary>
135 /// <param name="request">The incoming HTTP request.</param>
136 /// <returns>The access token, or null if no incoming authorization message was recognized.</returns>
137 public GrantAccessTokenMessage ProcessUserAuthorization(HttpRequest request) {
138 return this.ProcessUserAuthorization(new HttpRequestInfo(request));
141 /// <summary>
142 /// Exchanges a given request token for access token.
143 /// </summary>
144 /// <param name="requestToken">The request token that the user has authorized.</param>
145 /// <returns>The access token assigned by the Service Provider.</returns>
146 public GrantAccessTokenMessage ProcessUserAuthorization(string requestToken) {
147 string requestTokenSecret = this.TokenManager.GetTokenSecret(requestToken);
148 var requestAccess = new RequestAccessTokenMessage(this.ServiceProvider.AccessTokenEndpoint) {
149 RequestToken = requestToken,
150 TokenSecret = requestTokenSecret,
151 ConsumerKey = this.ConsumerKey,
152 ConsumerSecret = this.ConsumerSecret,
154 var grantAccess = this.Channel.Request<GrantAccessTokenMessage>(requestAccess);
155 this.TokenManager.ExpireRequestTokenAndStoreNewAccessToken(this.ConsumerKey, requestToken, grantAccess.AccessToken, grantAccess.TokenSecret);
156 return grantAccess;
159 /// <summary>
160 /// Creates a web request prepared with OAuth authorization
161 /// that may be further tailored by adding parameters by the caller.
162 /// </summary>
163 /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
164 /// <param name="accessToken">The access token that permits access to the protected resource.</param>
165 /// <returns>The initialized WebRequest object.</returns>
166 public WebRequest CreateAuthorizedRequest(MessageReceivingEndpoint endpoint, string accessToken) {
167 IDirectedProtocolMessage message = this.CreateAuthorizedRequestInternal(endpoint, accessToken);
168 HttpWebRequest wr = this.Channel.InitializeRequest(message);
169 return wr;
172 /// <summary>
173 /// Creates a web request prepared with OAuth authorization
174 /// that may be further tailored by adding parameters by the caller.
175 /// </summary>
176 /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
177 /// <param name="accessToken">The access token that permits access to the protected resource.</param>
178 /// <returns>The initialized WebRequest object.</returns>
179 /// <exception cref="WebException">Thrown if the request fails for any reason after it is sent to the Service Provider.</exception>
180 public Response SendAuthorizedRequest(MessageReceivingEndpoint endpoint, string accessToken) {
181 IDirectedProtocolMessage message = this.CreateAuthorizedRequestInternal(endpoint, accessToken);
182 HttpWebRequest wr = this.Channel.InitializeRequest(message);
183 return this.WebRequestHandler.GetResponse(wr);
186 /// <summary>
187 /// Begins an OAuth authorization request and redirects the user to the Service Provider
188 /// to provide that authorization.
189 /// </summary>
190 /// <param name="callback">
191 /// An optional Consumer URL that the Service Provider should redirect the
192 /// User Agent to upon successful authorization.
193 /// </param>
194 /// <param name="requestParameters">Extra parameters to add to the request token message. Optional.</param>
195 /// <param name="redirectParameters">Extra parameters to add to the redirect to Service Provider message. Optional.</param>
196 /// <param name="token">The request token that must be exchanged for an access token after the user has provided authorization.</param>
197 /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
198 internal Response RequestUserAuthorization(Uri callback, IDictionary<string, string> requestParameters, IDictionary<string, string> redirectParameters, out string token) {
199 // Obtain an unauthorized request token.
200 var requestToken = new RequestTokenMessage(this.ServiceProvider.RequestTokenEndpoint) {
201 ConsumerKey = this.ConsumerKey,
202 ConsumerSecret = this.ConsumerSecret,
204 requestToken.AddNonOAuthParameters(requestParameters);
205 var requestTokenResponse = this.Channel.Request<UnauthorizedRequestTokenMessage>(requestToken);
206 this.TokenManager.StoreNewRequestToken(requestToken, requestTokenResponse);
208 // Request user authorization.
209 var requestAuthorization = new DirectUserToServiceProviderMessage(this.ServiceProvider.UserAuthorizationEndpoint) {
210 Callback = callback,
211 RequestToken = requestTokenResponse.RequestToken,
213 requestAuthorization.AddNonOAuthParameters(redirectParameters);
214 token = requestAuthorization.RequestToken;
215 return this.Channel.Send(requestAuthorization);
218 /// <summary>
219 /// Processes an incoming authorization-granted message from an SP and obtains an access token.
220 /// </summary>
221 /// <param name="request">The incoming HTTP request.</param>
222 /// <returns>The access token, or null if no incoming authorization message was recognized.</returns>
223 internal GrantAccessTokenMessage ProcessUserAuthorization(HttpRequestInfo request) {
224 DirectUserToConsumerMessage authorizationMessage;
225 if (this.Channel.TryReadFromRequest<DirectUserToConsumerMessage>(request, out authorizationMessage)) {
226 string requestToken = authorizationMessage.RequestToken;
227 return this.ProcessUserAuthorization(requestToken);
228 } else {
229 return null;
233 /// <summary>
234 /// Creates a web request prepared with OAuth authorization
235 /// that may be further tailored by adding parameters by the caller.
236 /// </summary>
237 /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
238 /// <param name="accessToken">The access token that permits access to the protected resource.</param>
239 /// <returns>The initialized WebRequest object.</returns>
240 internal AccessProtectedResourcesMessage CreateAuthorizedRequestInternal(MessageReceivingEndpoint endpoint, string accessToken) {
241 if (endpoint == null) {
242 throw new ArgumentNullException("endpoint");
244 if (String.IsNullOrEmpty(accessToken)) {
245 throw new ArgumentNullException("accessToken");
248 AccessProtectedResourcesMessage message = new AccessProtectedResourcesMessage(endpoint) {
249 AccessToken = accessToken,
250 TokenSecret = this.TokenManager.GetTokenSecret(accessToken),
251 ConsumerKey = this.ConsumerKey,
252 ConsumerSecret = this.ConsumerSecret,
255 return message;