//-----------------------------------------------------------------------
//
// Copyright (c) Andrew Arnott. All rights reserved.
//
//-----------------------------------------------------------------------
namespace DotNetOAuth {
using System;
using System.Collections.Generic;
using System.Globalization;
using System.ServiceModel.Channels;
using System.Web;
using DotNetOAuth.ChannelElements;
using DotNetOAuth.Messages;
using DotNetOAuth.Messaging;
using DotNetOAuth.Messaging.Bindings;
///
/// A web application that allows access via OAuth.
///
///
/// The Service Provider’s documentation should include:
///
/// - The URLs (Request URLs) the Consumer will use when making OAuth requests, and the HTTP methods (i.e. GET, POST, etc.) used in the Request Token URL and Access Token URL.
/// - Signature methods supported by the Service Provider.
/// - Any additional request parameters that the Service Provider requires in order to obtain a Token. Service Provider specific parameters MUST NOT begin with oauth_.
///
///
public class ServiceProvider {
///
/// Initializes a new instance of the class.
///
/// The endpoints and behavior on the Service Provider.
/// The host's method of storing and recalling tokens and secrets.
public ServiceProvider(ServiceProviderDescription serviceDescription, ITokenManager tokenManager)
: this(serviceDescription, tokenManager, new OAuthServiceProviderMessageTypeProvider(tokenManager)) {
}
///
/// Initializes a new instance of the class.
///
/// The endpoints and behavior on the Service Provider.
/// The host's method of storing and recalling tokens and secrets.
/// An object that can figure out what type of message is being received for deserialization.
public ServiceProvider(ServiceProviderDescription serviceDescription, ITokenManager tokenManager, OAuthServiceProviderMessageTypeProvider messageTypeProvider) {
if (serviceDescription == null) {
throw new ArgumentNullException("serviceDescription");
}
if (tokenManager == null) {
throw new ArgumentNullException("tokenManager");
}
if (messageTypeProvider == null) {
throw new ArgumentNullException("messageTypeProvider");
}
var signingElement = serviceDescription.CreateTamperProtectionElement();
signingElement.SignatureVerificationCallback = this.TokenSignatureVerificationCallback;
INonceStore store = new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge);
this.ServiceDescription = serviceDescription;
this.Channel = new OAuthChannel(signingElement, store, messageTypeProvider, new StandardWebRequestHandler());
this.TokenGenerator = new StandardTokenGenerator();
this.TokenManager = tokenManager;
}
///
/// Gets the description of this Service Provider.
///
public ServiceProviderDescription ServiceDescription { get; private set; }
///
/// Gets or sets the generator responsible for generating new tokens and secrets.
///
public ITokenGenerator TokenGenerator { get; set; }
///
/// Gets the persistence store for tokens and secrets.
///
public ITokenManager TokenManager { get; private set; }
///
/// Gets or sets the channel to use for sending/receiving messages.
///
internal OAuthChannel Channel { get; set; }
///
/// Reads any incoming OAuth message.
///
/// The deserialized message.
public IProtocolMessage ReadRequest() {
return this.Channel.ReadFromRequest();
}
///
/// Reads any incoming OAuth message.
///
/// The HTTP request to read the message from.
/// The deserialized message.
public IProtocolMessage ReadRequest(HttpRequest request) {
return this.Channel.ReadFromRequest(new HttpRequestInfo(request));
}
///
/// Gets the incoming request for an unauthorized token, if any.
///
/// The incoming request, or null if no OAuth message was attached.
/// Thrown if an unexpected OAuth message is attached to the incoming request.
///
/// Requires HttpContext.Current.
///
public RequestTokenMessage ReadTokenRequest() {
return this.ReadTokenRequest(this.Channel.GetRequestFromContext());
}
///
/// Gets the incoming request for an unauthorized token, if any.
///
/// The incoming HTTP request.
/// The incoming request, or null if no OAuth message was attached.
/// Thrown if an unexpected OAuth message is attached to the incoming request.
public RequestTokenMessage ReadTokenRequest(HttpRequest request) {
return this.ReadTokenRequest(new HttpRequestInfo(request));
}
///
/// Sends an unauthorized token back to the Consumer for use in a user agent redirect
/// for subsequent authorization.
///
/// The token request message the Consumer sent that the Service Provider is now responding to.
/// Any extra parameters the Consumer should receive with the OAuth message. May be null.
/// The actual response the Service Provider will need to forward as the HTTP response.
public Response SendUnauthorizedTokenResponse(RequestTokenMessage request, IDictionary extraParameters) {
string token = this.TokenGenerator.GenerateRequestToken(request.ConsumerKey);
string secret = this.TokenGenerator.GenerateSecret();
UnauthorizedRequestTokenMessage response = new UnauthorizedRequestTokenMessage {
RequestToken = token,
TokenSecret = secret,
};
response.AddNonOAuthParameters(extraParameters);
this.TokenManager.StoreNewRequestToken(request, response);
return this.Channel.Send(response);
}
///
/// Gets the incoming request for the Service Provider to authorize a Consumer's
/// access to some protected resources.
///
/// The incoming request, or null if no OAuth message was attached.
/// Thrown if an unexpected OAuth message is attached to the incoming request.
///
/// Requires HttpContext.Current.
///
public DirectUserToServiceProviderMessage ReadAuthorizationRequest() {
return this.ReadAuthorizationRequest(this.Channel.GetRequestFromContext());
}
///
/// Gets the incoming request for the Service Provider to authorize a Consumer's
/// access to some protected resources.
///
/// The incoming HTTP request.
/// The incoming request, or null if no OAuth message was attached.
/// Thrown if an unexpected OAuth message is attached to the incoming request.
public DirectUserToServiceProviderMessage ReadAuthorizationRequest(HttpRequest request) {
return this.ReadAuthorizationRequest(new HttpRequestInfo(request));
}
///
/// Completes user authorization of a token by redirecting the user agent back to the Consumer.
///
/// The Consumer's original authorization request.
///
/// The pending user agent redirect based message to be sent as an HttpResponse,
/// or null if the Consumer requested no callback.
///
public Response SendAuthorizationResponse(DirectUserToServiceProviderMessage request) {
if (request == null) {
throw new ArgumentNullException("request");
}
if (request.Callback != null) {
var authorization = new DirectUserToConsumerMessage(request.Callback) {
RequestToken = request.RequestToken,
};
return this.Channel.Send(authorization);
} else {
return null;
}
}
///
/// Gets the incoming request to exchange an authorized token for an access token.
///
/// The incoming request, or null if no OAuth message was attached.
/// Thrown if an unexpected OAuth message is attached to the incoming request.
///
/// Requires HttpContext.Current.
///
public RequestAccessTokenMessage ReadAccessTokenRequest() {
return this.ReadAccessTokenRequest(this.Channel.GetRequestFromContext());
}
///
/// Gets the incoming request to exchange an authorized token for an access token.
///
/// The incoming HTTP request.
/// The incoming request, or null if no OAuth message was attached.
/// Thrown if an unexpected OAuth message is attached to the incoming request.
public RequestAccessTokenMessage ReadAccessTokenRequest(HttpRequest request) {
return this.ReadAccessTokenRequest(new HttpRequestInfo(request));
}
///
/// Prepares and sends an access token to a Consumer, and invalidates the request token.
///
/// The Consumer's message requesting an access token.
/// Any extra parameters the Consumer should receive with the OAuth message. May be null.
/// The HTTP response to actually send to the Consumer.
public Response SendAccessToken(RequestAccessTokenMessage request, IDictionary extraParameters) {
if (request == null) {
throw new ArgumentNullException("request");
}
if (!this.TokenManager.IsRequestTokenAuthorized(request.RequestToken)) {
throw new ProtocolException(
string.Format(
CultureInfo.CurrentCulture,
Strings.AccessTokenNotAuthorized,
request.RequestToken));
}
string accessToken = this.TokenGenerator.GenerateAccessToken(request.ConsumerKey);
string tokenSecret = this.TokenGenerator.GenerateSecret();
this.TokenManager.ExpireRequestTokenAndStoreNewAccessToken(request.ConsumerKey, request.RequestToken, accessToken, tokenSecret);
var grantAccess = new GrantAccessTokenMessage {
AccessToken = accessToken,
TokenSecret = tokenSecret,
};
grantAccess.AddNonOAuthParameters(extraParameters);
return this.Channel.Send(grantAccess);
}
///
/// Gets the authorization (access token) for accessing some protected resource.
///
/// The authorization message sent by the Consumer, or null if no authorization message is attached.
///
/// This method verifies that the access token and token secret are valid.
/// It falls on the caller to verify that the access token is actually authorized
/// to access the resources being requested.
///
/// Thrown if an unexpected message is attached to the request.
public AccessProtectedResourcesMessage GetProtectedResourceAuthorization() {
return this.GetProtectedResourceAuthorization(this.Channel.GetRequestFromContext());
}
///
/// Gets the authorization (access token) for accessing some protected resource.
///
/// The incoming HTTP request.
/// The authorization message sent by the Consumer, or null if no authorization message is attached.
///
/// This method verifies that the access token and token secret are valid.
/// It falls on the caller to verify that the access token is actually authorized
/// to access the resources being requested.
///
/// Thrown if an unexpected message is attached to the request.
public AccessProtectedResourcesMessage GetProtectedResourceAuthorization(HttpRequest request) {
return this.GetProtectedResourceAuthorization(new HttpRequestInfo(request));
}
///
/// Gets the authorization (access token) for accessing some protected resource.
///
/// HTTP details from an incoming WCF message.
/// The URI of the WCF service endpoint.
/// The authorization message sent by the Consumer, or null if no authorization message is attached.
///
/// This method verifies that the access token and token secret are valid.
/// It falls on the caller to verify that the access token is actually authorized
/// to access the resources being requested.
///
/// Thrown if an unexpected message is attached to the request.
public AccessProtectedResourcesMessage GetProtectedResourceAuthorization(HttpRequestMessageProperty request, Uri requestUri) {
return this.GetProtectedResourceAuthorization(new HttpRequestInfo(request, requestUri));
}
///
/// Reads a request for an unauthorized token from the incoming HTTP request.
///
/// The HTTP request to read from.
/// The incoming request, or null if no OAuth message was attached.
/// Thrown if an unexpected OAuth message is attached to the incoming request.
internal RequestTokenMessage ReadTokenRequest(HttpRequestInfo request) {
RequestTokenMessage message;
this.Channel.TryReadFromRequest(request, out message);
return message;
}
///
/// Reads in a Consumer's request for the Service Provider to obtain permission from
/// the user to authorize the Consumer's access of some protected resource(s).
///
/// The HTTP request to read from.
/// The incoming request, or null if no OAuth message was attached.
/// Thrown if an unexpected OAuth message is attached to the incoming request.
internal DirectUserToServiceProviderMessage ReadAuthorizationRequest(HttpRequestInfo request) {
DirectUserToServiceProviderMessage message;
this.Channel.TryReadFromRequest(request, out message);
return message;
}
///
/// Reads in a Consumer's request to exchange an authorized request token for an access token.
///
/// The HTTP request to read from.
/// The incoming request, or null if no OAuth message was attached.
/// Thrown if an unexpected OAuth message is attached to the incoming request.
internal RequestAccessTokenMessage ReadAccessTokenRequest(HttpRequestInfo request) {
RequestAccessTokenMessage message;
this.Channel.TryReadFromRequest(request, out message);
return message;
}
///
/// Gets the authorization (access token) for accessing some protected resource.
///
/// The incoming HTTP request.
/// The authorization message sent by the Consumer, or null if no authorization message is attached.
///
/// This method verifies that the access token and token secret are valid.
/// It falls on the caller to verify that the access token is actually authorized
/// to access the resources being requested.
///
/// Thrown if an unexpected message is attached to the request.
internal AccessProtectedResourcesMessage GetProtectedResourceAuthorization(HttpRequestInfo request) {
if (request == null) {
throw new ArgumentNullException("request");
}
AccessProtectedResourcesMessage accessMessage;
if (this.Channel.TryReadFromRequest(request, out accessMessage)) {
if (this.TokenManager.GetTokenType(accessMessage.AccessToken) != TokenType.AccessToken) {
throw new ProtocolException(
string.Format(
CultureInfo.CurrentCulture,
Strings.BadAccessTokenInProtectedResourceRequest,
accessMessage.AccessToken));
}
}
return accessMessage;
}
///
/// Fills out the secrets in an incoming message so that signature verification can be performed.
///
/// The incoming message.
private void TokenSignatureVerificationCallback(ITamperResistantOAuthMessage message) {
message.ConsumerSecret = this.TokenManager.GetConsumerSecret(message.ConsumerKey);
var tokenMessage = message as ITokenContainingMessage;
if (tokenMessage != null) {
message.TokenSecret = this.TokenManager.GetTokenSecret(tokenMessage.Token);
}
}
}
}