protected override void AddBodyParameters(HttpResponseMessage httpResponse, ProtocolResponse protocolResponse) { string oauthResponseString = httpResponse.Content.ReadAsStringAsync().Result; Dictionary<string, string> parameters = JsonConvert.DeserializeObject<Dictionary<string, string>>(oauthResponseString); protocolResponse.BodyParameters.AddRange(parameters); }
public ProtocolResponse CreateProtocolResponse(HttpResponseMessage httpResponse) { ThrowIfErrorResponse(httpResponse); var protocolResponse = new ProtocolResponse(); AddBodyParameters(httpResponse, protocolResponse); return protocolResponse; }
protected override void AddBodyParameters(HttpResponseMessage httpResponse, ProtocolResponse protocolResponse) { string oauthResponseString = httpResponse.Content.ReadAsStringAsync().Result; NameValueCollection parameters = HttpUtility.ParseQueryString(oauthResponseString); foreach (string key in parameters.AllKeys) { protocolResponse.BodyParameters.Add(key, parameters[key]); } }
public void CreateProtocolResponse_WithCorrectHttpResponse_ProtocolResponseIsCorrect() { var responseParameters = new Dictionary <string, string>() { { "attr1", "value1" }, { "attr2", "value2" } }; HttpResponseMessage httpResponse = CreateJsonHttpResponse(HttpStatusCode.OK, responseParameters); var responseAdapter = new JsonHttpResponseAdapter(); ProtocolResponse receivedResponse = responseAdapter.CreateProtocolResponse(httpResponse); Assert.AreEqual("value1", receivedResponse.BodyParameters["attr1"]); Assert.AreEqual("value2", receivedResponse.BodyParameters["attr2"]); }
public async Task Missing_token_should_return_error() { var data = new Dictionary <string, string> { { "client_id", client_id }, { "client_secret", client_secret } }; var response = await _mockPipeline.BackChannelClient.PostAsync(IdentityServerPipeline.RevocationEndpoint, new FormUrlEncodedContent(data)); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); var result = await ProtocolResponse.FromHttpResponseAsync <TokenRevocationResponse>(response); result.IsError.Should().BeTrue(); result.Error.Should().Be("invalid_request"); }
ProtocolResponse IProcessMessageStrategy.Process(ProtocolRequest request) { ProtocolResponse response = new ProtocolResponse(request.Action); if (Connection == null || !Connection.IsConnected) { Logger.Log("Connection to Redis Closed - Attempting to reopen..."); try { Connection = ConnectionMultiplexer.Connect(Config.RedisDBConnect); } catch (Exception ex) { Logger.Log("Error connecting to Redis - lost connection (" + ex.Message + ")"); response.Error = "Error connecting to Redis - lost connection (" + ex.Message + ")"; return(response); } } if (!Config.RedisActionKeys.ContainsKey(request.Action)) { Logger.Log("Error - Invalid Redis Action '" + request.Action + "' received from client"); response.Error = "Error executing query against Redis - Action: '" + request.Action + "' not found in server configuration - please check action message or server configuration."; return(response); } RedisAction ra = Config.RedisActionKeys[request.Action]; // parse out all lua nulls and convert to real nulls request.Data = Utility.ParseLuaNullsFromString(request.Data); try { IDatabase db = Connection.GetDatabase(); RedisUtility.PerformOperation(ra.Action, request.IsBulkQuery, ref db, ref Logger, Config.RedisEnvironmentKey, ra.Key, request.Data); response.Result = true; response.Data = new List <List <object> >(); } catch (Exception ex) { CatchException(ref ex, ref request, ref response); } return(response); }
public static async Task <Response <T> > GetResponseAsync <T>(this ProtocolResponse protocolResponse, bool isShow, string path, string error) { Response <T> response = await protocolResponse.HttpResponse.Content.ReadFromJsonAsync <Response <T> >(); if (response.ErrorData.IsNull()) { response = Response <T> .Fail( statusCode : (int)protocolResponse.HttpStatusCode, isShow : isShow, path : path, errors : new[] { protocolResponse.Error, error } ); } return(response); }
private ProtocolResponse CreateSwtTokenResponse() { var builder = new StringBuilder(); builder.AppendFormat("Issuer={0}&", HttpUtility.UrlEncode("http://mysts")); builder.AppendFormat("Audience={0}", HttpUtility.UrlEncode("myScope")); var bodyParameters = new Dictionary <string, string> { { "wrap_access_token", builder.ToString() }, { "wrap_access_token_expires_in", "900" } }; var oauthResponse = new ProtocolResponse(bodyParameters); return(oauthResponse); }
internal static async Task <TokenResponse> RequestTokenAsync(this HttpMessageInvoker client, ProtocolRequest request, CancellationToken cancellationToken = default) { request.Prepare(); request.Method = HttpMethod.Post; HttpResponseMessage response; try { response = await client.SendAsync(request, cancellationToken).ConfigureAwait(); } catch (Exception ex) { return(ProtocolResponse.FromException <TokenResponse>(ex)); } return(await ProtocolResponse.FromHttpResponseAsync <TokenResponse>(response).ConfigureAwait()); }
public static async Task <TokenResponse> RequestClientCredentialsTokenAsync(HttpMessageHandler handler, X509Certificate2 certificate, string tokenEndpoint, CancellationToken cancellationToken) { var keyId = "SN=5E4299BE"; var tokenEndpointUri = new Uri(tokenEndpoint); using var client = new HttpClient(handler); var httpRequest = new HttpRequestMessage(HttpMethod.Post, tokenEndpointUri); httpRequest.Headers.Accept.Clear(); httpRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var contentParameters = new Dictionary <string, string> { { OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.ClientCredentials }, }; var date = DateTime.UtcNow.ToString("R"); httpRequest.Headers.Add("Date", date); httpRequest.Content = new FormUrlEncodedContent(contentParameters); var payload = await httpRequest.Content.ReadAsStringAsync(); var digest = "SHA-256=" + Utils.Hash(payload); var signature = Utils.GetSignature(date, "post", tokenEndpointUri.PathAndQuery, digest, certificate); httpRequest.Headers.Add("Digest", digest); httpRequest.Headers.Add("authorization", $"Signature keyId=\"{keyId}\",algorithm=\"rsa-sha256\",headers=\"(request-target) date digest\",signature=\"{signature}\""); httpRequest.Headers.Add("TPP-Signature-Certificate", Convert.ToBase64String(certificate.Export(X509ContentType.Cert))); try { var response = await client.SendAsync(httpRequest, cancellationToken); return(await ProtocolResponse.FromHttpResponseAsync <TokenResponse>(response)); } catch (Exception ex) { return(ProtocolResponse.FromException <TokenResponse>(ex)); } }
protected async Task <TokenResponse> RequestAuthorizationUrlSignedAsync(string authorizationEndpoint, CancellationToken cancellationToken) { var httpRequest = new HttpRequestMessage(HttpMethod.Get, authorizationEndpoint); httpRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); httpRequest.Content = new StringContent(string.Empty); httpRequest.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); try { var response = await Backchannel.SendAsync(httpRequest, cancellationToken); return(await ProtocolResponse.FromHttpResponseAsync <TokenResponse>(response)); } catch (Exception ex) { return(ProtocolResponse.FromException <TokenResponse>(ex)); } }
public void ProcessMessage_Default_Success() { IProcessMessageStrategy strategy = new InvalidProcessMessageStrategy(); ProtocolRequest request = new ProtocolRequest() { Action = "SampleAction", IsBulkQuery = false, Destination = "Unknown", Data = "{}", IPAddress = "", Type = Newtonsoft.Json.Linq.JTokenType.Object }; ProtocolResponse response = strategy.Process(request); Assert.That(response.Result == false); Assert.That(response.Action == "SampleAction"); Assert.That(response.Error == "Invalid Destination specified - must be either 'REDIS' or 'MYSQL'"); }
public void RequestAccessToken_RequestContentIsCorrect() { ProtocolResponse oauthResponse = CreateSwtTokenResponse(); IHttpClient httpAdapter = Substitute.For <IHttpClient>(); ProtocolRequest receivedRequest = null; httpAdapter.SendRequest(Arg.Do <ProtocolRequest>(request => receivedRequest = request)) .Returns(oauthResponse); var flow = new AcsClientAccountPasswordFlow(_serviceUri, _tokenRequest, httpAdapter); flow.GetAccessToken(); httpAdapter.Received(1).SendRequest(Arg.Any <ProtocolRequest>()); Assert.AreEqual(receivedRequest.BodyParameters["wrap_name"], _tokenRequest.ClientId); Assert.AreEqual(receivedRequest.BodyParameters["wrap_password"], _tokenRequest.ClientSecret); Assert.AreEqual(receivedRequest.BodyParameters["wrap_scope"], _tokenRequest.Scope); }
public async Task <TokenResponse> GetToken(string username, string pwd) { var client = new HttpClient(); var formData = new Dictionary <string, string>() { { "grant_type", "password" }, { "client_id", _clientId }, { "username", username }, { "password", pwd }, }; var request = new HttpRequestMessage(HttpMethod.Post, $"{_authority}/connect/token") { Content = new FormUrlEncodedContent(formData) }; var httpResponse = await client.SendAsync(request); var result = await ProtocolResponse.FromHttpResponseAsync <TokenResponse>(httpResponse); return(result); }
protected string RequestAccessToken() { if (_cachedToken == null || _cachedToken.Expiration < DateTime.Now.AddMinutes(1)) { Dictionary <string, string> bodyParameters = CreateAccessTokenRequestParameters(); UrlParts url = new UrlParts(TokenEndpoint) { Path = TokenEndpoint.AbsolutePath }; ProtocolRequest oauthRequest = CreateProtocolRequest(url, bodyParameters); ProtocolResponse oauthResponse = _httpClient.SendRequest(oauthRequest); _cachedToken = ExtractSecurityTokenFromResponse(oauthResponse); } return(_cachedToken.Token); }
/// <summary> /// Sends a userinfo request. /// </summary> /// <param name="client">The client.</param> /// <param name="request">The request.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns></returns> public static async Task <DeviceAuthorizationResponse> RequestDeviceAuthorizationAsync(this HttpMessageInvoker client, DeviceAuthorizationRequest request, CancellationToken cancellationToken = default) { var clone = request.Clone(); clone.Parameters.AddOptional(OidcConstants.AuthorizeRequest.Scope, request.Scope); clone.Method = HttpMethod.Post; clone.Prepare(); HttpResponseMessage response; try { response = await client.SendAsync(clone, cancellationToken).ConfigureAwait(); } catch (Exception ex) { return(ProtocolResponse.FromException <DeviceAuthorizationResponse>(ex)); } return(await ProtocolResponse.FromHttpResponseAsync <DeviceAuthorizationResponse>(response).ConfigureAwait()); }
public void ProcessMessage_BulkQueryException_Success() { IProcessMessageStrategy strategy = CreateMySqlProcessStrategyWithMocks(new Mocks.MockDBConnection(new MockMySqlBulkQueryExceptionBehaviour())); ProtocolRequest request = new ProtocolRequest { Action = "SampleCall", Destination = "MYSQL", IsBulkQuery = true, IPAddress = "127.0.0.1", Type = Newtonsoft.Json.Linq.JTokenType.Object, Data = "[{'Param1':1},{'Param2':2}]" }; ProtocolResponse response = strategy.Process(request); Assert.That(response.Result == false); Assert.That(response.Error == "Error executing query against MySQL (Action: " + request.Action + ") - A sample bulk query exception has occurred"); Assert.That(response.Action == "SampleCall"); Assert.That(response.Data.Count == 1); }
public void ProcessMessage_EmptyData_Success() { IProcessMessageStrategy strategy = CreateMySqlProcessStrategyWithMocks(new Mocks.MockDBConnection()); ProtocolRequest request = new ProtocolRequest { Action = "SampleCall", Destination = "MYSQL", IsBulkQuery = false, IPAddress = "127.0.0.1", Type = Newtonsoft.Json.Linq.JTokenType.Object, Data = "{}" }; ProtocolResponse response = strategy.Process(request); Assert.That(response.Result == true); Assert.That(response.Error == ""); Assert.That(response.Action == "SampleCall"); Assert.That((int)response.Data[0][0] == 1); }
public void RequestAccessToken_RequestUrlIsCorrect() { ProtocolResponse oauthResponse = CreateSwtTokenResponse(); IHttpClient httpAdapter = Substitute.For <IHttpClient>(); ProtocolRequest receivedRequest = null; httpAdapter.SendRequest(Arg.Do <ProtocolRequest>(request => receivedRequest = request)) .Returns(oauthResponse); var flow = new AcsClientAccountPasswordFlow(_serviceUri, _tokenRequest, httpAdapter); // Act flow.GetAccessToken(); // Assert httpAdapter.Received(1).SendRequest(Arg.Any <ProtocolRequest>()); Uri expectedUri = new Uri(_serviceUri, "/WRAPv0.9/"); Assert.AreEqual(expectedUri, receivedRequest.Url.BuildUri()); }
public void RequestAccessToken_RequestContentIsCorrect() { JwtSecurityToken token = CreateJwtToken(); ProtocolResponse oauthResponse = CreateJwtTokenResponse(token); IHttpClient httpAdapter = Substitute.For <IHttpClient>(); ProtocolRequest receivedRequest = null; httpAdapter.SendRequest(Arg.Do <ProtocolRequest>(request => receivedRequest = request)) .Returns(oauthResponse); var flow = new AzureAdClientCredentialsFlow(_serviceUri, _tokenRequest, httpAdapter); flow.RequestAccessToken(); httpAdapter.Received(1).SendRequest(Arg.Any <ProtocolRequest>()); Assert.AreEqual(receivedRequest.BodyParameters["grant_type"], "client_credentials"); Assert.AreEqual(receivedRequest.BodyParameters["client_id"], _tokenRequest.ClientId); Assert.AreEqual(receivedRequest.BodyParameters["client_secret"], _tokenRequest.ClientSecret); Assert.AreEqual(receivedRequest.BodyParameters["resource"], _tokenRequest.Scope); }
/// <summary> /// Sends an OAuth token introspection request. /// </summary> /// <param name="client">The client.</param> /// <param name="request">The request.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns></returns> public static async Task <TokenIntrospectionResponse> IntrospectTokenAsync(this HttpMessageInvoker client, TokenIntrospectionRequest request, CancellationToken cancellationToken = default) { var clone = request.Clone(); clone.Method = HttpMethod.Post; clone.Parameters.AddRequired(OidcConstants.TokenIntrospectionRequest.Token, request.Token); clone.Parameters.AddOptional(OidcConstants.TokenIntrospectionRequest.TokenTypeHint, request.TokenTypeHint); clone.Prepare(); HttpResponseMessage response; try { response = await client.SendAsync(clone, cancellationToken).ConfigureAwait(); } catch (Exception ex) { return(ProtocolResponse.FromException <TokenIntrospectionResponse>(ex)); } return(await ProtocolResponse.FromHttpResponseAsync <TokenIntrospectionResponse>(response).ConfigureAwait()); }
public async Task Invalid_token_type_hint_should_return_error() { var tokens = await GetTokensAsync(); (await IsAccessTokenValidAsync(tokens)).Should().BeTrue(); var data = new Dictionary <string, string> { { "client_id", client_id }, { "client_secret", client_secret }, { "token", tokens.AccessToken }, { "token_type_hint", "not_valid" } }; var response = await _mockPipeline.BackChannelClient.PostAsync(IdentityServerPipeline.RevocationEndpoint, new FormUrlEncodedContent(data)); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); var result = await ProtocolResponse.FromHttpResponseAsync <TokenRevocationResponse>(response); result.IsError.Should().BeTrue(); result.Error.Should().Be("unsupported_token_type"); }
public void RequestAccessToken_RequestUrlIsCorrect() { JwtSecurityToken token = CreateJwtToken(); ProtocolResponse oauthResponse = CreateJwtTokenResponse(token); IHttpClient httpAdapter = Substitute.For <IHttpClient>(); ProtocolRequest receivedRequest = null; httpAdapter.SendRequest(Arg.Do <ProtocolRequest>(request => receivedRequest = request)) .Returns(oauthResponse); var flow = new AcsClientCredentialsFlow(_serviceUri, _tokenRequest, httpAdapter); // Act flow.RequestAccessToken(); // Assert httpAdapter.Received(1).SendRequest(Arg.Any <ProtocolRequest>()); Uri expectedUri = new Uri(_serviceUri, "/v2/oauth2-13/"); Assert.AreEqual(expectedUri, receivedRequest.Url.BuildUri()); }
/// <summary> /// Sends a discovery document request /// </summary> /// <param name="client">The client.</param> /// <param name="request">The request.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns></returns> public static async Task <DiscoveryDocumentResponse> GetDiscoveryDocumentAsync(this HttpMessageInvoker client, DiscoveryDocumentRequest request, CancellationToken cancellationToken = default) { string address; if (request.Address.IsPresent()) { address = request.Address; } else if (client is HttpClient httpClient) { address = httpClient.BaseAddress.AbsoluteUri; } else { throw new ArgumentException("An address is required."); } var parsed = DiscoveryEndpoint.ParseUrl(address, request.Policy.DiscoveryDocumentPath); var authority = parsed.Authority; var url = parsed.Url; if (request.Policy.Authority.IsMissing()) { request.Policy.Authority = authority; } var jwkUrl = ""; if (!DiscoveryEndpoint.IsSecureScheme(new Uri(url), request.Policy)) { return(ProtocolResponse.FromException <DiscoveryDocumentResponse>(new InvalidOperationException("HTTPS required"), $"Error connecting to {url}. HTTPS required.")); } try { var clone = request.Clone(); clone.Method = HttpMethod.Get; clone.Prepare(); clone.RequestUri = new Uri(url); var response = await client.SendAsync(clone, cancellationToken).ConfigureAwait(); string responseContent = null; if (response.Content != null) { responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(); } if (!response.IsSuccessStatusCode) { return(await ProtocolResponse.FromHttpResponseAsync <DiscoveryDocumentResponse>(response, $"Error connecting to {url}: {response.ReasonPhrase}").ConfigureAwait()); } var disco = await ProtocolResponse.FromHttpResponseAsync <DiscoveryDocumentResponse>(response, request.Policy).ConfigureAwait(); if (disco.IsError) { return(disco); } try { jwkUrl = disco.JwksUri; if (jwkUrl != null) { var jwkClone = request.Clone <JsonWebKeySetRequest>(); jwkClone.Method = HttpMethod.Get; jwkClone.Address = jwkUrl; jwkClone.Prepare(); var jwkResponse = await client.GetJsonWebKeySetAsync(jwkClone, cancellationToken).ConfigureAwait(); if (jwkResponse.IsError) { return(await ProtocolResponse.FromHttpResponseAsync <DiscoveryDocumentResponse>(jwkResponse.HttpResponse, $"Error connecting to {jwkUrl}: {jwkResponse.HttpErrorReason}").ConfigureAwait()); } disco.KeySet = jwkResponse.KeySet; } return(disco); } catch (Exception ex) { return(ProtocolResponse.FromException <DiscoveryDocumentResponse>(ex, $"Error connecting to {jwkUrl}. {ex.Message}.")); } } catch (Exception ex) { return(ProtocolResponse.FromException <DiscoveryDocumentResponse>(ex, $"Error connecting to {url}. {ex.Message}.")); } }
ProtocolResponse IProcessMessageStrategy.Process(ProtocolRequest request) { ProtocolResponse response = new ProtocolResponse(request.Action); if (request.IsBulkQuery) { // now deserialize this string into a list of dictionaries for parsing List <Dictionary <string, object> > DataDictionary = Newtonsoft.Json.JsonConvert.DeserializeObject <List <Dictionary <string, object> > >(request.Data); try { if (CheckConnection()) { foreach (var d in DataDictionary) { IDbCommand cmd = SqlUtility.CreateCommand(Connection, request.Action, d); List <object> results = SqlUtility.InvokeCommand(cmd, out string error); response.Error += error; if (results != null) { response.Data.Add(results); } } response.Result = true; } else { response.Result = false; response.Error = "Could not connect to MYSQL instance"; } } catch (Exception ex) { CatchException(ref ex, ref request, ref response); } finally { CloseConnection(); } } else { // now deserialize this string into a list of dictionaries for parsing Dictionary <string, object> DataDictionary = null; if (request.Type == JTokenType.Object) { DataDictionary = Newtonsoft.Json.JsonConvert.DeserializeObject <Dictionary <string, object> >(request.Data); } else { DataDictionary = new Dictionary <string, object>(); } // special scenario - because we cant get the ip address of the game server from DCS, we'll get it from the socket sender object // and specially insert it as a parameter into the data dictionary // the other special scenario is the server description request can supply - this can contain harmful html, so we must sanitize the input if (request.Action == ACTION_GET_SERVERID) { DataDictionary.Add("IP", request.IPAddress); if (DataDictionary.ContainsKey("Description")) { try { string html = Convert.ToString(DataDictionary["Description"]); DataDictionary["Description"] = Utility.SanitizeHTML(html, ref Config); } catch (Exception ex) { Logger.Log("Error sanitizing ServerDescription html string (Action: " + request.Action + ") - " + ex.Message); response.Error = "Error sanitizing ServerDescription html string (Action: " + request.Action + ") - " + ex.Message; response.Result = false; return(response); } } // Check the API version that the game is using if (DataDictionary.ContainsKey("Version") && DataDictionary["Version"].ToString() != Config.VersionKey) { Logger.Log("Client Version Mismatch (Expected: " + Config.VersionKey + ", Got: " + DataDictionary["Version"] + ")"); response.Error = "Version mismatch - you are running an older version of KI - the latest version is [" + Config.Version + "] - Please update to the latest version"; response.Result = false; return(response); } else if (!DataDictionary.ContainsKey("Version")) { Logger.Log("Client Version Mismatch - Client did not provide version information"); response.Error = "Version mismatch - you are running an older version of KI - the latest version is [" + Config.Version + "] - Please update to the latest version"; response.Result = false; return(response); } } try { if (CheckConnection()) { IDbCommand cmd = SqlUtility.CreateCommand(Connection, request.Action, DataDictionary); List <object> results = SqlUtility.InvokeCommand(cmd, out string error); response.Error += error; if (results != null) { response.Data.Add(results); } response.Result = true; } else { response.Result = false; response.Error = "Could not connect to MYSQL instance"; } } catch (Exception ex) { CatchException(ref ex, ref request, ref response); } finally { CloseConnection(); } } return(response); }
protected abstract void AddBodyParameters(HttpResponseMessage httpResponse, ProtocolResponse protocolResponse);
private ProtocolResponse ReceiveResponse() { ProtocolResponse response = new ProtocolResponse(); String[] pair = null; String line = null; char[] split = { ' ' }; // 서버 명령 해석기와 연결되지 않았을 경우 if (socket == null || !socket.Connected) { response.Code = ProtocolResponse.ServiceNotAvailable; response.Message = "서버와 연결되지 않았습니다."; return(response); } try { line = reader.ReadLine(); } catch (Exception) { response.Code = ProtocolResponse.ServiceNotAvailable; response.Message = "서버와 연결되지 않았습니다."; return(response); } // 서버 명령 해석기로 부터 받은 응답이 없을 경우 if (line == null) { response.Code = ProtocolResponse.ServiceNotAvailable; response.Message = "서버와 연결되지 않았습니다."; return(response); } pair = line.Trim().Split(split, 2); // 잘못된 응답 코드를 수신한 경우 if (pair[0].Length != 3) { throw new Exception("알 수 없는 응답 코드가 수신되었습니다."); } // 서버 명령 해석기의 응답을 파싱 response.Code = int.Parse(pair[0]); if (pair.Length == 1) { logger.DebugFormat("수신: {0}", pair[0]); } else if (pair.Length == 2) { logger.DebugFormat("수신: {0} {1}", pair[0], pair[1]); response.Message = pair[1]; } return(response); }
private void CatchException(ref Exception ex, ref ProtocolRequest request, ref ProtocolResponse response) { Logger.Log("Error executing query against MySQL (Action: " + request.Action + ") - " + ex.Message); response.Error = "Error executing query against MySQL (Action: " + request.Action + ") - " + ex.Message; }
private async Task <TokenResponse> GetToken(FormUrlEncodedContent body) { var response = await _client.PostAsync(TokenEndpoint, body); return(await ProtocolResponse.FromHttpResponseAsync <TokenResponse>(response)); }