Reporting Services Authentication issue - authentication

I am trying to programmatically render a PDF using Azure Reporting Services. I suspect that the actual PDF retrieval is fine, but I cannot find a way to authenticate the connection before requesting the report (via URL). I am working in the services layer of my web application and I cannot use a web reference (might not work with Azure) and it doesn't make sense to use a ReportViewer control (since it's a service layer method).
I have all the details to connect, but I suspect that I require a cookie to authenticate and I'm not sure how to manually create this. Any suggestions/solutions?
Here's my code so far:
string userName = BJConfigurationManager.GetSetting("ReportingServiceUsername");
string password = BJConfigurationManager.GetSetting("ReportingServicePassword");
NetworkCredential networkCredential = new NetworkCredential(userName, password);
Domain.Report report = GetReportById(id);
int timeout = 30; //seconds
string url = "https://bleh.ctp.reporting.database.windows.net/ReportServer/Pages/ReportViewer.aspx?...";
string destinationFileName = "#C:\\Temp.pdf";
// Create a web request to the URL
HttpWebRequest MyRequest = (HttpWebRequest)WebRequest.Create(url);
MyRequest.PreAuthenticate = true;
MyRequest.Credentials = networkCredential;
MyRequest.Timeout = timeout * 1000;
try
{
// Get the web response -- THE RESPONSE COMES BACK AS UNAUTHENTICATED...
HttpWebResponse MyResponse = (HttpWebResponse)MyRequest.GetResponse();

Check out the section titled "SOAP Management Endpoint Programmatic Access":
http://msdn.microsoft.com/en-us/library/windowsazure/771e88b6-ab0f-4910-a5fa-5facd8d56767#SOAPManagement.
It explains how to authenticate using a cookie container without a ReportViewer control.

I don't think that is going to work. Azure Reporting uses Forms Authentication and as I understand it, you aren't going to be able to match the Forms Auth cookie along with the MachineKey for encryption.

I was trying to accomplish the same task..but using a WebRequest was impossible.
I changed the approach using a ServerReport class like this:
ServerReport report;
report = new ServerReport();
report.ReportServerUrl = new Uri(reportServerName + "/ReportServer");
report.ReportPath = "/ReportPath";
report.ReportServerCredentials = new ReportServerCredentials();
report.SetParameters(new Microsoft.Reporting.WebForms.ReportParameter("param1", param1));
report.SetParameters(new Microsoft.Reporting.WebForms.ReportParameter("param2", param1));
return report.Render(reportParams.OutputFormat);
The ReportServerCredentials class must implement the IReportServerCredentials interface like this.
More info about the IReportServerCredentials interface and implementation here.

Related

How to connect TFS Online using PAT or OAUT?

Can't believe I'm stuck with a LOGIN :( hate when this happens.
Can somebody enlight me how to connect TF.EXE by using PAT password or in the best case an OAuth token?
I might add that I already have a Pat token and an OAuth token, not a problem while trying to get those, but every time I try this example:
TF.exe workspaces /collection:xxxx.visualstudio.com/xxxx /loginType:OAuth /login:.,MyPatTokenOrMyOauthToken /noprompt
I get the following response:
TF30063: You are not authorized to access xxxx.visualstudio.com\xxxx.
So, I Know command it's ok, because if I don't specify a login, a modal window prompts for credentials, and I tested already with that approach and works fine.
For the end, I might change everything to change tf.exe for the TFS api, but I'm unable to find same methods in the api (see reference: https://learn.microsoft.com/es-es/rest/api/vsts/?view=vsts )
If API has same methods than TF.exe, that will be useful, but so far I don't see same methods in the API.
Hope somebody has the solution for my problem.
Thanks in advance.
From my test, PAT token doesn't work in the following command, you have to get a OAuth token:
tf workspaces /collection:https://xxxx.visualstudio.com /loginType:OAuth /login:.,[OAuth token]
For the api that authenticate with Visual Studio Team Services (VSTS), you could refer to the examples in this link:
Here is an example getting a list of projects for your account:
REST API
using System.Net.Http;
using System.Net.Http.Headers;
...
//encode your personal access token
string credentials = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", "", personalAccessToken)));
ListofProjectsResponse.Projects viewModel = null;
//use the httpclient
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://{accountname}.visualstudio.com"); //url of our account
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials);
//connect to the REST endpoint
HttpResponseMessage response = client.GetAsync("_apis/projects?stateFilter=All&api-version=1.0").Result;
//check to see if we have a succesfull respond
if (response.IsSuccessStatusCode)
{
//set the viewmodel from the content in the response
viewModel = response.Content.ReadAsAsync<ListofProjectsResponse.Projects>().Result;
//var value = response.Content.ReadAsStringAsync().Result;
}
}
.Net Client Libraries
using Microsoft.TeamFoundation.Core.WebApi;
using Microsoft.VisualStudio.Services.Common;
...
//create uri and VssBasicCredential variables
Uri uri = new Uri(url);
VssBasicCredential credentials = new VssBasicCredential("", personalAccessToken);
using (ProjectHttpClient projectHttpClient = new ProjectHttpClient(uri, credentials))
{
IEnumerable<TeamProjectReference> projects = projectHttpClient.GetProjects().Result;
}
Add a screenshot:
Update:
I've tested with a new account, and the result is as below. If I remove /loginType and /login parameters, a window will pop up to ask me logon.
The screenshot without /loginType and /login parameters:
The screenshot with /loginType and /login parameters:

