//----------------------------------------------------------------------- // // Copyright (c) Outercurve Foundation. All rights reserved. // //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth2 { using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Net; using System.Text; using System.Web; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth2.Messages; /// /// An OAuth 2.0 consumer designed for web applications. /// public class WebServerClient : ClientBase { /// /// Initializes a new instance of the class. /// /// The authorization server. /// The client identifier. /// The client secret. public WebServerClient(AuthorizationServerDescription authorizationServer, string clientIdentifier = null, string clientSecret = null) : base(authorizationServer, clientIdentifier, clientSecret) { } /// /// Gets or sets an optional component that gives you greater control to record and influence the authorization process. /// /// The authorization tracker. public IClientAuthorizationTracker AuthorizationTracker { get; set; } /// /// Prepares a request for user authorization from an authorization server. /// /// The scope of authorized access requested. /// The URL the authorization server should redirect the browser (typically on this site) to when the authorization is completed. If null, the current request's URL will be used. public void RequestUserAuthorization(IEnumerable scope = null, Uri returnTo = null) { var authorizationState = new AuthorizationState(scope) { Callback = returnTo, }; this.PrepareRequestUserAuthorization(authorizationState).Send(); } /// /// Prepares a request for user authorization from an authorization server. /// /// The scope of authorized access requested. /// The URL the authorization server should redirect the browser (typically on this site) to when the authorization is completed. If null, the current request's URL will be used. /// The authorization request. public OutgoingWebResponse PrepareRequestUserAuthorization(IEnumerable scopes = null, Uri returnTo = null) { var authorizationState = new AuthorizationState(scopes) { Callback = returnTo, }; return this.PrepareRequestUserAuthorization(authorizationState); } /// /// Prepares a request for user authorization from an authorization server. /// /// The authorization state to associate with this particular request. /// The authorization request. public OutgoingWebResponse PrepareRequestUserAuthorization(IAuthorizationState authorization) { Requires.NotNull(authorization, "authorization"); Requires.ValidState(authorization.Callback != null || (HttpContext.Current != null && HttpContext.Current.Request != null), MessagingStrings.HttpContextRequired); Requires.ValidState(!string.IsNullOrEmpty(this.ClientIdentifier), OAuth2Strings.RequiredPropertyNotYetPreset, "ClientIdentifier"); Contract.Ensures(Contract.Result() != null); if (authorization.Callback == null) { authorization.Callback = this.Channel.GetRequestFromContext().GetPublicFacingUrl() .StripMessagePartsFromQueryString(this.Channel.MessageDescriptions.Get(typeof(EndUserAuthorizationSuccessResponseBase), Protocol.Default.Version)) .StripMessagePartsFromQueryString(this.Channel.MessageDescriptions.Get(typeof(EndUserAuthorizationFailedResponse), Protocol.Default.Version)); authorization.SaveChanges(); } var request = new EndUserAuthorizationRequest(this.AuthorizationServer) { ClientIdentifier = this.ClientIdentifier, Callback = authorization.Callback, }; request.Scope.ResetContents(authorization.Scope); // Mitigate XSRF attacks by including a state value that would be unpredictable between users, but // verifiable for the same user/session. // If the host is implementing the authorization tracker though, they're handling this protection themselves. if (this.AuthorizationTracker == null) { var context = this.Channel.GetHttpContext(); if (context.Session != null) { request.ClientState = context.Session.SessionID; } else { Logger.OAuth.WarnFormat("No request context discovered, so no client state parameter could be set to mitigate XSRF attacks."); } } return this.Channel.PrepareResponse(request); } /// /// Processes the authorization response from an authorization server, if available. /// /// The incoming HTTP request that may carry an authorization response. /// The authorization state that contains the details of the authorization. public IAuthorizationState ProcessUserAuthorization(HttpRequestBase request = null) { Requires.ValidState(!string.IsNullOrEmpty(this.ClientIdentifier), OAuth2Strings.RequiredPropertyNotYetPreset, "ClientIdentifier"); Requires.ValidState(!string.IsNullOrEmpty(this.ClientSecret), OAuth2Strings.RequiredPropertyNotYetPreset, "ClientSecret"); if (request == null) { request = this.Channel.GetRequestFromContext(); } IMessageWithClientState response; if (this.Channel.TryReadFromRequest(request, out response)) { Uri callback = MessagingUtilities.StripMessagePartsFromQueryString(request.GetPublicFacingUrl(), this.Channel.MessageDescriptions.Get(response)); IAuthorizationState authorizationState; if (this.AuthorizationTracker != null) { authorizationState = this.AuthorizationTracker.GetAuthorizationState(callback, response.ClientState); ErrorUtilities.VerifyProtocol(authorizationState != null, OAuth2Strings.AuthorizationResponseUnexpectedMismatch); } else { var context = this.Channel.GetHttpContext(); if (context.Session != null) { ErrorUtilities.VerifyProtocol(String.Equals(response.ClientState, context.Session.SessionID, StringComparison.Ordinal), OAuth2Strings.AuthorizationResponseUnexpectedMismatch); } else { Logger.OAuth.WarnFormat("No request context discovered, so no client state parameter could be checked to mitigate XSRF attacks."); } authorizationState = new AuthorizationState { Callback = callback }; } var success = response as EndUserAuthorizationSuccessAuthCodeResponse; var failure = response as EndUserAuthorizationFailedResponse; ErrorUtilities.VerifyProtocol(success != null || failure != null, MessagingStrings.UnexpectedMessageReceivedOfMany); if (success != null) { this.UpdateAuthorizationWithResponse(authorizationState, success); } else { // failure Logger.OAuth.Info("User refused to grant the requested authorization at the Authorization Server."); authorizationState.Delete(); } return authorizationState; } return null; } } }