Forwarding SAML by delegation - POST from web server to web server - asp.net

I have an ASP.NET web app using WSFederation for user authentication. I would like to impersonate the users and make a webrequest to another web server from my web server through delegation. The other server also does WSFederation.
Ultimately, i just need a single page from the other web server. There is some header info i need from that HTTP Response Header. Sample code below.
I have been following these steps but this is for Webserver -> WCF, not Webserver -> Webserver.
https://technet.microsoft.com/en-us/library/adfs2-identity-delegation-step-by-step-guide(v=ws.10).aspx
Based on that article, I got the following in my web.config:
<system.identityModel>
<identityConfiguration saveBootstrapContext="true">
Here is a bit of sample code that I'm running to make the web request to the second webapp. Its a simple POST and I need to read some data off the Response header.
try
{
var claimsPrincipal = ClaimsPrincipal.Current;
if (claimsPrincipal.Identities.First().BootstrapContext == null)
{
throw new Exception("No bootstrap context found");
}
WebRequest request = WebRequest.Create("https://destServer/sap/bc/gui/sap/its/webgui/?sap-client=555&sap-language=en");
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
using (var stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
var response = (HttpWebResponse)request.GetResponse();
var responseBody = new StreamReader(response.GetResponseStream()).ReadToEnd();
var responseHeaders = "";
for (int i = 0; i < response.Headers.Count; ++i)
{
responseHeaders += response.Headers.Keys[i] + ": " + response.Headers[i] + " <br/>";
}
ViewBag.responseHeader = responseHeaders;
ViewBag.responseBody = responseBody;
}
catch (Exception e)
{
ViewBag.responseHeader = "FAILED";
ViewBag.responseBody = e.Message;
}
I'm not sure how I need to add the appropriate SAML token into the request. Right now this results in a redirect response to the STS. I know I need to work with the STS admins to get the correct tokens, but assuming I already had this, how does one POST (or GET) to another website with those SAML tokens?

Related

You are unauthorized to view this page: JWT Authentication for WP REST API with MemberPress plug in using WordPressPCL

Trying to use JWT Authentication for WP REST API using WordPressPCL API.
Managed to authenticate and publish both posts and pages to the WordPress server using WordPressPCL. In order to restrict access to only paying members I was planning to use MemberPress. I created a special category for the post and published them as such. The I set up a rule in MemberPress to give access to only to subscribers.
Tested access to the posts and can see the content from my Browser and all is fine with that.
The issue is that when I try to do the same using WordPressPCL although I am properly authorized from the JWT/WordPress perspective I don't have access to the content. It looks like MemberPress blocks an authorized user coming via WordPressPCL but allows access when coming via the web browser.
The same thing happen when I try to post pages as opposed to posts. I should also mention that I can download all posts metadata but not the content of each post which takes me to "You are unauthorized to view this page".
The code below retrieves all posts with a certain title and certain category but myPosts.Content.Rendered == "You are unauthorized to view this page" for all posts.
try
{
WordPressClient client = await GetClient(clientURL,userName,password);
if (await client.IsValidJWToken())
{
var posts = await client.Posts.GetAll();
var myPosts = posts.Where(p => p.Categories[0] == category && p.Title.Rendered == title);
}
...
I tried a similar thing without JWT. I can authenticate but cannot retrieve page content:
CookieContainer cc = new CookieContainer();
var request = (HttpWebRequest)WebRequest.Create(loginUri);
request.Proxy = null;
request.AllowAutoRedirect = false;
request.CookieContainer = cc;
request.Method = "post";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = requestData.Length;
using (Stream s = request.GetRequestStream())
s.Write(requestData, 0, requestData.Length);
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
foreach (Cookie c in response.Cookies)
Console.WriteLine(c.Name + " = " + c.Value);
}
string newloginUri = "http://localhost/myWP/myPostforToday/";
HttpWebRequest newrequest = (HttpWebRequest)WebRequest.Create(newloginUri);
newrequest.Proxy = null;
newrequest.CookieContainer = cc;
using (HttpWebResponse newresponse = (HttpWebResponse)newrequest.GetResponse())
using (Stream resSteam = newresponse.GetResponseStream())
using (StreamReader sr = new StreamReader(resSteam))
File.WriteAllText(#"retrievedpage.html", sr.ReadToEnd());
I suspect that MemeberPress rules restrict the access but I couldn't find any solution. Some guidance on how to handle this ( with or without MemberPress involvement) would be really appreciated.
Change this
var posts = await client.Posts.GetAll();
to:
var posts = await client.Posts.GetAll().Result;
If you have Result() I think this will work and next original ;
var client = new WordPressClient("http://wordpress-domain.com/wp-json/");
client.AuthMethod = AuthMethod.JWT;
await client.RequestJWToken("user", "password");
return client;
But you use:
WordPressClient client = await GetClient(clientURL,userName,password);

QRS API call returns "The client certificate credentials were not recognized"

Exported Qlik Sense certificate using QMC (client.pfx, root.cer, server.pfx).
Imported certificates into IIS web server using MMC. Server and client certificates to store Personal->Certificates, root to store Trusted Root Certification Authorities.
Requested QRS API from ASP.NET controller using QlikClient certificate from store (code below). Tried various user IDs and directories, including INTERNAL/sa_repository, but in all cases got an error "An error occurred while sending the request. The client certificate credentials were not recognized".
Endpoint for test : https://server:4242/qrs/about
I've searched the web but I haven't managed to find what I'm doing wrong, what credentials I should provide.
On the other hand, as I converted exported certificates to separate .key/.crt files (using https://www.markbrilman.nl/2011/08/howto-convert-a-pfx-to-a-seperate-key-crt-file/) and used them in the Postman from web server, it worked without any problem, actually with any UserId in header (i guess it's ignored in that case).
ASP.NET controller:
public X509Certificate2 LoadQlikCertificate()
{
X509Certificate2 certificate = null;
try
{
// Open certification store (MMC)
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
// Get certiface based on the friendly name
certificate = store.Certificates.Cast<X509Certificate2>().FirstOrDefault(c => c.FriendlyName == "QlikClient");
// Logging for debugging purposes
if (certificate != null)
{
logger.Log(LogLevel.Warning, $"Certificate: {certificate.FriendlyName} {certificate.GetSerialNumberString()}");
}
else
{
logger.Log(LogLevel.Warning, $"Certificate: No certificate");
}
// Close certification store
store.Close();
// Return certificate
return certificate;
}
catch (Exception e)
{
...
}
}
/* Get Qlik API response
***********************/
[HttpGet("getqlikapi")]
public IActionResult GetQlikAPI()
{
// Get Qlik certificate
var certificate = this.LoadQlikCertificate();
try
{
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
// Set server name
string server = "server";
// HARDCODED USER AND DIRECTORY FOR TESTING
string userID = "sa_repository"; // tried also other user ids
string userDirectory = "INTERNAL";
// Set Xrfkey header to prevent cross-site request forgery
string xrfkey = "abcdefg123456789";
// Create URL to REST endpoint
string url = $"https://{server}:4242/qrs/about?xrfkey={xrfkey}";
// The JSON object containing the UserId and UserDirectory
string body = $"{{ 'UserId': '{userID}', 'UserDirectory': '{userDirectory}', 'Attributes': [] }}";
// Encode the json object and get the bytes
byte[] bodyBytes = Encoding.UTF8.GetBytes(body);
// Create the HTTP Request
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
// Add the method to authentication the user
request.ClientCertificates.Add(certificate);
// POST request will be used
request.Method = "POST";
// The request will accept responses in JSON format
request.Accept = "application/json";
// A header is added to validate that this request contains a valid cross-site scripting key (the same key as the one used in the url)
request.Headers.Add("X-Qlik-Xrfkey", xrfkey);
request.ContentType = "application/json";
request.ContentLength = bodyBytes.Length;
Stream requestStream = request.GetRequestStream();
requestStream.Write(bodyBytes, 0, bodyBytes.Length);
requestStream.Close();
// Make the web request and get response
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream stream = response.GetResponseStream();
// Return string in response
//return new OkObjectResult(stream != null ? new StreamReader(stream).ReadToEnd() : string.Empty);
return new OkObjectResult("test");
}
catch (Exception e)
{
...
}
}
I ran into this issue on a system we are building.
The problem was that the user did not have rights to the certificate.
Open certificate manager (Start > Manage Computer Certificates)
Find the required certificate.
Right-click cert > All Tasks > Manage Private Keys > Add > [Select the appropriate user]
Note: Manage User Certificates does not have the Manage Private Keys option.

How to consume a secure Rest MVC web api

I'm just a beginner on the .NET world and I've created a web api (.NET 4.5.2) and I'm using the annotation [Authorize] above my controllers like shown below:
[Authorize]
public class PhasesController : ApiController
{
private TestReportEntities db = new TestReportEntities();
// GET: api/Phases
public IQueryable<Phase> GetPhase()
{
return db.Phase;
}
}
I've already created my DB and I'm using the default tables that the web.api uses to manage the access, as you can see on this image:
My tables
I've already done a method to request to my web api, in another project/solution, it's working fine when I remove the annotation [Authorize] from my web api controllers.
this is an example about how I'm requesting my api:
public int GetCurrentIdPhase(int idProject)
{
int phaseId = -1;
WebRequest request = WebRequest.Create(string.Concat(URL, string.Format("api/phases/?idProject={0}", idProject)));
using (var resp = (HttpWebResponse)request.GetResponse())
{
using (var reader = new StreamReader(resp.GetResponseStream()))
{
string objText = reader.ReadToEnd();
var phase = JsonConvert.DeserializeObject<List<Phase>>(objText);
phaseId = phase[0].id;
}
}
if (phaseId != -1)
{
return phaseId;
}
else
{
throw new Exception("Phase not found");
}
}
At the end of the day my questions are:
How can I request a token to my api (POST - www.myApi/token) using the example above?
How can I use the token, once I've got it, on every request to my API?
if you can help me I would really appreciate it.
Thanks.
I've created a method to get the Token from my Web API, this is the method:
var request = (HttpWebRequest)WebRequest.Create(string.Concat(URL, "token"));
var postData = "grant_type=password";
postData += string.Format("&userName={0}", user);
postData += string.Format("&password={0}", pass);
var data = Encoding.ASCII.GetBytes(postData);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
using (var stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
var response = (HttpWebResponse)request.GetResponse();
string objText = new StreamReader(response.GetResponseStream()).ReadToEnd();
var requestedToken = (JObject)JsonConvert.DeserializeObject(objText);
token = string.Concat(token, requestedToken["access_token"].Value<string>());
And to request something to my API all I need to do is just add the token on the header of all requests like shown on the line below:
request.Headers.Add(HttpRequestHeader.Authorization, getToke());
Hope it can help someone else who is beginning to work with .NET web API like me.
Regards.
Im assuming the "GetCurrentIdPhase" call is from an unrelated app with unrealted auth - if any auth.
The difficulty here is in using Authorize and the traidtional browser authentication flow. Here's an example of changing the pipeline a bit to use a different auth form for using console/desktop apps. You don't say where you are calling GetCurrentIdPhase from so I'll have to assume either a separate app. If its a web app and you are authenticated using the same tables, then you will have to share the token between them using for ex. the url blackice provided above.
If the app is a desktop/console/etc (not another app that the user had to auth against the same tables) then you can try this approach to change how auth is done to make it easier to access.
MVC WebAPI authentication from Windows Forms

The operation has timed out in WCF REST Service

I have created a web service using WCF REST Service Template 40(CS). When I try to consume it using
var request = WebRequest.Create(string.Concat(serviceUrl, resourceUrl)) as HttpWebRequest;
if (method == "POST" && requestBody != null)
{
byte[] requestBodyBytes = ToByteArrayUsingJsonContractSer(requestBody);
request.ContentLength = requestBodyBytes.Length;
using (Stream postStream = request.GetRequestStream())
postStream.Write(requestBodyBytes, 0, requestBodyBytes.Length);
}
var response = request.GetResponse() as HttpWebResponse;
I keep getting:
The operation has timed out
How to increase time out ? Do I need to increase it in service or the client which is using this service with url:
http://myservice.com/RecordingCompleted/
Please suggest
try this code before Call the WEb Service.
request.Timeout = 5000;

How to send a xml file over HTTP and HTTPS protocol and get result back

i want to send xml file with userid and password over HTTPs and then send all other xml file on HTTP using POST method and get the response as a xml file back. in ASP.NET (with vb.net preferred)
The url to which i want to send my xml file is:http://www.hostelspoint.com/xml/xml.php
exect xml file pettern is:
<?xml version="1.0" encoding="UTF-8"?>
<OTA_PingRQ xmlns="http://www.opentravel.org/OTA/2003/05"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opentravel.org/OTA/2003/05OTA_PingRQ.xsd"
TimeStamp="2003-03-17T11:09:47-05:00"
Target="Production" Version="1.001" PrimaryLangID="en"
EchoToken="testtoken12">
<EchoData>Hello</EchoData>
</OTA_PingRQ>
You should check out the WCF REST Starter Kit, and watch the screencast on HTTP Plain XML (POX) Services which explains step by step how to do just that - create a WCF REST service that will accept and process a plain XML stream.
All the WCF and WCF REST screencasts by Pluralsight are highly recommended! It's excellent material on how to get started and work with WCF.
In addition to that, the MSDN WCF Developer Center is your first point of contact for any questions or more information on WCF and WCF REST.
i don't know why u removed correct answer from here but yesterday i got correct answer here. and it is:- (can any one tell me how to do same with HTTPS protocol?)
string targetUri = "http://www.hostelspoint.com/xml/xml.php";
System.Xml.XmlDocument reqDoc = new System.Xml.XmlDocument();
reqDoc.Load(Server.MapPath("~\\myfile.xml"));
string formParameterName = "OTA_request";
string xmlData = reqDoc.InnerXml;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(targetUri);
string sendString = formParameterName + "=" + HttpUtility.UrlEncode(xmlData);
byte[] byteStream;
byteStream = System.Text.Encoding.UTF8.GetBytes(sendString);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byteStream.LongLength;
using (Stream writer = request.GetRequestStream())
{
writer.Write(byteStream, 0, (int)request.ContentLength);
writer.Flush();
}
HttpWebResponse resp = (HttpWebResponse)request.GetResponse();
string respStr = "";
if (request.HaveResponse)
{
if (resp.StatusCode == HttpStatusCode.OK || resp.StatusCode == HttpStatusCode.Accepted)
{
StreamReader respReader = new StreamReader(resp.GetResponseStream());
respStr = respReader.ReadToEnd(); // get the xml result in the string object
XmlDocument doc = new XmlDocument();
doc.LoadXml(respStr);
Label1.Text = doc.InnerXml.ToString();
}
}
Yes, you can do same thing using HTTPS protocol. You have to add this code before request:
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate(object sender, X509Certificate certificate, X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
bool validationResult = true;
//
// policy code here ...
//
return validationResult;
};

Resources