I need to forward a HttpRequest made to an Azure function (through proxies.json) to a different endpoint. The request needs to be forwarded with query strings and headers intact. I need to modify the response body before sending it back to the original caller.
Proxies.json
"Transactions.BatchImport.Settlements": {
"matchCondition": {
"methods": [ "GET" ],
"route": "/transactions/v1/getsettlementsbyid/{batchId}"
},
"backendUri": "http://localhost/api/storebox/settlements/{batchId}"
},
Psudocode of desired functionality
[FunctionName(nameof(GetStoreboxSettlements))]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "GET", Route = "storebox/settlements/{batchId}")]
HttpRequest request, string batchId)
{
var response = ForwardRequest(request, $"www.anotherEndpoint.com/{batchId}");
response.Body = TransformBody(response.Body);
return new OkObjectResult(response);
}
Alternative solutions are welcome.
If anyone is interested, this is how I solved the problem:
Proxies.json
"Transactions.BatchImport.Settlements": {
"matchCondition": {
"methods": [ "GET" ],
"route": "/transactions/v1/getsettlementsbyid/{batchId}"
},
"backendUri": "http://localhost/api/v1/settlements/{batchId}"
},
Function code
[FunctionName(nameof(GetStoreboxSettlementsProxy))]
public async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "GET", Route = "v1/settlements/{batchId?}")]
HttpRequest request)
{
var baseUri = _environment.Get(Constants.Api.Endpoint);
var requestUri = new UriBuilder(baseUri)
{
Path = $"{request.Path}",
Query = request.QueryString.Value,
}.Uri;
var httpRequestMessage = new HttpRequestMessage();
httpRequestMessage.Headers.Authorization = AuthenticationHeaderValue.Parse(request.Headers["Authorization"]);
httpRequestMessage.RequestUri = requestUri;
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
try
{
response = await _httpClient.SendAsync(httpRequestMessage);
response.EnsureSuccessStatusCode();
var json = response.Content.ReadAsStringAsync().Result;
var storeBoxTransactionData = JsonConvert.DeserializeObject<StoreBoxTransactionData>(json);
if (storeBoxTransactionData.TotalSize > 0)
{
//Transform storebox settlement data
var transformedStoreBoxData = _processStoreBoxSettlementService.Process(storeBoxTransactionData);
response.Content = new StringContent(JsonConvert.SerializeObject(transformedStoreBoxData), Encoding.UTF8, "application/json");
return response;
}
}
catch (Exception e)
{
_logger.LogError($"Failed to get settlements {e}");
}
return response;
}
Related
private async Task GetresultAsync()
{
var client = new HttpClient();
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri("https://api.foursquare.com/v3/places/search?ll=15.3494005,75.142583&query=shops&fields=geocodes,categories,name,hours,fsq_id,price,rating,stats,location"),
Headers =
{
{ "Accept", "application/json" },
{ "Authorization", "fsq322aNlTt3+PuRKw5js/ndngtry/XxNV0Q70yzKjDTQn0="
},
},
};
using (var response = await client.SendAsync(request))
{
response.EnsureSuccessStatusCode();
var body = await response.Content.ReadAsStringAsync();
Debug.WriteLine(body);
}
Please any suggestions? I'm very new to xamarin and I'm getting data in postman but not getting result in xamarin using
RESTCLIENT OR HTTPCLIENT for( foursqaure places api for v3) where am I wrong?
The following is working in Postman but not when a request is sent via Xamarin, is there something obvious that I am missing. My get request is working fine but I can't seem to 'put' data.
Core Api
[HttpPut("{id}")]
public async Task<IActionResult> PutTickets(int id, [FromBody] WorkTickets workTickets)
{
if (id != workTickets.Id)
{
return BadRequest();
}
_context.Entry(workTickets).State = EntityState.Modified;
await _context.SaveChangesAsync();
return NoContent();
}
Data
private async void UploadLocalTickets()
{
try
{
WorkTickets workTickets = new WorkTickets()
{
Id = 4426,
ActualWorkActivity = "test",
};
HttpClient client = new HttpClient();
await DisplayAlert("Got Here", "Before Cloud", "OK");
var json = JsonConvert.SerializeObject(workTickets);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var result = await client.PutAsync("https://xxxx.azurewebsites.net/api/apitickets/" + 4426, content);
await DisplayAlert("Got Here", result.StatusCode.ToString(), "OK");
}
catch
{
await DisplayAlert("Error", "Caught", "OK");
}
}
Error is internal server error
I am using core 3.1 to connect to the canvas API, this is part of my code..
services.AddAuthentication(config =>
{
config.DefaultAuthenticateScheme = "CanvasCookies";
config.DefaultSignInScheme = "CanvasCookies";
config.DefaultChallengeScheme = "CanvasLMS";
})
.AddCookie("CanvasCookies")
.AddOAuth("CanvasLMS", config =>
{
var canvas_domain = Configuration.GetValue<string>("Canvas:Domain");
var client_secret = Configuration.GetValue<string>("Canvas:Secret");
var client_id = Configuration.GetValue<string>("Canvas:Client_id");
config.ClientId = client_id;
config.ClientSecret = client_secret;
config.CallbackPath = new PathString("/oauth/callback");
//config.Scope.Add("google.com")
config.AuthorizationEndpoint = $"{canvas_domain}login/oauth2/auth";
config.TokenEndpoint = $"{canvas_domain}login/oauth2/token";
config.UserInformationEndpoint = $"{canvas_domain}api/v1/users//courses";
config.SaveTokens = true;
config.Events = new OAuthEvents()
{
OnCreatingTicket = context =>
{
var accessToken = context.AccessToken;
var base64payload = accessToken.Split('.')[1];
var bytes = Convert.FromBase64String(base64payload);
var jsonPayload = Encoding.UTF8.GetString(bytes);
var claims = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonPayload);
foreach(var claim in claims)
{
context.Identity.AddClaim(new Claim(claim.Key, claim.Value));
}
return Task.CompletedTask;
}
this is the controller
public class APICanvasController : Controller
{
...
[Authorize]
public async Task<IActionResult> Secret()
{
var serverResponse = await AccessTokenRefreshWrapper(
() => SecuredGetRequest("https://localhost:44388/secret/index"));
var apiResponse = await AccessTokenRefreshWrapper(
() => SecuredGetRequest("https://localhost:44388/secret/index"));
return View();
}
private async Task<HttpResponseMessage> SecuredGetRequest(string url)
{
var token = await HttpContext.GetTokenAsync("access_token");
var client = _httpClientFactory.CreateClient();
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
return await client.GetAsync(url);
}
public async Task<HttpResponseMessage> AccessTokenRefreshWrapper(
Func<Task<HttpResponseMessage>> initialRequest)
{
var response = await initialRequest();
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
await RefreshAccessToken();
response = await initialRequest();
}
return response;
}
private async Task RefreshAccessToken()
{
...
}
}
}
when I execute the code I obtain this error
Exception: The oauth state was missing or invalid.
Unknown location
Exception: An error was encountered while handling the remote login.
Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler.HandleRequestAsync()
Any idea what I am doing wrong?
Thanks
CallbackPath is not supposed to refer to a controller, it refers to a unique path handled by the auth middleware. It will redirect back to your controller when it's done.
"/oauth/callback" should handle oauth authentication result as a json instead of page.
The code below, which is summarized for better understanding, works perfectly with LOCALHOST, however, when I put it in IIS, the body of the request always arrives EMPTY. Can someone help me?
Client application code:
login(userName: string, password: string): Observable<User> {
const headers = new HttpHeaders({
'Content-Type': 'application/json'
});
return this.http.post(`${environment.API_URL}/profiles/login`,
{ userName, password }, { headers }
).pipe(
tap((currentUser: User) => {
this.updateUser(currentUser)
.then(
() => {
console.log('currentUser login stored: ', AppSettings.currentUser);
},
error => console.error('Error storing currentUser login', error)
);
return AppSettings.currentUser;
}),
);
}
ASP.NET Core 3.1 application code on the server:
[Route("api/[controller]")]
[ApiController]
public class ProfilesController : ControllerBase
{
[HttpPost("login")]
public async Task<ActionResult> Login(LoginRequest request)
{
try
{
using (var Reader = new StreamReader(Request.Body, Encoding.UTF8))
{
var sb = new StringBuilder();
sb.AppendFormat("ContentType: {0}\n", Request.ContentType);
sb.AppendFormat("Request: {0}\n", Request.ToString());
sb.AppendFormat("ContentLength: {0}\n", Request.ContentLength.ToString());
if (Request.IsHttps)
sb.AppendFormat("{0}\n", "HTTPS!");
var headers = String.Empty;
foreach (var key in Request.Headers)
headers += key.Key + "=" + key.Value + Environment.NewLine;
sb.AppendFormat("Headers: \n{0}\n", headers);
sb.AppendFormat("QueryString: {0}\n", Request.QueryString);
var text = await Reader.ReadToEndAsync();
sb.AppendFormat("Body: {0}\n", text);
return Ok(sb.ToString());
}
return Ok("OK");
}
catch (System.Exception ex)
{
return Unauthorized($"{ex.Message}: {ex.StackTrace}");
}
}
}
Request result:
ContentType: application/json
Request: Microsoft.AspNetCore.Http.DefaultHttpRequest
ContentLength: 79
Headers:
Accept=*/*
Accept-Encoding=gzip, deflate, br
Cache-Control=no-cache
Connection=keep-alive
Content-Length=79
Content-Type=application/json
Host=loja.online
User-Agent=PostmanRuntime/7.22.0
Postman-Token=121f1927-c340-492f-a98b-0d6586ff32d8
QueryString:
Body:
Using POSTMAN the same thing happens!
Try Specifying the source:
public async Task<ActionResult> Login([FromBody] LoginRequest request) //Added [FromBody]
Just for Further Details
I am having a difficult time using UseJwtBearerAuthentication Method, I am using Microsoft Azure ACS to obtain a token (using a service identity). The JWT token returns fine to my test program. In the test program the token is sent to a MVC WebAPI 2. (The WAAD authentication works fine when token is obtained from Azure Active Directory)
public partial class Startup
{
private const string Issuer = "https://bluebeam-us-east.accesscontrol.windows.net/";
public void ConfigureAuth(IAppBuilder app)
{
string CertificateThumbprint = "99B25E3E31FCD24F669C260A743FBD508D21FE30";
var audience = ConfigurationManager.AppSettings["ida:Audience"];
app.UseErrorPage(new ErrorPageOptions()
{
ShowEnvironment = true,
ShowCookies = false,
ShowSourceCode = true,
});
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = audience ,
Tenant = ConfigurationManager.AppSettings["ida:Tenant"]
});
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AllowedAudiences = new[] { audience },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new X509CertificateSecurityTokenProvider(Issuer, X509CertificateHelper.FindByThumbprint(StoreName.My,StoreLocation.LocalMachine,CertificateThumbprint).First())
},
});
}
The Code to get Token from ACS is as follows:
private async void GetJwtToken()
{
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(IdP.Authority);
var content = new FormUrlEncodedContent(new Dictionary<String, String>
{
{"grant_type","client_credentials"},
{"client_id", IdP.UserName},
{"client_secret", IdP.Password},
{"scope", IdP.Resource}
});
var response = await client.PostAsync("v2/OAuth2-13", content);
response.EnsureSuccessStatusCode();
var jwtdata = await response.Content.ReadAsStringAsync();
var jwt = JsonConvert.DeserializeObject<Token>(jwtdata);
AccessToken = jwt.access_token;
TokenType = jwt.token_type;
long expire;
if (long.TryParse(jwt.expires_in, out expire))
{
ExpiresOn = DateTimeOffset.UtcNow.AddSeconds(expire);
}
Authorization = AccessToken;
}
}
catch (HttpRequestException re)
{
Response = re.Message;
}
}
The code to request a Resource (WebAPI):
private async void WebApiRequestCall()
{
try
{
ConfigureSsl();
using (var client = new HttpClient())
{
client.BaseAddress = _baseAddress;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (!String.IsNullOrWhiteSpace(Authorization))
client.DefaultRequestHeaders.Add("Authorization", Authorization);
var response = await client.GetAsync(WebApiRequest);
response.EnsureSuccessStatusCode();
Response = await response.Content.ReadAsStringAsync();
}
}
catch (HttpRequestException e)
{
Response = e.Message;
}
}
The decoded Token (using google token decoder looks as follows)
Header
{
"x5t": "mbJePjH80k9mnCYKdD-9UI0h_jA",
"alg": "RS256",
"typ": "JWT"
}
Claims
{
"identityprovider": "https://bluebeam-us-east.accesscontrol.windows.net/",
"iss": "https://bluebeam-us-east.accesscontrol.windows.net/",
"http://schemas.microsoft.com/identity/claims/identityprovider": "revu",
"exp": 1406957036,
"nbf": 1406956676,
"aud": "https://bluebeam.com/Bluebeam.Licensing.WebApi/"
}
So I have the following questions:
1) Is using JwtBearerToken the correct method to use to decode decode JWT token from ACS
2) Is there any tracing facilities in Owin that can provide whats going on in the authentication pipeline?
I am using Microsoft Own 3.0-rc1.
It seems that I had an error in my code where I was not setting the correct "bearer header" for OWIN when sending the client request to WebAPI.
After Receiving the JWT Token from ACS, I needed to set the Authorization correctly
private async void GetJwtToken()
{
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(IdP.Authority);
var content = new FormUrlEncodedContent(new Dictionary<String, String>
{
{"grant_type","client_credentials"},
{"client_id", IdP.UserName},
{"client_secret", IdP.Password},
{"scope", IdP.Resource}
});
var response = await client.PostAsync("v2/OAuth2-13", content);
response.EnsureSuccessStatusCode();
var jwtdata = await response.Content.ReadAsStringAsync();
var jwt = JsonConvert.DeserializeObject<Token>(jwtdata);
IdP.AccessToken = jwt.access_token;
IdP.TokenType = jwt.token_type;
long expire;
if (long.TryParse(jwt.expires_in, out expire))
{
IdP.ExpiresOn = DateTimeOffset.UtcNow.AddSeconds(expire);
}
// Ensure that Correct Authorization Header for Owin
Authorization = String.Format("{0} {1}", "Bearer", IdP.AccessToken);**
}
}
catch (HttpRequestException re)
{
Response = re.Message;
}
}
We also need support for symmetric key on the WebAPI, based upon how ACS sends the token
public void ConfigureAuth(IAppBuilder app)
{
var thumbPrint = ConfigurationManager.AppSettings["ida:Thumbprint"];
var audience = ConfigurationManager.AppSettings["ida:Audience"];
var trustedTokenPolicyKey = ConfigurationManager.AppSettings["ida:SymmetricKey"];
app.UseErrorPage(new ErrorPageOptions()
{
ShowEnvironment = true,
ShowCookies = false,
ShowSourceCode = true,
});
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions()
{
AllowedAudiences = new[] {audience},
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new X509CertificateSecurityTokenProvider(Issuer,
X509CertificateHelper.FindByThumbprint(StoreName.My, StoreLocation.LocalMachine, thumbPrint)
.First()),
new SymmetricKeyIssuerSecurityTokenProvider(Issuer, trustedTokenPolicyKey),
},
});
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = audience,
Tenant = ConfigurationManager.AppSettings["ida:Tenant"]
});
}