/// <summary>
		///  Log a user into an Auth0 application given an user name and password.
		/// </summary>
		/// <returns>Task that will complete when the user has finished authentication.</returns>
		/// <param name="connection" type="string">The name of the connection to use in Auth0. Connection defines an Identity Provider.</param>
		/// <param name="userName" type="string">User name.</param>
		/// <param name="password type="string"">User password.</param>
		public Task<Auth0User> LoginAsync(string connection, 
			string userName, 
			string password, 
			bool withRefreshToken = false,
			string scope = "openid")
		{

            var endpoint = string.Format(Auth0Constants.ResourceOwnerEndpoint, this.Domain);
			var scopeParameter = IncreaseScopeWithOfflineAccess (withRefreshToken, scope);
			var parameters = new Dictionary<string, string> 
			{
				{ "client_id", this.ClientId },
				{ "connection", connection },
				{ "username", userName },
				{ "password", password },
				{ "grant_type", "password" },
				{ "scope",  scopeParameter }
			};

			if (ScopeHasOfflineAccess (scopeParameter)) {
				var deviceId = this.DeviceIdProvider.GetDeviceId ().Result;
				parameters ["device"] = deviceId;
			}
				
			var request = new Request ("POST", new Uri(endpoint), parameters);
			return request.GetResponseAsync ().ContinueWith<Auth0User>(t => 
			{
				try
				{
					var text = t.Result.GetResponseText();
					var data = JObject.Parse(text).ToObject<Dictionary<string, string>>();

					if (data.ContainsKey ("error")) 
					{
						throw new AuthException ("Error authenticating: " + data["error"]);
					} 
					else if (data.ContainsKey ("access_token"))
					{
						this.SetupCurrentUser (data);
					} 
					else 
					{
						throw new AuthException ("Expected access_token in access token response, but did not receive one.");
					}
				}
				catch (Exception ex)
				{
					throw ex;
				}

				return this.CurrentUser;
			});
		}
		/// <summary>
		///  Log a user into an Auth0 application given an user name and password.
		/// </summary>
		/// <returns>Task that will complete when the user has finished authentication.</returns>
		/// <param name="connection" type="string">The name of the connection to use in Auth0. Connection defines an Identity Provider.</param>
		/// <param name="userName" type="string">User name.</param>
		/// <param name="password type="string"">User password.</param>
		public Task<Auth0User> LoginAsync(string connection, string userName, string password)
		{
			var endpoint = string.Format (ResourceOwnerEndpoint, this.domain);
			var parameters = new Dictionary<string, string> 
			{
				{ "client_id", this.clientId },
				{ "connection", connection },
				{ "username", userName },
				{ "password", password },
				{ "grant_type", "password" },
				{ "scope", "openid profile" }
			};

			var request = new Request ("POST", new Uri(endpoint), parameters);
			return request.GetResponseAsync ().ContinueWith<Auth0User>(t => 
			{
				try
				{
					var text = t.Result.GetResponseText();
					var data = JObject.Parse(text).ToObject<Dictionary<string, string>>();

					if (data.ContainsKey ("error")) 
					{
						throw new AuthException ("Error authenticating: " + data["error"]);
					} 
					else if (data.ContainsKey ("access_token"))
					{
						this.SetupCurrentUser (data);
					} 
					else 
					{
						throw new AuthException ("Expected access_token in access token response, but did not receive one.");
					}
				}
				catch (Exception ex)
				{
					throw ex;
				}

				return this.CurrentUser;
			});
		}
		/// <summary>
		/// Get a delegation token.
		/// </summary>
		/// <returns>Delegation token result.</returns>
		/// <param name="targetClientId">Target client ID.</param>
		/// <param name="options">Custom parameters.</param>
		public Task<JObject> GetDelegationToken(string targetClientId, IDictionary<string, string> options = null)
		{
			var id_token = string.Empty;
			options = options ?? new Dictionary<string, string> ();

			// ensure id_token
			if (options.ContainsKey ("id_token")) {
				id_token = options ["id_token"];
				options.Remove ("id_token");
			} else {
				id_token = this.CurrentUser.IdToken;
			}

			if (string.IsNullOrEmpty (id_token)) {
				throw new InvalidOperationException (
					"You need to login first or specify a value for id_token parameter.");
			}

			var endpoint = string.Format (DelegationEndpoint, this.domain);
			var parameters = new Dictionary<string, string> 
			{
				{ "grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer" },
				{ "id_token", id_token },
				{ "target", targetClientId },
				{ "client_id", this.clientId }
			};

			// custom parameters
			foreach (var option in options) {
				parameters.Add (option.Key, option.Value);
			}

			var request = new Request ("POST", new Uri(endpoint), parameters);
			return request.GetResponseAsync ().ContinueWith<JObject>(t => 
				{
					try
					{
						var text = t.Result.GetResponseText();
						return JObject.Parse(text);
					}
					catch (Exception)
					{
						throw;
					}
				});
		}
		private void SetupCurrentUser(IDictionary<string, string> accountProperties)
		{
			var endpoint = string.Format(UserInfoEndpoint, this.domain, accountProperties["access_token"]);

			var request = new Request ("GET", new Uri(endpoint));
			request.GetResponseAsync ().ContinueWith (t => 
			{
					try
					{
						var text = t.Result.GetResponseText();

						if (t.Result.StatusCode != System.Net.HttpStatusCode.OK)
						{
							throw new InvalidOperationException(text);
						}

						accountProperties.Add("profile", text);
					}
					catch (Exception ex)
					{
						throw ex;
					}
					finally
					{
						this.CurrentUser = new Auth0User(accountProperties);
					}
				}).Wait();
		}
		/// <summary>
		/// Get a delegation token
		/// </summary>
		/// <returns>Delegation token result.</returns>
		/// <param name="api">The type of the API to be used.</param>
		/// <param name="idToken">The string representing the JWT. Useful only if not expired.</param>
		/// <param name="refreshToken">The refresh token.</param>
		/// <param name="targetClientId">The clientId of the target application for which to obtain a delegation token.</param>
		/// <param name="options">Additional parameters.</param>
		public async Task<JObject> GetDelegationToken(
			string api = "",
			string idToken = "",
			string refreshToken = "",
			string targetClientId = "",
			Dictionary<string, string> options = null)
		{
			if (!(string.IsNullOrEmpty(idToken) || string.IsNullOrEmpty(refreshToken)))
			{
				throw new InvalidOperationException(
					"You must provide either the idToken parameter or the refreshToken parameter, not both.");
			}

			if (string.IsNullOrEmpty(idToken) && string.IsNullOrEmpty(refreshToken))
			{
				if (this.CurrentUser == null || string.IsNullOrEmpty(this.CurrentUser.IdToken)){
					throw new InvalidOperationException(
						"You need to login first or specify a value for idToken or refreshToken parameter.");
				}

				idToken = this.CurrentUser.IdToken;
			}

			options = options ?? new Dictionary<string, string>();
			options["id_token"] = idToken;
			options["api_type"] = api;
			options["refresh_token"] = refreshToken;
			options["target"] = targetClientId;
			options["grant_type"] = "urn:ietf:params:oauth:grant-type:jwt-bearer";
			options ["client_id"] = this.ClientId;

            var endpoint = string.Format(Auth0Constants.DelegationEndpoint, this.Domain);

			options = options
				.Where (kvp => !string.IsNullOrEmpty (kvp.Value))
				.ToDictionary (kvp => kvp.Key, kvp => kvp.Value);
				
			var request = new Request ("POST", new Uri(endpoint), options);
			var result = await request.GetResponseAsync ();

			try
			{
				var text = result.GetResponseText();
				var data = JObject.Parse(text);
				JToken temp = null;

				if(data.TryGetValue("id_token", out temp))
				{
					var jwt = temp.Value<string>();

					this.CurrentUser = this.CurrentUser 
						?? new Auth0User() { RefreshToken = refreshToken };
					this.CurrentUser.IdToken = jwt;
				}

				return data;
			}
			catch (Exception)
			{
				throw;
			}
		}