//-----------------------------------------------------------------------
//
// Copyright (c) Outercurve Foundation. All rights reserved.
//
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OAuth2.ChannelElements {
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Net;
using System.Net.Mime;
using System.Text;
using System.Web;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Reflection;
using DotNetOpenAuth.OAuth2.Messages;
///
/// The channel for the OAuth protocol.
///
internal class OAuth2ResourceServerChannel : StandardMessageFactoryChannel {
///
/// The messages receivable by this channel.
///
private static readonly Type[] MessageTypes = new Type[] {
typeof(Messages.AccessProtectedResourceRequest),
};
///
/// The protocol versions supported by this channel.
///
private static readonly Version[] Versions = Protocol.AllVersions.Select(v => v.Version).ToArray();
///
/// Initializes a new instance of the class.
///
protected internal OAuth2ResourceServerChannel()
: base(MessageTypes, Versions) {
// TODO: add signing (authenticated request) binding element.
}
///
/// Gets the protocol message that may be embedded in the given HTTP request.
///
/// The request to search for an embedded message.
///
/// The deserialized message, if one is found. Null otherwise.
///
protected override IDirectedProtocolMessage ReadFromRequestCore(HttpRequestInfo request) {
var fields = new Dictionary();
string accessToken;
if ((accessToken = SearchForBearerAccessTokenInRequest(request)) != null) {
fields["token_type"] = Protocol.AccessTokenTypes.Bearer;
fields["access_token"] = accessToken;
}
if (fields.Count > 0) {
MessageReceivingEndpoint recipient;
try {
recipient = request.GetRecipient();
} catch (ArgumentException ex) {
Logger.OAuth.WarnFormat("Unrecognized HTTP request: " + ex.ToString());
return null;
}
// Deserialize the message using all the data we've collected.
var message = (IDirectedProtocolMessage)this.Receive(fields, recipient);
return message;
}
return null;
}
///
/// Gets the protocol message that may be in the given HTTP response.
///
/// The response that is anticipated to contain an protocol message.
///
/// The deserialized message parts, if found. Null otherwise.
///
/// Thrown when the response is not valid.
protected override IDictionary ReadFromResponseCore(IncomingWebResponse response) {
// We never expect resource servers to send out direct requests,
// and therefore won't have direct responses.
throw new NotImplementedException();
}
///
/// Queues a message for sending in the response stream where the fields
/// are sent in the response stream in querystring style.
///
/// The message to send as a response.
///
/// The pending user agent redirect based message to be sent as an HttpResponse.
///
///
/// This method implements spec OAuth V1.0 section 5.3.
///
protected override OutgoingWebResponse PrepareDirectResponse(IProtocolMessage response) {
var webResponse = new OutgoingWebResponse();
// The only direct response from a resource server is a 401 Unauthorized error.
var unauthorizedResponse = response as UnauthorizedResponse;
ErrorUtilities.VerifyInternal(unauthorizedResponse != null, "Only unauthorized responses are expected.");
// First initialize based on the specifics within the message.
var httpResponse = response as IHttpDirectResponse;
webResponse.Status = httpResponse != null ? httpResponse.HttpStatusCode : HttpStatusCode.Unauthorized;
foreach (string headerName in httpResponse.Headers) {
webResponse.Headers.Add(headerName, httpResponse.Headers[headerName]);
}
// Now serialize all the message parts into the WWW-Authenticate header.
var fields = this.MessageDescriptions.GetAccessor(response);
webResponse.Headers[HttpResponseHeader.WwwAuthenticate] = MessagingUtilities.AssembleAuthorizationHeader(Protocol.BearerHttpAuthorizationScheme, fields);
return webResponse;
}
///
/// Searches for a bearer access token in the request.
///
/// The request.
/// The bearer access token, if one exists. Otherwise null.
private static string SearchForBearerAccessTokenInRequest(HttpRequestInfo request) {
Requires.NotNull(request, "request");
// First search the authorization header.
string authorizationHeader = request.Headers[HttpRequestHeader.Authorization];
if (!string.IsNullOrEmpty(authorizationHeader) && authorizationHeader.StartsWith(Protocol.BearerHttpAuthorizationSchemeWithTrailingSpace, StringComparison.OrdinalIgnoreCase)) {
return authorizationHeader.Substring(Protocol.BearerHttpAuthorizationSchemeWithTrailingSpace.Length);
}
// Failing that, scan the entity
if (!string.IsNullOrEmpty(request.Headers[HttpRequestHeader.ContentType])) {
var contentType = new ContentType(request.Headers[HttpRequestHeader.ContentType]);
if (string.Equals(contentType.MediaType, HttpFormUrlEncoded, StringComparison.Ordinal)) {
if (request.Form[Protocol.BearerTokenEncodedUrlParameterName] != null) {
return request.Form[Protocol.BearerTokenEncodedUrlParameterName];
}
}
}
// Finally, check the least desirable location: the query string
if (!String.IsNullOrEmpty(request.QueryStringBeforeRewriting[Protocol.BearerTokenEncodedUrlParameterName])) {
return request.QueryStringBeforeRewriting[Protocol.BearerTokenEncodedUrlParameterName];
}
return null;
}
}
}