I have a web application and web api services that authenticate through ADFS. They are contained in the same IIS application, and the web app makes calls back to the web api services without a problem.
I'm now trying to call the same services from a different application, but am having trouble passing the token. I am able to authenticate and retrieve a SAML token with the following code:
var stsEndpoint = "https://MyAdfsServer/adfs/services/trust/13/UsernameMixed";
var reliantPartyUri = "https://MyDomain/AppRoot/";
var factory = new Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannelFactory(
new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
new EndpointAddress(stsEndpoint));
factory.TrustVersion = System.ServiceModel.Security.TrustVersion.WSTrust13;
// Username and Password here...
factory.Credentials.UserName.UserName = #"Domain\UserName";
factory.Credentials.UserName.Password = "Password";
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
AppliesTo = new EndpointAddress(reliantPartyUri),
KeyType = KeyTypes.Bearer,
};
var channel = factory.CreateChannel();
var token = channel.Issue(rst) as GenericXmlSecurityToken;
var saml = token.TokenXml.OuterXml;
However, I'm not sure how to pass the saml in to the web api call. I've tried this:
using (var handler = new HttpClientHandler()
{
ClientCertificateOptions = ClientCertificateOption.Automatic,
AllowAutoRedirect = false
})
{
using (var client = new HttpClient(handler))
{
client.BaseAddress = new Uri("https://MyDomain/AppRoot/api/");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("SAML", saml);
HttpResponseMessage response = client.GetAsync("MyService/Get/").Result;
// Get the results...
var result = response.Content.ReadAsStringAsync().Result;
var status = response.StatusCode;
}
}
This is returning a status code of 302 and trying to redirect me to the ADFS server for authentication. Is there another way to pass the SAML token to the web api service?
(SET)
string samlString = "blah blah blah";
byte[] bytes = Encoding.UTF8.GetBytes(samlString);
string base64SamlString = Convert.ToBase64String(bytes);
myHttpClient.DefaultRequestHeaders.Add("X-My-Custom-Header", base64SamlString);
(GET)
IEnumerable<string> headerValues = request.Headers.GetValues("X-My-Custom-Header");
if (null != headerValues)
{
var encoding = Encoding.GetEncoding("iso-8859-1");
string samlToken = encoding.GetString(Convert.FromBase64String(headerValues.FirstOrDefault()));
}
When you want to access a resource which is protected via SSO (like ADFS, I assume), I found it easiest to use following approach: Show a WebBrowser element, let user enter credentials, then grab global cookies, and pass them into new HttpClient which performs the actual HTTP operation.
Here a complete code sample which downloads all build statuses from a SAML-protected Jenkins server:
private void Start()
{
var t = new Thread(ThreadProc);
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
}
public async void ThreadProc()
{
try
{
var urlBase = "https://JENKINS/";
var url = urlBase + "job/JOBNAME/api/json?depth=1&tree=lastBuild[timestamp],builds[number,result,timestamp,url,actions[lastBuiltRevision[SHA1,branch[name]],totalCount,failCount,skipCount],building,duration]";
var form = new Form();
var browser = new System.Windows.Forms.WebBrowser();
browser.SetBounds(0, 0, 400, 400);
form.Size = new System.Drawing.Size(400, 400);
form.Controls.AddRange(new Control[] { browser });
form.FormBorderStyle = FormBorderStyle.FixedDialog;
form.StartPosition = FormStartPosition.CenterScreen;
form.MinimizeBox = false;
form.MaximizeBox = false;
// Navigate to base URL. It should internally forward to login form. After logging in, close browser window.
browser.Navigate(urlBase);
form.ShowDialog();
var cookieString = GetGlobalCookies(urlBase);
var cookieContainer = new System.Net.CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = new Uri(urlBase, UriKind.Absolute) })
{
cookieContainer.SetCookies(client.BaseAddress, cookieString);
var response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
var responseStream = await response.Content.ReadAsStreamAsync();
using (var reader = new System.IO.StreamReader(responseStream))
{
var responseString = await reader.ReadToEndAsync();
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
[System.Runtime.InteropServices.DllImport("wininet.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
private static extern bool InternetGetCookieEx(string pchURL, string pchCookieName,
System.Text.StringBuilder pchCookieData, ref uint pcchCookieData, int dwFlags, IntPtr lpReserved);
private const int INTERNET_COOKIE_HTTPONLY = 0x00002000;
public string GetGlobalCookies(string uri)
{
uint uiDataSize = 2048;
var sbCookieData = new System.Text.StringBuilder((int)uiDataSize);
if (InternetGetCookieEx(uri, null, sbCookieData, ref uiDataSize,
INTERNET_COOKIE_HTTPONLY, IntPtr.Zero)
&&
sbCookieData.Length > 0)
{
return sbCookieData.ToString().Replace(";", ",");
}
return null;
}
Related
I am working on an asp.net web application where I have to create users directly in Tableau Server using Rest API and C#. The users are local users and not AD or SAML users.
I have coded the part however, I am getting Unauthorised as the result. Below is my code.
public static string site_id { get; set; }
public static string token { get; set; }
static void Main(string[] args)
{
CallWebAPIAsync().Wait();
}
static async Task CallWebAPIAsync()
{
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
client.BaseAddress = new Uri("https://serverurl.com");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/octet-stream"));
SignIn(client);
Users(client, site_id, token);
CreateUser(client, site_id, token);
}
Console.Read();
}
public class CustomXmlMediaTypeFormatter : XmlMediaTypeFormatter
{
public CustomXmlMediaTypeFormatter()
{
UseXmlSerializer = true;
WriterSettings.OmitXmlDeclaration = false;
}
}
public static tableauCredentialsType SignIn(HttpClient client)
{
var name = "Administrator";
var password = "Password";
var site = string.Empty;
var tableauCredentialsType = new tsRequest()
{
Item = new tableauCredentialsType
{
name = name,
password = password,
site = new siteType() { contentUrl = site }
}
};
var httpContent = new ObjectContent<tsRequest>(tableauCredentialsType, new CustomXmlMediaTypeFormatter());
var httpResponseMessage = client.PostAsync("/api/3.2/auth/signin", httpContent).Result;
if (httpResponseMessage.StatusCode != System.Net.HttpStatusCode.OK)
throw new Exception(string.Format("Unable to login to the server", site, name));
var responseLogin = httpResponseMessage.Content.ReadAsAsync<tsResponse>(new List<MediaTypeFormatter>() { new CustomXmlMediaTypeFormatter() }).Result;
var resultTableauCredentialsType = ((tableauCredentialsType)responseLogin.Items[0]);
site_id = resultTableauCredentialsType.site.id;
token = resultTableauCredentialsType.token;
return resultTableauCredentialsType;
}
public static userType CreateUser(HttpClient client, string siteid, string auth_token)
{
var name = "user#domain.com";
var userType = new tsRequest()
{
Item = new userType
{
name = name,
siteRole = siteRoleType.ExplorerCanPublish,
authSetting = siteUserAuthSettingType.ServerDefault
}
};
var httpContent = new ObjectContent<tsRequest>(userType, new CustomXmlMediaTypeFormatter());
client.DefaultRequestHeaders.Add("x-tableau-auth", auth_token);
var uri = string.Format("/api/3.2/sites/{0}/users", siteid);
var httpResponseMessage = client.PostAsync(uri, httpContent).Result;
if (httpResponseMessage.StatusCode != System.Net.HttpStatusCode.OK)
throw new Exception(string.Format("Unable to create user in the server", siteid, name));
var responseLogin = httpResponseMessage.Content.ReadAsAsync<tsResponse>(new List<MediaTypeFormatter>() { new CustomXmlMediaTypeFormatter() }).Result;
var resultTableauCredentialsType = ((userType)responseLogin.Items[0]);
return resultTableauCredentialsType;
}
I was able to successfully login into the server and was also able to provide the x-tableau-auth to my client headers.
The code is working fine except the post sync method in Create user method. Once the code executes, it throws UnAuthorized error as the response from the server. My user is the administrator in Tableau Server and when I query the workbooks and users in Tableau Server with the same x-tableau-auth, it's working fine.
I am not good in API programming and need some help in solving out this issue. Any help is highly appreciated.
Hy I have written an Console Application and i want to connect to the Identity-Server. With the NamedPipeServerStream i want to get Result. However, it always tries to connect to the Server.
Here is my code:
var options = new OidcClientOptions
{
Authority = authority,
ClientId = clientId,
ClientSecret = clientSecret,
Scope = scope,
RedirectUri = redirectUrl,
ResponseMode = OidcClientOptions.AuthorizeResponseMode.Redirect
};
_oidcClient = new OidcClient(options);
var state = await _oidcClient.PrepareLoginAsync();
var callbackManager = new CallbackManager(state.State);
Process.Start(state.StartUrl);
var response = await callbackManager.RunServer();
var result = await _oidcClient.ProcessResponseAsync(response, state);
Here is my Callbackmanager
public async Task<string> RunServer(CancellationToken? token = null)
{
token = CancellationToken.None;
using (var server = new NamedPipeServerStream(_name, PipeDirection.In))
{
//here it spos and waits for the Connection
await server.WaitForConnectionAsync(token.Value);
using (var sr = new StreamReader(server))
{
var msg = await sr.ReadToEndAsync();
return msg;
}
}
}
the solution was to add an Flow:
Flow = OidcClientOptions.AuthenticationFlow.AuthorizationCode
now it works
I have got error with RestSharp component when i am call Paypal Rest API.
I have the following code using Xamarin for Android.
public async Task<PayPalGetTokenResponse> GetAccessToken()
{
var restRequest = new RestRequest("/oauth2/token", Method.POST);
// Add headers
restRequest.AddHeader("Accept", "application/json");
restRequest.AddHeader("Accept-Language", "en_US");
// Make Authorization header
restClient.Authenticator = new HttpBasicAuthenticator(Config.ApiClientId, Config.ApiSecret);
// add data to send
restRequest.AddParameter("grant_type", "client_credentials");
var response = restClient.Execute<PayPalGetTokenResponse>(restRequest);
response.Data.DisplayError = CheckResponseStatus(response, HttpStatusCode.OK);
return response.Data;
}
But got error :"Error: SecureChannelFailure (The authentication or decryption has failed.)"
I have Also use ModernHttpClient but got same error
public async Task<PayPalGetTokenResponse> GetAccessToken()
{
string clientId = Config.ApiClientId;
string secret = Config.ApiSecret;
string oAuthCredentials = Convert.ToBase64String(Encoding.Default.GetBytes(clientId + ":" + secret));
string uriString = Config.ApiUrl+"/oauth2/token";
PayPalGetTokenResponse result;
HttpClient client = new HttpClient(new NativeMessageHandler());
var h_request = new HttpRequestMessage(HttpMethod.Post, uriString);
h_request.Headers.Authorization = new AuthenticationHeaderValue("Basic", oAuthCredentials);
h_request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
h_request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue("en_US"));
h_request.Content = new StringContent("grant_type=client_credentials", UTF8Encoding.UTF8);
try
{
HttpResponseMessage response = await client.SendAsync(h_request);
//if call failed ErrorResponse created...simple class with response properties
if (!response.IsSuccessStatusCode)
{
var error = await response.Content.ReadAsStringAsync();
var errResp = JsonConvert.DeserializeObject<string>(error);
//throw new PayPalException { error_name = errResp.name, details = errResp.details, message = errResp.message };
}
var success = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<PayPalGetTokenResponse>(success);
}
catch (Exception)
{
throw new HttpRequestException("Request to PayPal Service failed.");
}
return result;
}
Have you tried to force to modern day SSL protocol?
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
This works for me:
if (ServicePointManager.SecurityProtocol != SecurityProtocolType.Tls12)
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var client = new RestClient(payPalURL) {
Encoding = Encoding.UTF8
};
var authRequest = new RestRequest("oauth2/token", Method.POST) {
RequestFormat = DataFormat.Json
};
client.Authenticator = new HttpBasicAuthenticator(clientId, secret);
authRequest.AddParameter("grant_type","client_credentials");
var authResponse = client.Execute(authRequest);
I'm coding a Xamarin cross-platform mobile app. The server is a SpringMVC server which uses JWT Tokens to authenticate against each of the endpoints/webservices. So basically when I'm doing a request to a webservice for first time, before I need to hit a /authorize POST endpoint sending my email and password, the endpoint response will contain in the "Cookie" header an authenticaton token which comes as "AUTH_TOKEN={MD5-String}". Once I got the token I send the request to the endpoint, let's say /feed. But my problem is that I cannot figure out the way of setting the "Cookie" header in the C# HttpClient. I tried everything but the endpoing just keeps responding with the login screen html instead of the actual JSON response. I tried the same steps in Postman and other REST clients and It worked. So It means that I'm doing something wrong. Here's my code:
public class RestService : IRestService
{
HttpClient client;
HttpClientHandler handler;
CookieContainer cookies;
string authToken;
public List<Feed> FeedItems { get; private set; }
public RestService()
{
cookies = new CookieContainer();
handler = new HttpClientHandler();
handler.UseCookies = true; //Otherwise It'll not use the cookies container!!
handler.CookieContainer = cookies;
client = new HttpClient(handler);
client.MaxResponseContentBufferSize = 256000;
}
public async Task<List<Role>> GetFeedDataAsync()
{
//Request credentials
//Credentials validation
var credentials = new HalliganCredential()
{
email = Constants.Username,
password = Constants.Password
};
var jsonCredentials = JsonConvert.SerializeObject(credentials);
var jsonCredentialsContent = new StringContent(jsonCredentials, Encoding.UTF8, "application/json");
var authorizeUri = new Uri(Constants.AuthorizeEndpoint);
var authorizeResponse = await client.PostAsync(authorizeUri, jsonCredentialsContent);
if (authorizeResponse.IsSuccessStatusCode)
{
//If authentication went OK
IEnumerable<Cookie> responseCookies = cookies.GetCookies(authorizeUri).Cast<Cookie>();
foreach (Cookie cookie in responseCookies)
{
if (cookie.Name.Equals("AUTH-TOKEN"))
{
authToken = cookie.Value;
}
}
}
else
{
//Authentication failed throw error
throw new HttpRequestException("Authentication failed");
}
FeedItems = new List<Feed>();
//Making the GET request
var uri = new Uri(string.Format(Constants.FeedEnpoint, string.Empty));
try
{
cookies.Add(uri, new Cookie("Cookie", string.Format("AUTH_TOKEN={0}", authToken)));
client.DefaultRequestHeaders.Add("Cookie", string.Format("AUTH_TOKEN={0}", authToken));
handler.CookieContainer.Add(uri, new Cookie("Cookie", string.Format("AUTH_TOKEN={0}", authToken)));
var response = await client.GetAsync(uri);
response.EnsureSuccessStatusCode();
//Credentials validation
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
FeedItems = JsonConvert.DeserializeObject<List<Feed>>(content);
}
}
catch (Exception ex)
{
Debug.WriteLine(#"ERROR {0}", ex.Message);
}
return FeedItems;
}
}
When I reach the line var content = await response.Content.ReadAsStringAsync(); the response is an HTML string instead of the actual JSON response.
I tried with several other key values for the header, although "Cookie" is the one that worked on Postman.
I tried with "Set-Cookie", "set-cookie", "Set-Cookie", setting the header as "AUTH_TOKEN". I tried all this convinations in different places like adding them in the cookie CookieContainer, in the handler CookieContainer and in the client.DefaultRequestHeaders.
I tried setting on and off the handler.UseCookies = true; //Otherwise It'll not use the cookies container!! line.
Any help will be welcome!
UPDATE
I tried with one of the suggested solutions but didn't work I tried either with UseCookies in true and false.
//Making the GET request
var baseAddress = new Uri("http://app.******.io");
using (var handler = new HttpClientHandler { UseCookies = true })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
var message = new HttpRequestMessage(HttpMethod.Get, "/api/v1/feed?api_key=sarasa");
message.Headers.Add("Cookie", string.Format("AUTH_TOKEN={0}", authToken));
message.Headers.Add("Cookie", string.Format("AUTH_TOKEN={0};", authToken));
message.Headers.Add("Set-Cookie", string.Format("AUTH_TOKEN={0}", authToken));
message.Headers.Add("AUTH_TOKEN", authToken);
var result = await client.SendAsync(message);
result.EnsureSuccessStatusCode();
if (result.IsSuccessStatusCode)
{
var content = await result.Content.ReadAsStringAsync();
FeedItems= JsonConvert.DeserializeObject<List<Feed>>(content);
}
}
return FeedItems;
UPDATE
I tried with the another solution, same results.
var baseAddress = new Uri("http://app.*****.io");
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
cookieContainer.Add(baseAddress, new Cookie("Cookie", string.Format("AUTH_TOKEN={0}", authToken)));
cookieContainer.Add(baseAddress, new Cookie("Set-Cookie", string.Format("AUTH_TOKEN={0}", authToken)));
var result = client.GetAsync("/api/v1/roles?api_key=sarasa").Result;
result.EnsureSuccessStatusCode();
if (result.IsSuccessStatusCode)
{
var content = await result.Content.ReadAsStringAsync();
RolesItems = JsonConvert.DeserializeObject<List<Role>>(content);
}
}
Is there an alternative to HttpClient?
I finally could set the Cookie header parameter, but I change HttpClient by HttpWebRequest
Getting the Cookies
//Credentials validation
var credentials = new CompanyCredential()
{
Email = Constants.Username,
Password = Constants.Password
};
var jsonCredentials = JsonConvert.SerializeObject(credentials);
var request = (HttpWebRequest) WebRequest.Create(new Uri(baseAddress, Constants.AuthorizeEndpoint));
request.ContentType = "application/json";
request.Method = "POST";
var requestStream = request.GetRequestStreamAsync().Result;
var streamWriter = new StreamWriter(requestStream);
streamWriter.Write(jsonCredentials);
streamWriter.Flush();
try
{
HttpWebResponse response = (HttpWebResponse) request.GetResponseAsync().Result;
if (response.StatusCode.Equals(HttpStatusCode.OK))
{
authToken = response.Headers["Set-Cookie"];
tokenExpireDate = DateTime.ParseExact(response.Headers["Expires"], "yyyy-MM-dd HH:mm:ss,fff",
System.Globalization.CultureInfo.InvariantCulture);
}
else
{
//Authentication failed throw error
throw new HttpRequestException("Authentication failed");
}
} catch (Exception e)
{
Debug.WriteLine(string.Format("Warning: {0}", e.Message));
}
Setting the Cookies
var request = (HttpWebRequest)WebRequest.Create(new Uri(baseAddress, endpoint));
SetHeaders(request);
if (string.IsNullOrEmpty(authToken))
{
throw new AuthTokenNullException();
}
request.Headers["Cookie"] = authToken;
request.Method = "GET";
HttpWebResponse response = request.GetResponseAsync().Result as HttpWebResponse;
if (!response.StatusCode.Equals(HttpStatusCode.OK))
{
throw new HttpRequestException(string.Format("Warning expected response as 200 and got {0}", Convert.ToString(response.StatusCode)));
}
var reader = new StreamReader(response.GetResponseStream());
string stringResponse = reader.ReadToEnd();
return JsonConvert.DeserializeObject<T>(stringResponse);
I'm trying to login a website for parsing purpose. while program runs it seems to login to web site. I can see that from fiddler. But at the end of the program, even the status code seems tobe 200, ı find myself not logged in.
string loginUri = "https://www.sample.com";
string username = "username";
string password = "password";
CookieContainer cc = new CookieContainer();
var handler = new HttpClientHandler { CookieContainer = cc };
var request = new HttpRequestMessage(HttpMethod.Post, loginUri);
handler.AllowAutoRedirect = true;
request.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "return_url", "emagaza.php"},
{ "user_login", "sampleuser"},
{ "password", "sapmplepass"},
{ "dispatch[auth.login]", "Giriş yap"}
});
var client = new HttpClient(handler);
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Any attempt to use cookies failed.
I tried this;
httpWebRequest2.CookieContainer = cc;
HttpWebResponse response3 = (HttpWebResponse)httpWebRequest2.GetResponse();
and I tried this;
var handler2 = new HttpClientHandler { CookieContainer = cc };
var request2 = new HttpRequestMessage(HttpMethod.Get, loginUri);
var response2 = await client.SendAsync(request2);