//----------------------------------------------------------------------- // // Copyright (c) Outercurve Foundation. All rights reserved. // //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth2 { using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth2.Messages; using Validation; /// /// The OAuth client for the user-agent flow, providing services for installed apps /// and in-browser Javascript widgets. /// public class UserAgentClient : ClientBase { /// /// Initializes a new instance of the class. /// /// The token issuer. /// The client identifier. /// The client secret. /// The host factories. public UserAgentClient(AuthorizationServerDescription authorizationServer, string clientIdentifier = null, string clientSecret = null, IHostFactories hostFactories = null) : this(authorizationServer, clientIdentifier, DefaultSecretApplicator(clientSecret), hostFactories) { } /// /// Initializes a new instance of the class. /// /// The authorization endpoint. /// The token endpoint. /// The client identifier. /// The client secret. /// The host factories. public UserAgentClient(Uri authorizationEndpoint, Uri tokenEndpoint, string clientIdentifier = null, string clientSecret = null, IHostFactories hostFactories = null) : this(authorizationEndpoint, tokenEndpoint, clientIdentifier, DefaultSecretApplicator(clientSecret), hostFactories) { } /// /// Initializes a new instance of the class. /// /// The authorization endpoint. /// The token endpoint. /// The client identifier. /// The tool to use to apply client credentials to authenticated requests to the Authorization Server. /// May be null for clients with no secret or other means of authentication. /// The host factories. public UserAgentClient(Uri authorizationEndpoint, Uri tokenEndpoint, string clientIdentifier, ClientCredentialApplicator clientCredentialApplicator, IHostFactories hostFactories = null) : this(new AuthorizationServerDescription { AuthorizationEndpoint = authorizationEndpoint, TokenEndpoint = tokenEndpoint }, clientIdentifier, clientCredentialApplicator, hostFactories) { Requires.NotNull(authorizationEndpoint, "authorizationEndpoint"); Requires.NotNull(tokenEndpoint, "tokenEndpoint"); } /// /// Initializes a new instance of the class. /// /// The token issuer. /// The client identifier. /// The tool to use to apply client credentials to authenticated requests to the Authorization Server. /// May be null for clients with no secret or other means of authentication. /// The host factories. public UserAgentClient(AuthorizationServerDescription authorizationServer, string clientIdentifier, ClientCredentialApplicator clientCredentialApplicator, IHostFactories hostFactories = null) : base(authorizationServer, clientIdentifier, clientCredentialApplicator, hostFactories) { } /// /// Generates a URL that the user's browser can be directed to in order to authorize /// this client to access protected data at some resource server. /// /// The scope of authorized access requested. /// The client state that should be returned with the authorization response. /// The URL that the authorization response should be sent to via a user-agent redirect. /// The cancellation token. /// /// A fully-qualified URL suitable to initiate the authorization flow. /// public Task RequestUserAuthorizationAsync(IEnumerable scope = null, string state = null, Uri returnTo = null, CancellationToken cancellationToken = default(CancellationToken)) { var authorization = new AuthorizationState(scope) { Callback = returnTo, }; return this.RequestUserAuthorizationAsync(authorization, state: state, cancellationToken: cancellationToken); } /// /// Generates a URL that the user's browser can be directed to in order to authorize /// this client to access protected data at some resource server. /// /// The authorization state that is tracking this particular request. Optional. /// true to request an access token in the fragment of the response's URL; /// false to authenticate to the authorization server and acquire the access token (and possibly a refresh token) via a private channel. /// The client state that should be returned with the authorization response. /// The cancellation token. /// /// A fully-qualified URL suitable to initiate the authorization flow. /// public async Task RequestUserAuthorizationAsync(IAuthorizationState authorization, bool implicitResponseType = false, string state = null, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(authorization, "authorization"); RequiresEx.ValidState(!string.IsNullOrEmpty(this.ClientIdentifier)); var request = this.PrepareRequestUserAuthorization(authorization, implicitResponseType, state); var response = await this.Channel.PrepareResponseAsync(request, cancellationToken); return response.GetDirectUriRequest(); } /// /// Scans the incoming request for an authorization response message. /// /// The actual URL of the incoming HTTP request. /// The authorization. /// The cancellation token. /// /// The granted authorization, or null if the incoming HTTP request did not contain an authorization server response or authorization was rejected. /// public async Task ProcessUserAuthorizationAsync(Uri actualRedirectUrl, IAuthorizationState authorizationState = null, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(actualRedirectUrl, "actualRedirectUrl"); if (authorizationState == null) { authorizationState = new AuthorizationState(); } var carrier = new HttpRequestMessage(HttpMethod.Get, actualRedirectUrl); IDirectedProtocolMessage response = await this.Channel.ReadFromRequestAsync(carrier, cancellationToken); if (response == null) { return null; } return await this.ProcessUserAuthorizationAsync(authorizationState, response, cancellationToken); } /// /// Scans the incoming request for an authorization response message. /// /// The authorization. /// The incoming authorization response message. /// The cancellation token. /// /// The granted authorization, or null if the incoming HTTP request did not contain an authorization server response or authorization was rejected. /// internal async Task ProcessUserAuthorizationAsync(IAuthorizationState authorizationState, IDirectedProtocolMessage response, CancellationToken cancellationToken) { Requires.NotNull(authorizationState, "authorizationState"); Requires.NotNull(response, "response"); EndUserAuthorizationSuccessAccessTokenResponse accessTokenSuccess; EndUserAuthorizationSuccessAuthCodeResponse authCodeSuccess; if ((accessTokenSuccess = response as EndUserAuthorizationSuccessAccessTokenResponse) != null) { UpdateAuthorizationWithResponse(authorizationState, accessTokenSuccess); } else if ((authCodeSuccess = response as EndUserAuthorizationSuccessAuthCodeResponse) != null) { await this.UpdateAuthorizationWithResponseAsync(authorizationState, authCodeSuccess, cancellationToken); } else if (response is EndUserAuthorizationFailedResponse) { authorizationState.Delete(); return null; } return authorizationState; } /// /// Generates a URL that the user's browser can be directed to in order to authorize /// this client to access protected data at some resource server. /// /// The authorization state that is tracking this particular request. Optional. /// /// true to request an access token in the fragment of the response's URL; /// false to authenticate to the authorization server and acquire the access token (and possibly a refresh token) via a private channel. /// /// The client state that should be returned with the authorization response. /// /// A message to send to the authorization server. /// internal EndUserAuthorizationRequest PrepareRequestUserAuthorization(IAuthorizationState authorization, bool implicitResponseType = false, string state = null) { Requires.NotNull(authorization, "authorization"); RequiresEx.ValidState(!string.IsNullOrEmpty(this.ClientIdentifier)); if (authorization.Callback == null) { authorization.Callback = new Uri("http://localhost/"); } var request = implicitResponseType ? (EndUserAuthorizationRequest)new EndUserAuthorizationImplicitRequestC(this.AuthorizationServer) : new EndUserAuthorizationRequestC(this.AuthorizationServer); request.ClientIdentifier = this.ClientIdentifier; request.Callback = authorization.Callback; request.ClientState = state; request.Scope.ResetContents(authorization.Scope); return request; } } }