Cannot access UserInfo endpoint in IdentityServer4 doc example from Client

I'm testing out IdentityServer4, going through the documentation in order to learn more about OAuth2, OpenId Connect and Claim-based authentication, all of which I'm new at. However, some of the example code behaves weirdly and I can't figure out why...
So from my understanding, when given permission to access user data, the client can reach out to the UserInfo endpoint, which contains data such as claims, etc.
In IdentityServer4 there's even a setting
GetClaimsFromUserInfoEndpoint
that the documentation recommends we set to true.
So I'm following the IdentityServer4 startup guides and everything works perfectly until a point. This Quickstart contains the example code provided, although I'm assuming that I'm missing something obvious and seeing the code is not required.
Based on the openId Configuration page of the running server, the userinfo endpoint is located at
http://localhost:5000/connect/userinfo and when I try to access it via the browser I'm seeing a navbar which claims I'm logged in, but the body of the page is a signin prompt. Looks weird but I'm assuming that this is because I'm logged in at localhost:5000 (IdentityServer4), but I'm not sending the userId token which I got for the client on localhost:5002.
So I wrote the following code on my client app:
public async Task<IActionResult> GetData()
{
var accessToken = HttpContext.Authentication.GetTokenAsync("access_token").Result;
HttpClient client = new HttpClient();
client.SetBearerToken(accessToken);
var userInfo = await client.GetStringAsync("http://localhost:5000/connect/userinfo");
return Content(userInfo);
}
Here I know that GetTokenAsync("access_token") should work as it's used in other places in the example project by the client app that connect to an API. However, the responce I'm getting is again the layout page of IdentityServer and a log in prompt.
Any idea what my mistake is and how to access the UserInfo endpoint?
Edit: removed thread-blocking so that I don't show strangers shameful test code
Ok, so it turns out that this code should have a simplified version, namely:
UserInfoClient uic = new UserInfoClient("http://localhost:5000", idToken);
var result = await uic.GetAsync();
return Content(JsonConvert.SerializeObject(result.Claims));
Yet, the problem persists, even the encapsulated code inside UserInfoClient hits the brick wall of "no user endpoint data, just the layout for the example website".
It's probably little late to answer, but for anyone who is still stumbling upon this, try this ---
var accessToken = await HttpContext.Authentication.GetTokenAsync("access_token");
var client = new HttpClient();
client.SetBearerToken(accessToken);
var userInfoClient = new UserInfoClient("http://localhost:5000/connect/userinfo");
var response = await userInfoClient.GetAsync(accessToken);
var claims = response.Claims;
You can also get the list of claims on the client app like -
var claims = HttpContext.User.Claims.ToList();
without calling the endpoint.

