Using AuthenticationContext & ActiveDirectoryClient with an application proxy? - azure

Using the Azure Active Directory Graph Client API, how would I configure the underlying HttpClient to use an HttpClientHander, where I can define an authenticated application proxy?
var proxy = new WebProxy(...);
proxy.Credentials = ...;
var handler = new HttpClientHandler { Proxy = proxy, UseProxy = true};
var auth = new AuthenticationContext(...);
var client = new ActiveDirectoryClient(...);
Or, can I not use the Graph Client behind a proxy?
Thanks

I was exploring the same problem. It took some digging, but I found a solution. Now, I realize you asked specifically how to apply an HttpClientHandler. I don't know if this can be done; however, you can apply a proxy. Here's how.
The ActiveDirectoryClient class provides a DataServiceContextWrapper property called Context, which is, not surprisingly, a wrapper to a DataServiceContext.
This is good. It reduces the problem to figuring out how to apply a proxy to the DataServiceContext class. I used some old code I had sitting around, and things pretty much exploded. This is because I used the deprecated SendingRequest event to intercept a request and apply a proxy before it goes out the door. This client is not compatible with the deprecated event.
It took a little more digging to figure out how to do it with the SendingRequest2 event; it only required a little type casting.
Do this:
var client = new ActiveDirectoryClient(...);
client.Context.SendingRequest2 += OnSendingRequest2;
// ...
static void OnSendingRequest2(object sender, SendingRequest2EventArgse)
{
var request = ((HttpWebRequestMessage)e.RequestMessage).HttpWebRequest;
request.Proxy = new WebProxy("http://myproxy:port");
}
Don't Do This: (It is deprecated and will produce an exception.)
var client = new ActiveDirectoryClient(...);
client.Context.SendingRequest += OnSendingRequest;
// ...
static void OnSendingRequest(object sender, SendingRequestEventArgs e)
{
e.Request.Proxy = new WebProxy("http://myproxy:port");
}

a bit late but i ran into the same issue.
using the code below in app.config saved my day !
<system.net>
<defaultProxy useDefaultCredentials="true" />
</system.net>

Related

Dotnet Core 2.0.3 Migration | Encoding unable to translate bytes [8B]

Im not sure if this should just go direct to the github but I thought id check here first if anyone has encountered this issue before.
I recently have upgraded one of my apps to use dot net 2.0.3 From 1.1.4.
Everything works fine locally but when I deploy to my app service in azure I get the following exception.
System.Text.DecoderFallbackException: Unable to translate bytes [8B] at index 1 from specified code page to Unicode.
The code that calls it is a httpclient that talks between the apps.
public async Task<T1> Get<T1>(string url, Dictionary<string, string> urlParameters = null) where T1 : DefaultResponse, new()
{
var authToken = _contextAccessor.HttpContext.Request.Cookies["authToken"];
using (var client = new HttpClient().AcceptJson().Acceptgzip().AddAuthToken(authToken))
{
var apiResponse = await client.GetAsync(CreateRequest(url, urlParameters));
T1 output;
if (apiResponse.IsSuccessStatusCode)
{
output = await apiResponse.Content.ReadAsAsync<T1>();
//output.Succeeded = true;
}
else
{
output = new T1();
var errorData = GlobalNonSuccessResponseHandler.Handle(apiResponse);
output.Succeeded = false;
output.Messages.Add(errorData);
}
return output;
}
}
public static HttpClient AcceptJson(this HttpClient client)
{
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
public static HttpClient Acceptgzip(this HttpClient client)
{
// Commenting this out fixes the issue.
//client.DefaultRequestHeaders.AcceptEncoding.Add(StringWithQualityHeaderValue.Parse("gzip"));
client.DefaultRequestHeaders.AcceptEncoding.Add(StringWithQualityHeaderValue.Parse("deflate"));
return client;
}
public static HttpClient AddAuthToken(this HttpClient client, string authToken)
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
return client;
}
Im a bit stumped as to whats going on.
So I have 2 apps which we call client and server from now on.
Client uses the above code to talk to the server.
Locally this is fine on azure not so, this all worked fine before upgrading.
So I setup the client locally to talk to the server on azure I was able to replicate the issue.
I had a look at the response in fiddler and it is able to correctly decode it.
If anyone has any idea where I should look and has seen it before any info would be great :D.
UPDATE 1
So after some more digging I decided to remove gzip and then everything started working.
client.DefaultRequestHeaders.AcceptEncoding.Add(StringWithQualityHeaderValue.Parse("gzip"));
Can anyone explain this?
8B can be a second byte of multi-byte UTF8 character. The DecoderFallbackException tells that you’re interpreting the data as some other encoding. Probably Latin-1 which doesn’t have 8B character.
In fiddler, you should look at the content-type HTTP header in the response. If it says application/json or application/json; charset=utf-8, it’s probably a bug in .NET, because even without charset=utf-8 RFC 4627 says the default encoding is already UTF-8.
If it says something else, I would try changing the server so it sends the correct content-type header in the response.

