//-----------------------------------------------------------------------
//
// 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 {
///
/// The field behind the property.
///
private OAuthChannel channel;
///
/// 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();
INonceStore store = new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge);
this.ServiceDescription = serviceDescription;
this.OAuthChannel = new OAuthChannel(signingElement, store, tokenManager, messageTypeProvider, new StandardWebRequestHandler());
this.TokenGenerator = new StandardTokenGenerator();
}
///
/// 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 { return this.OAuthChannel.TokenManager; }
}
///
/// Gets the channel to use for sending/receiving messages.
///
public Channel Channel {
get { return this.OAuthChannel; }
}
///
/// Gets or sets the channel to use for sending/receiving messages.
///
internal OAuthChannel OAuthChannel {
get {
return this.channel;
}
set {
if (this.channel != null) {
this.channel.Sending -= this.OAuthChannel_Sending;
}
this.channel = value;
if (this.channel != null) {
this.channel.Sending += this.OAuthChannel_Sending;
}
}
}
///
/// Reads any incoming OAuth message.
///
/// The deserialized message.
///
/// Requires HttpContext.Current.
///
public IOAuthDirectedMessage ReadRequest() {
return (IOAuthDirectedMessage)this.Channel.ReadFromRequest();
}
///
/// Reads any incoming OAuth message.
///
/// The HTTP request to read the message from.
/// The deserialized message.
public IOAuthDirectedMessage ReadRequest(HttpRequest request) {
return (IOAuthDirectedMessage)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 UnauthorizedTokenRequest 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 UnauthorizedTokenRequest ReadTokenRequest(HttpRequest request) {
return this.ReadTokenRequest(new HttpRequestInfo(request));
}
///
/// 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.
public UnauthorizedTokenRequest ReadTokenRequest(HttpRequestInfo request) {
UnauthorizedTokenRequest message;
this.Channel.TryReadFromRequest(request, out message);
return message;
}
///
/// Prepares a message containing an unauthorized token for the Consumer to use in a
/// user agent redirect for subsequent authorization.
///
/// The token request message the Consumer sent that the Service Provider is now responding to.
/// The response message to send using the , after optionally adding extra data to it.
public UnauthorizedTokenResponse PrepareUnauthorizedTokenMessage(UnauthorizedTokenRequest request) {
if (request == null) {
throw new ArgumentNullException("request");
}
string token = this.TokenGenerator.GenerateRequestToken(request.ConsumerKey);
string secret = this.TokenGenerator.GenerateSecret();
UnauthorizedTokenResponse response = new UnauthorizedTokenResponse(request, token, secret);
return 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 UserAuthorizationRequest 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 UserAuthorizationRequest ReadAuthorizationRequest(HttpRequest request) {
return this.ReadAuthorizationRequest(new HttpRequestInfo(request));
}
///
/// 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.
public UserAuthorizationRequest ReadAuthorizationRequest(HttpRequestInfo request) {
UserAuthorizationRequest message;
this.Channel.TryReadFromRequest(request, out message);
return message;
}
///
/// Prepares the message to send back to the consumer following proper authorization of
/// a token by an interactive user at the Service Provider's web site.
///
/// The Consumer's original authorization request.
///
/// The message to send to the Consumer using if one is necessary.
/// Null if the Consumer did not request a callback.
///
public UserAuthorizationResponse PrepareAuthorizationResponse(UserAuthorizationRequest request) {
if (request == null) {
throw new ArgumentNullException("request");
}
if (request.Callback != null) {
var authorization = new UserAuthorizationResponse(request.Callback) {
RequestToken = request.RequestToken,
};
return 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 AuthorizedTokenRequest 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 AuthorizedTokenRequest ReadAccessTokenRequest(HttpRequest request) {
return this.ReadAccessTokenRequest(new HttpRequestInfo(request));
}
///
/// 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.
public AuthorizedTokenRequest ReadAccessTokenRequest(HttpRequestInfo request) {
AuthorizedTokenRequest message;
this.Channel.TryReadFromRequest(request, out message);
return message;
}
///
/// Prepares and sends an access token to a Consumer, and invalidates the request token.
///
/// The Consumer's message requesting an access token.
/// The HTTP response to actually send to the Consumer.
public AuthorizedTokenResponse PrepareAccessTokenMessage(AuthorizedTokenRequest request) {
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 AuthorizedTokenResponse {
AccessToken = accessToken,
TokenSecret = tokenSecret,
};
return 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 AccessProtectedResourceRequest ReadProtectedResourceAuthorization() {
return this.ReadProtectedResourceAuthorization(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 AccessProtectedResourceRequest ReadProtectedResourceAuthorization(HttpRequest request) {
return this.ReadProtectedResourceAuthorization(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 AccessProtectedResourceRequest ReadProtectedResourceAuthorization(HttpRequestMessageProperty request, Uri requestUri) {
return this.ReadProtectedResourceAuthorization(new HttpRequestInfo(request, requestUri));
}
///
/// 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 AccessProtectedResourceRequest ReadProtectedResourceAuthorization(HttpRequestInfo request) {
if (request == null) {
throw new ArgumentNullException("request");
}
AccessProtectedResourceRequest 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;
}
///
/// Hooks the channel in order to perform some operations on some outgoing messages.
///
/// The source of the event.
/// The instance containing the event data.
private void OAuthChannel_Sending(object sender, ChannelEventArgs e) {
// Hook to store the token and secret on its way down to the Consumer.
var grantRequestTokenResponse = e.Message as UnauthorizedTokenResponse;
if (grantRequestTokenResponse != null) {
this.TokenManager.StoreNewRequestToken(grantRequestTokenResponse.RequestMessage, grantRequestTokenResponse);
}
}
}
}