Sharepoint 2013 REST api from desktop application - Authentication

I am trying to consume SharePoint 2013 REST services from a Desktop application ( cross-platform, cross-os ). Application is basically a HTML page in application view.
Is there a simple way I can authenticate my calls using HTTP methods ?
Yes, you can get authenticated and receive a digest via a REST call.
string url = "http://Your.SP.Site";
HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
client.BaseAddress = new System.Uri(url);
string cmd = "_api/contextinfo";
client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
client.DefaultRequestHeaders.Add("ContentType", "application/json");
client.DefaultRequestHeaders.Add("ContentLength", "0");
StringContent httpContent = new StringContent("");
var response = client.PostAsync(cmd, httpContent).Result;
if (response.IsSuccessStatusCode)
{
string content = response.Content.ReadAsStringAsync().Result;
JsonObject val = JsonValue.Parse(content).GetObject();
JsonObject d = val.GetNamedObject("d");
JsonObject wi = d.GetNamedObject("GetContextWebInformation");
retVal = wi.GetNamedString("FormDigestValue");
}
The above example shows how to retrieve the digest in C# with the HttpClient. This string needs to be passed as a header to all of the other rest calls you make to carry forward the authentication. You can create a credential by passing in a username and password if needed.
I have more examples here:
https://arcandotnet.wordpress.com/2015/04/01/sharepoint-2013-rest-services-using-c-and-the-httpclient-for-windows-store-apps/
You can do these calls in JavaScript as well and Microsoft has a lot of documentation on that. There is also .NET library, Microsoft.SharePoint.Client.DLL (CSOM) that simplifies this type of coding but you must have the library installed on the client.

Adding authentication to security header in WCF to consume Metro WSIT service

I use this simple way to attach username and password to the SOAP request header. This works fine inside Java boundaries, but I want to be able to call it with my WCF client. How do I do this?
I've tried the following code, but it does not include the credentials in the header:
wsClient.ClientCredentials.UserName.UserName = "Hello";
wsClient.ClientCredentials.UserName.Password = "World";
Thanks in advance!
That is quite awful non-standardized way. It uses custom HTTP Headers so you cannot expect that built in WCF mechanism will magically support such approach. How should WCF know that you want to add custom non-standard HTTP header to HTTP request (not SOAP header)?
Use this:
var proxy = new YourServiceClient();
using (var scope = new OperationContextScope(proxy.InnerChannel))
{
var prop = new HttpRequestMessageProperty();
prop.Headers.Add("UserName", "Hello");
prop.Headers.Add("Password", "World");
OperationContext context = OperationContext.Current;
context.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = prop;
proxy.CallYourOperation();
}

.net WCF - CXF/WSS4j interoperability