Azure Schema Extensions in Graph Client

Whatever I tried I cannot set an extension property on a User object, here is a reproducible piece of code:
public async Task CleanTest(string extName)
{
ExtensionProperty ep = new ExtensionProperty
{
Name = extName,
DataType = "String",
TargetObjects = { "User" }
};
App app = (App)(await _client.Applications.Where(a => a.AppId == _managementAppClientId).ExecuteSingleAsync());
app.ExtensionProperties.Add(ep);
await app.UpdateAsync();
GraphUser user = (GraphUser)(await _client.Users.Where(u => u.UserPrincipalName.Equals("email")).ExecuteSingleAsync());
string propName = FormatExtensionPropertyName(extName); //formats properly as extesion_xxx_name
user.SetExtendedProperty(propName, "testvalue");
//user.SetExtendedProperty(extName, "testvalue");
await user.UpdateAsync(); // fails here
}
user.UpdateAsync() according to Fiddler doesn't even go out and application fails with an exception:
"The property 'extension_e206e28ff36244b19bc56c01160b9cf0_UserEEEqdbtgd3ixx2' does not exist on type 'Microsoft.Azure.ActiveDirectory.GraphClient.Internal.User'. Make sure to only use property names that are defined by the type."
This issue is also being tracked here:
https://github.com/Azure-Samples/active-directory-dotnet-graphapi-console/issues/28
I've got an alternative workaround for this bug, for those that want to use the version 5.7 OData libraries rather than redirecting to the v5.6.4 versions.
Add a request pipeline configuration handler.
// initialize in the usual way
ActiveDirectoryClient activeDirectoryClient =
AuthenticationHelper.GetActiveDirectoryClientAsApplication();
// after initialization add a handler to the request pipline configuration.
activeDirectoryClient.Context
.Configurations.RequestPipeline
.OnMessageWriterSettingsCreated(UndeclaredPropertyHandler);
In the handler, change the ODataUndeclaredPropertyBehaviorKinds value on the writer settings to SupportUndeclaredValueProperty.
private static void UndeclaredPropertyHandler(MessageWriterSettingsArgs args)
{
var field = args.Settings.GetType().GetField("settings",
BindingFlags.NonPublic | BindingFlags.Instance);
var settingsObject = field?.GetValue(args.Settings);
var settings = settingsObject as ODataMessageWriterSettings;
if (settings != null)
{
settings.UndeclaredPropertyBehaviorKinds =
ODataUndeclaredPropertyBehaviorKinds.SupportUndeclaredValueProperty;
}
}
Just in case you still looking for solution to this problem or someone else is facing the same issue:
I got similar issue and it looks like, at least for me, the problem was in latest version of "Microsoft.Data.Services.Client" package - 5.7.0 (or in one of it dependencies). When I downgraded to previous version - 5.6.4 it worked as a charm.
I had same symptoms - updating of extended property was failing even w/o any request is made (also used Fiddler)
Hope it helps!
Artem Liman

servicestack server events with azure RedisServerEvents doesn't work, while default option MemoryServerEvents works

Question on Servicestack ServerEvents using Azure Redis cache..
Server Code:
I have these lines under Configure method of Global.asax file
Plugins.Add(new ServerEventsFeature {
OnConnect = (res,httpReq) => res.Publish("cmd.onConnect","Message on connect") ,
OnCreated = (res,httpReq) => res.Publish("cmd.onConnect","Message on create"),
...I have custom message for OnSubscription and OnPublish as well
})
**var redisHost = AppSettings.GetString("RedisHost");
container.Register<IRedisClientsManager>(
new RedisManagerPool(redisHost));
container.Register<IServerEvents>(c =>
new RedisServerEvents(c.Resolve<IRedisClientsManager>()));
container.Resolve<IServerEvents>().Start();**
Following is the value format I have in my web.config, for the redis connection
add key="RedisHost" value="passKey#Hostname:6380?ssl=true"
Client Code:
ServerEventConnect connectMsg = null;
var msgs = new List<ServerEventMessage>();
var commands = new List<ServerEventMessage>();
var errors = new List<Exception>();
var client = new ServerEventsClient(baseUri,"home") {
OnConnect = e => connectMsg = e,
OnCommand = commands.Add,
OnMessage = msgs.Add,
OnException = errors.Add,
}.Start();
var connectMsg = client.Connect();
var joinMsg = client.WaitForNextCommand();
connectMsg.Wait();//It gets connnected
joinMsg.Wait(); //When I debug, it is getting lost on this line. I don't get any error message!
When I remove Redis registration marked above in Global.asax (that is default option MemoryServerEvents) works well. Any suggestion, ideas would be very helpful. Thank you
oh God.. Finally I found what the issue is.. It had nothing to do with server events code or its confguration!!! But I have the following line used in my application for a different purpose and which had impact on server events!
// Set the default reuse scope for the container to per request
container.DefaultReuse = ReuseScope.Request;
I guess onConnect is first request and OnJoin or other events are separate requests. As reuse scope was set in my app, it couldn't proceed?! Please share your thoughts if am wrong. Thank you

