//----------------------------------------------------------------------- // // 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; } } }