I would like to consume a CXF web-service from a .net c# client. We are currently working with java-to-java requests and we protect SOAP envelopes through ws-security (WSS4J library).
My question is: how can I implement a C# WS-client which produces the same SOAP requests as the following client-side java code?
//doc is the original SOAP envelope to process with WSS4J
WSSecHeader secHeader = new WSSecHeader();
secHeader.insertSecurityHeader(doc);
//add username token with password digest
WSSecUsernameToken usrNameTok = new WSSecUsernameToken();
usrNameTok.setPasswordType(WSConstants.PASSWORD_DIGEST);
usrNameTok.setUserInfo("guest",psw_guest);
usrNameTok.prepare(doc);
usrNameTok.appendToHeader(secHeader);
//sign the envelope body with client key
WSSecSignature sign = new WSSecSignature();
sign.setUserInfo("clientx509v1", psw_clientx509v1);
sign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
Document signedDoc = null;
sign.prepare(doc, sigCrypto, secHeader);
signedDoc = sign.build(doc, sigCrypto, secHeader);
//encrypt envelope body with server public key
WSSecEncrypt encrypt = new WSSecEncrypt();
encrypt.setUserInfo("serverx509v1");
// build the encrypted SOAP part
String out = null;
Document encryptedDoc = encrypt.build(signedDoc, encCrypto, secHeader);
return encryptedDoc;
Does anybody know where I could find a microsoft how-to or a .net working example?
================================ EDIT ====================================
Thank you Ladislav! I applied your suggestions and I came up with something like:
X509Certificate2 client_pk, server_cert;
client_pk = new X509Certificate2(#"C:\x509\clientKey.pem", "blablabla");
server_cert = new X509Certificate2(#"C:\x509\server-cert.pfx", "blablabla");
// Create the binding.
System.ServiceModel.WSHttpBinding myBinding = new WSHttpBinding();
myBinding.TextEncoding = ASCIIEncoding.UTF8;
myBinding.MessageEncoding = WSMessageEncoding.Text;
myBinding.Security.Mode = SecurityMode.Message;
myBinding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
myBinding.Security.Message.AlgorithmSuite =
System.ServiceModel.Security.SecurityAlgorithmSuite.Basic128;
// Disable credential negotiation and the establishment of
// a security context.
myBinding.Security.Message.NegotiateServiceCredential = false;
myBinding.Security.Message.EstablishSecurityContext = false;
// Create the endpoint address.
EndpointAddress ea =
new EndpointAddress(new Uri("http://bla.bla.bla"),
EndpointIdentity.CreateDnsIdentity("issuer"));
// configure the username credentials on the channel factory
UsernameClientCredentials credentials = new UsernameClientCredentials(new
UsernameInfo("superadmin", "secret"));
// Create the client.
PersistenceClient client = new PersistenceClient(myBinding, ea);
client.Endpoint.Contract.ProtectionLevel =
System.Net.Security.ProtectionLevel.EncryptAndSign;
// replace ClientCredentials with UsernameClientCredentials
client.Endpoint.Behaviors.Remove(typeof(ClientCredentials));
client.Endpoint.Behaviors.Add(credentials);
// Specify a certificate to use for authenticating the client.
client.ClientCredentials.ClientCertificate.Certificate = client_pk;
// Specify a default certificate for the service.
client.ClientCredentials.ServiceCertificate.DefaultCertificate = server_cert;
// Begin using the client.
client.Open();
clientProxyNetwork[] response = client.GetAllNetwork();
As a result I get (server-side) the following CXF exception:
java.security.SignatureException: Signature does not match.
at sun.security.x509.X509CertImpl.verify(X509CertImpl.java:421)
at sun.security.provider.certpath.BasicChecker.verifySignature(BasicChecker.java:133)
at sun.security.provider.certpath.BasicChecker.check(BasicChecker.java:112)
at sun.security.provider.certpath.PKIXMasterCertPathValidator.validate (PKIXMasterCertPathValidator.java:117)
Therefore it seems a key jks->pem conversion problem... Or am I am missing something in the client-code above?
Well, in the end the solution is to encrypt and sign the whole username token. As for the interoperability, the ws addressing must be activated in cxf and a custom binding in c# is needed. The custom binding that did the trick is basically
AsymmetricSecurityBindingElement abe =
(AsymmetricSecurityBindingElement)SecurityBindingElement.
CreateMutualCertificateBindingElement(MessageSecurityVersion.
WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10);
           
Wcf signs each ws addressing element, therefore the same must be done server side.
This is usually pretty big problem because WCF does not support UserNameToken Profile with Digested password. I needed it few months ago and we had to implement our own custom binding but that code is not ready for publishing. Fortunatelly this blog article describes other implementation and contains sample code with new UserNameClientCredentials class supporting digested password.
Btw. same security configuration should be possible with older API called WSE 3.0. It was replaced by WCF but still some WS-* stack configuration are much simpler with that API and old ASMX services.