HttpClient fails to authenticate via NTLM on the second request when using the Sharepoint REST API on Windows Phone 8.1

Sorry for the long title, but it seems to be the best summary based on what I know so far.
We’re currently working on a Universal App that needs to access some documents on a Sharepoint server via the REST API using NTLM Authentication, which proves to be more difficult than it should be. While we were able to find workarounds for all problems (see below), I don’t really understand what is happening and why they are even necessary.
Somehow the HttpClient class seems to behave differently on the phone and on the PC. Here’s what I figured out so far.
I started with this code:
var credentials = new NetworkCredential(userName, password);
var handler = new HttpClientHandler()
{
Credentials = credentials
};
var client = new HttpClient(handler);
var response = await client.GetAsync(url);
This works fine in the Windows app, but it fails in the Windows Phone app. The server just returns a 401 Unauthorized status code.
Some research revealed that you need to provide a domain to the NetworkCredential class.
var credentials = new NetworkCredential(userName, password, domain);
This works on both platforms. But why is the domain not required on Windows?
The next problem appears when you try to do multiple requests:
var response1 = await client.GetAsync(url);
var response2 = await client.GetAsync(url);
Again, this works just fine in the Windows app. Both requests return successfully:
And again, it fails on the phone. The first request returns without problems:
Strangely any consecutive requests to the same resource fail, again with status code 401.
This problem has been encountered before, but there doesn’t seem to be a solution yet.
An answer in the second thread suggests that there’s something wrong with the NTLM handshake. But why only the second time?
Also, it seems to be a problem of the HttpClient class, because the following code works without problems on both platforms:
var request3 = WebRequest.CreateHttp(url);
request3.Credentials = credentials;
var response3 = await request3.GetResponseAsync();
var request4 = WebRequest.CreateHttp(url);
request4.Credentials = credentials;
var response4 = await request4.GetResponseAsync();
So the problem only appears:
on Windows Phone. The same code in a Windows App works.
when connecting to Sharepoint. Accessing another site with NTLM authentication works on both platforms.
when using HttpClient. Using WebRequest, it works.
So while I'm glad that I at least found some way to make it work, I’d really like to know what’s so special about this combination and what could be done to make it work?
Hi Daniel at the same problem when I do my sync, because windows phone had a lot of problems with cache, finallt I could solve with add headers.
Also I think so it's good idea that you use the timeout because it's a loooong response you can wait a lot of time... And the other good way to work it's use "using", it's similar that use ".Dispose()". Now I show you the code:
var request3 = WebRequest.CreateHttp(url);
request3.Credentials = credentials;
request.ContinueTimeout = 4000; //4 seconds
//For solve cache problems
request.Headers["Cache-Control"] = "no-cache";
request.Headers["Pragma"] = "no-cache";
using(httpWebResponse response3 = (httpWebResponse) await request3.GetResponseAsync()){
if (response3.StatusCode == HttpStatusCode.OK)
{
//Your code...
}
}
var request4 = WebRequest.CreateHttp(url);
request4.Credentials = credentials;
request.ContinueTimeout = 4000; //4 seconds
//For solve cache problems
request.Headers["Cache-Control"] = "no-cache";
request.Headers["Pragma"] = "no-cache";
using(httpWebResponse response4 = (httpWebResponse) await request4.GetResponseAsync()){
if (response4.StatusCode == HttpStatusCode.OK)
{
//Your code...
}
}
I wait that my code can help you. Thanks and good luck!

Set Cookie for UIWebView requests

I want to embed an UIWebView into my MonoTouch application for an area that is not yet implemented natively.
In order to authenticate with the website I want to set a cookie containing a key for the current session.
I tried creating a NSDictionary with the properties for the Cookie and then create a new NSHttpCookie and add it to the NSHttpCookieStorage.SharedStorage.
Sadly the cookie seems to be empty and not used for the request.
An example of how to build be cookie with properties and a comment on whether or not this is the simplest way to do this would be greatly appreciated.
Following Anuj's bug report I felt bad about how many lines of code were required to create the cookies. So the next MonoTouch versions will have new constructors for NSHttpCookie, similar to System.Net.Cookie that will allow you do to something like:
// this ctor requires all mandatory parameters
// so you don't have to guess them while coding
var cookie = new NSHttpCookie ("iherd", "ulikecookies", "/", "yodawg.com");
You'll even be able to create a NSHttpCookie from a .NET System.Net.Cookie.
Note: Never hesitate to fill a bug report when an API proves to be way more complicated than it should be :-)
Whenever I need to send cookies and params up to the server I use something like RestSharp or Hammock and then pass the response.Content value into UIWebView's loadHtmlString method:
//setup cookies and params here
var response = client.Execute(req);
_webView = new UIWebView();
_webView.LoadHtmlString(response.Content, new NSUrl(baseUrl));
The NSDictionary API is fairly trivial too:
var props = new NSMutableDictionary ();
props.Add (NSHttpCookie.KeyOriginURL, new
NSString("http://yodawg.com"));
props.Add (NSHttpCookie.KeyName, new NSString("iherd"));
props.Add (NSHttpCookie.KeyValue, new NSString("ulikecookies"));
props.Add (NSHttpCookie.KeyPath, new NSString("/"));
AFAIK every application has its own cookie storage so try to use this code before rendering the page in the UIWebView
NSHttpCookie cookie = new NSHttpCookie()
{
Domain = "yourdomain.com",
Name = "YourName",
Value = "YourValue" //and any other info you need to set
};
NSHttpCookieStorage cookiejar = NSHttpCookieStorage.SharedStorage;
cookiejar.SetCookie(cookie);
I'm not in a MAC right now so im not able to test it hope this helps
okay sorry, i wasn't able to test it before posting, anyways I won't get home until tonight so give this a spin
var objects = new object[] { "http://yoururl.com", "CookieName", "CookieValue", "/" };
var keys = new object[] { "NSHTTPCookieOriginURL", "NSHTTPCookieName", "NSHTTPCookieValue", "NSHTTPCookiePath" };
NSDictionary properties = (NSDictionary) NSDictionary.FromObjectsAndKeys(objects, keys);
NSHttpCookie cookie = NSHttpCookie.CookieFromProperties(properties);
NSHttpCookieStorage.SharedStorage.SetCookie(cookie);
As you stated above, in the case that doesn't work might be a bug on monotouch binding so you can bind it manually by doing this
var objects = new object[] { "http://yoururl.com", "CookieName", "CookieValue", "/" };
var keys = new object[] { "NSHTTPCookieOriginURL", "NSHTTPCookieName", "NSHTTPCookieValue", "NSHTTPCookiePath" };
NSDictionary properties = (NSDictionary) NSDictionary.FromObjectsAndKeys(objects, keys);
NSHttpCookie cookie = (NSHttpCookie) Runtime.GetNSObject(Messaging.IntPtr_objc_msgSend_IntPtr(new Class("NSHTTPCookie").Handle, new Selector("cookieWithProperties:").Handle, properties.Handle))
NSHttpCookieStorage.SharedStorage.SetCookie(cookie);
also don't forget to include using MonoTouch.ObjCRuntime; if manually binding it
if manually binding works please don't forget to post a bug report on https://bugzilla.xamarin.com/
Alex
I’ve wrote the NSMutableURLRequest+XSURLRequest catagory and XSCookie class to do this;-) http://blog.peakji.com/cocoansurlrequest-with-cookies/
This might give you a lead. Previously I used a similar strategy to make a
WebRequest to a site and collect cookies which were stored in the .Net/Mono CookieStore. Then when loading a url in the UIWebView I copied those cookies over to the NSHttpCookieStorage.
public NSHttpCookieStorage _cookieStorage;
/// <summary>
/// Convert the .NET cookie storage to the iOS NSHttpCookieStorage with Login Cookies
/// </summary>
void DotNetCookieStoreToNSHttpCookieStore()
{
foreach (Cookie c in _cookies.GetCookies(new Uri(UrlCollection["Login"], UriKind.Absolute))) {
Console.WriteLine (c);
_cookieStorage.SetCookie(new NSHttpCookie(c));
}
}

Resources