Access ACS Azure from Winforms - winforms

I have an Azure webrole that requires ACSv2 authentication that I want to access from a winforms application. Many of my clients use Windows XP so I can not use the WIF (which is not available on Windows XP). What is the best way to get an authentication token for my web request in this case?

For desktop applications you can do the following:
Get the list of identity providers from your ACS namespace
Display these in a WebBrowser control
After the user logs in, get the token from the WebBrowser control and parse it.
This is similar to when you want to use ACS in a Windows Phone application. I suggest you take a look at this blog post: Azure ACS on Windows Phone 7. And here is the sample code which parses the token after the user logs in through the WebBrowser control (on WP7):
private void SignInWebBrowserControl_ScriptNotify(object sender, NotifyEventArgs e)
{
var acsResponse = ACSResponse.FromJSON(e.Value);
RequestSecurityTokenResponse rstr = null;
Exception exception = null;
try
{
string binaryToken = HttpUtility.HtmlDecode(acsResponse.securityToken);
string tokenText = RequestSecurityTokenResponseDeserializer.ProcessBinaryToken(binaryToken);
DateTime expiration = DateTime.Now + TimeSpan.FromSeconds(acsResponse.expires – acsResponse.created);
rstr = new RequestSecurityTokenResponse
{
Expiration = expiration,
TokenString = tokenText,
TokenType = acsResponse.tokenType
};

Related

How to list subscriptions with Microsoft.Azure.ResourceManager?

Context
My core goal is to write an Azure WebApps deployment tool in C#. The process will be roughly
User logs in
User selects subscription
User selects or creates resource group
User selects or creates storage for the web app
User selects or creates web service plan
User selects or creates web app
Tool uploads the web app using Kudu to POST a zip
Since the last step can't be done in the portal, my idea was to do everything in a GUI tool.
I started out using Kudu's ARMClient.Authentication and Microsoft.Azure.ResourceManager 1.0.0-preview. However, when it comes to creating a storage account I get a permissions error (The subscription is not registered to use namespace Microsoft.Storage), so my plan B was to do the authentication myself following Brady Gaster's blog post.
The problem
I've set up an application as documented, and using its clientId and tenantId I'm able to log in and list tenants. But I can't list any subscriptions. (NB I've partly elided the clientId and tenantId in case there are security risks with giving them in full).
string clientId = "f62903b9-ELIDED";
string tenantId = "47b6e6c3-ELIDED";
const string redirectUri = "urn:ietf:wg:oauth:2.0:oob";
const string baseAuthUri = "https://login.microsoftonline.com/";
const string resource = "https://management.core.windows.net/";
var ctx = new AuthenticationContext(baseAuthUri + tenantId);
var authResult = ctx.AcquireToken(resource, clientId, new Uri(redirectUri), PromptBehavior.Auto);
var token = new TokenCredentials(authResult.AccessToken);
var subClient = new SubscriptionClient(token);
var tenants = await subClient.Tenants.ListAsync();
foreach (var tenant in tenants) Console.WriteLine(tenant.TenantId);
var subs = await subClient.Subscriptions.ListAsync();
foreach (var sub in subs) Console.WriteLine(sub.DisplayName);
When I run this it prompts me to login, and lists the tenants corresponding to the subscriptions I own or co-administer. But it doesn't list a single subscription. If I change the IDs to the commonly used (I think officially for Powershell) values
clientId = "1950a258-227b-4e31-a9cf-717495945fc2";
tenantId = "common";
then it's the same.
What is the step I've missed in order to get a list of my subscriptions?
You need to iterate through the tenants, authenticate in tenant and get a subscription list for every tenant.
The following code will output the Subscriptions like Get-AzureRmSubscription powershell cmdlet does.
class Program
{
private static string m_resource = "https://management.core.windows.net/";
private static string m_clientId = "1950a258-227b-4e31-a9cf-717495945fc2"; // well-known client ID for Azure PowerShell
private static string m_redirectURI = "urn:ietf:wg:oauth:2.0:oob"; // redirect URI for Azure PowerShell
static void Main(string[] args)
{
try
{
var ctx = new AuthenticationContext("https://login.microsoftonline.com/common");
// This will show the login window
var mainAuthRes = ctx.AcquireToken(m_resource, m_clientId, new Uri(m_redirectURI), PromptBehavior.Always);
var subscriptionCredentials = new TokenCloudCredentials(mainAuthRes.AccessToken);
var cancelToken = new CancellationToken();
using (var subscriptionClient = new SubscriptionClient(subscriptionCredentials))
{
var tenants = subscriptionClient.Tenants.ListAsync(cancelToken).Result;
foreach (var tenantDescription in tenants.TenantIds)
{
var tenantCtx = new AuthenticationContext("https://login.microsoftonline.com/" + tenantDescription.TenantId);
// This will NOT show the login window
var tenantAuthRes = tenantCtx.AcquireToken(
m_resource,
m_clientId,
new Uri(m_redirectURI),
PromptBehavior.Never,
new UserIdentifier(mainAuthRes.UserInfo.DisplayableId, UserIdentifierType.RequiredDisplayableId));
var tenantTokenCreds = new TokenCloudCredentials(tenantAuthRes.AccessToken);
using (var tenantSubscriptionClient = new SubscriptionClient(tenantTokenCreds))
{
var tenantSubscriptioins = tenantSubscriptionClient.Subscriptions.ListAsync(cancelToken).Result;
foreach (var sub in tenantSubscriptioins.Subscriptions)
{
Console.WriteLine($"SubscriptionName : {sub.DisplayName}");
Console.WriteLine($"SubscriptionId : {sub.SubscriptionId}");
Console.WriteLine($"TenantId : {tenantDescription.TenantId}");
Console.WriteLine($"State : {sub.State}");
Console.WriteLine();
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
Console.WriteLine("press something");
Console.ReadLine();
}
}
}
A couple things you can look into...
1) the error you saw during creating of the storage account is likely due to the Resource Provider not being registered for use with the subscription. Any RP needs to be registered before use, some clients (Portal, PowerShell) will register the RP for you so you never notice it. See: https://msdn.microsoft.com/en-us/library/azure/dn790548.aspx - you should be able to do that from your code if the user has sufficient perms.
2) You may not be getting any subscriptions back because your endpoint (management.core.windows.net) is the endpoint for Azure Service Management not Azure Resource Manager (management.azure.com). If the subscription access is granted via AzureRM and RBAC, the old ASM apis will not see (i.e. have access to) those subscriptions.

Deleting cookies in WPF web browser control

I am developing a WPF application in which I am working with twitter API. To show twitter authentication page I am using WPF web-browser control. I am able to login and use twitter API successfully. My problem is that I need to clear web browser's cookies to implement logout functionality. Is there any way to clear session cookies in WPF web browser?
I ran into this issue yesterday and finally came up with a full solution today. The answer is mentioned here which is put into more detail here and here.
The primary issue here is that the WebBrowser (in WPF and WinForms) does not permit you to modify (delete) existing session cookies. These session cookies are what prevent a multi-user single device experience from being successful.
The StackOverflow response in the link above omits an important part, it requires the use of an unsafe code block, instead of using the Marshal service. Below is a full solution that can be placed into your project to suppress the session cookie persistence.
public static partial class NativeMethods
{
[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength);
private const int INTERNET_OPTION_SUPPRESS_BEHAVIOR = 81;
private const int INTERNET_SUPPRESS_COOKIE_PERSIST = 3;
public static void SuppressCookiePersistence()
{
var lpBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int)));
Marshal.StructureToPtr(INTERNET_SUPPRESS_COOKIE_PERSIST, lpBuffer, true);
InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SUPPRESS_BEHAVIOR, lpBuffer, sizeof(int));
Marshal.FreeCoTaskMem(lpBuffer);
}
}
Check the following,
http://social.msdn.microsoft.com/Forums/en/wpf/thread/860d1b66-23c2-4a64-875b-1cac869a5e5d
private static void _DeleteSingleCookie(string name, Uri url)
{
try
{
// Calculate "one day ago"
DateTime expiration = DateTime.UtcNow - TimeSpan.FromDays(1);
// Format the cookie as seen on FB.com. Path and domain name are important factors here.
string cookie = String.Format("{0}=; expires={1}; path=/; domain=.facebook.com", name, expiration.ToString("R"));
// Set a single value from this cookie (doesnt work if you try to do all at once, for some reason)
Application.SetCookie(url, cookie);
}
catch (Exception exc)
{
Assert.Fail(exc + " seen deleting a cookie. If this is reasonable, add it to the list.");
}
}
I have not tested this, but I think the best way would be to define a Javascript method on the page (if you're able to) that clears the cookie.
document.cookie='c_user=;expires=Thu, 01 Jan 1970 00:00:00 GMT;domain=.facebook.com';
(or whatever the cookie name is). Then you can use the InvokeScript method on the WebBrowser control.

Facebook UserName WPF

Its very simple question, I'm new to this and I can't understand the problem , I am trying to get the user name and display it in a label , when i try this code to get the access token it doesn't enter the condition :
it enters the event of (browser_navigated) but not the Conditions in , anyone knows whats the problem ?
private void webBrowser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
FacebookOAuthResult result;
if (FacebookOAuthResult.TryParse(e.Uri, out result))
{
if (result.IsSuccess)
{
var accesstoken = result.AccessToken;
}
else
{
var errorDescription = result.ErrorDescription;
var errorReason = result.ErrorReason;
}
}
There is an annoying bug in wpf browser control which ignores everything after #. (This is only for web browser controls based on wpf so it is present in silverlight and wp7 web browser controls as well.)
Facebook returns access token as part of url fragment.
https://url.com#access_token=....
Due to the bug, when you pass e.Uri as a parameter it doesn't part #access_token=.. thus fb c# sdk thinks it is not a valid oauth callback url and thus TryParse always returns false.
Solution:
Either use the winforms browser control for login or set response_type as code token and then exchange code for access token.

Using Windows Credentials to instantiate OrganizationServiceProxy in CRM 2011

Has anybody tried to use the Windows credentials to create an instance of OrganizationServiceProxy in CRM 2011 (On-Premise) ? I have a WCF service that uses
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows"/>
</security>
and I am able to confirm that the user is authenticated (OperationContext.Current.ServiceSecurityContext.WindowsIdentity.IsAuthenticated) but I don't know how to generate/pass the ClientCredentials to create an instance of the CRM service. This is called from a Silverlight application that does not live in an IFrame inside CRM.
Thanks.
What you need to use separate user account to log into the OrganizationServiceProxy.
You wont be able retrieve the windows credentials to pass to the proxy for authentication.
The user that you do use needs prvActOnBehalfOfAnotherUser privilege associated with it.
Once this is done and you can successfullly login and retrieve a valid OrganizationServiceProxy, what you need to do as a consumer of the service is specify the CallerId whenever you are calling operations on it. This token you should retrieve from the xrm model using Xrm.Page.context.getUserId. See. http://msdn.microsoft.com/en-us/library/gg334511.aspx.
Then from silverlight you would use the System.Windows.Browser.ScriptObject bridge to execute client side javascript to retrieve the userid of current user logged into crm.
Preferably do this at application bootstrap time and save the value into an applicationdata variable so so can access it globally from within your silverlight app.
Eg. of client side script.
function CrmContext() {
}
var context = null;
with (window.parent) {
context = Xrm.Page.context;}
CrmContext.prototype.ReadUserId = function () {
var userId = context.getUserId();
return userId;
}
Once you have the user token set the Proxy CallerId with this value
Eg.
private OrganizationServiceProxy Proxy { get; set; }
public Guid Create(CreateEntity request)
{
if (request == null || request.UserId == Guid.Empty || request.Entity == null)
{
throw new InvalidMessageException("Invalid reqest message. Please provide compulsory criteria");
}
var result = Guid.Empty;
try
{
if (Proxy != null)
{
Proxy.CallerId = request.UserId;
using (Proxy)
{
result = Proxy.Create(request.Entity);
}
}
}
catch (FaultException<OrganizationServiceFault> e)
{
Log.Error(e.Message);
throw new IntegrationException(e.Message);
}
return result;
}
The approach ive taken to solve this was to create a crm adapter encapsulating the crm proxy and sending request object to service interface that includes the user token.
public OrganizationServiceAdapter(ICrmConfigurationContext crmConfigurationConext)
{
try
{
Proxy = new OrganizationServiceProxy(
crmConfigurationConext.OrganizationServiceConfiguration,
crmConfigurationConext.Credentials);
}
catch (Exception e)
{
//// TODO: Add local proxy pattern implementation for failover
Proxy = null;
Log.Error(e.Message);
throw new IntegrationException(ExceptionMessages.CouldNotLoginToOrganizationService());
}
}

Managing cookies in a WPF WebBrowser control?

Is there a way to read/write the cookies that a WebBrowser control uses?
I am doing something like this...
string resultHtml;
HttpWebRequest request = CreateMyHttpWebRequest(); // fills http headers and stuff
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
resultHtml = sr.ReadToEnd();
}
WebBrowser browser = new WebBrowser();
browser.CookieContainer = request.CookieContainer; // i wish i could do this :(
browser.NavigateToString(resultHtml);
One of the potentially confusing things about the WebBrowser control and cookies is that at a first glance, it often looks like your app gets a separate cookie store. For example, if you log into a site that stores a persistent cookie to identify you, then whether you appear to be logged in for that site from inside an app hosting the control will be independent of whether you seem to be logged in via Internet Explorer.
In fact, you can even be logged in with different identities.
However, although it might be natural to draw the conclusion that each app hosting the WebBrowser therefore gets its own cookies, in fact that's not true. There are merely two sets of cookies: the ones used in 'low integrity' mode (which is what IE runs in by default), and the other set, which is what you'll get in a normal app that hosts the WebBrowser and also what you'll get if you run IE elevated.
the webbrowser control uses WinInet for networking, specifically use the InternetSetCookie(Ex) and InternetGetCookie(Ex) functions for Cookie management. There isn't a WinInet wrapper in .Net, but you can p-invoke.
Yes you are right, InternetGetCookieEx is the only way to retrieve HttpOnly cookies and it is the preferred way to grab cookie from WebBrowser control.
I posted a complete example here
You can use Application.GetCookie and Application.SetCookie methods.
Although Application is more or less related to WPF, you can use these methods in any desktop .NET code. In fact, they are wrappers on InternetGetCookieEx and InternetSetCookieEx Windows APIs.
I faced the same issue few days ago.
Besides the examples of the previous answers, here is a Win32 wrapper for the WebBrowser control. The advantage of this implementation is that it exposes more options that the default WebBrowser control.
Unfortunately if It's not WPF native, so you will have to create a wrapper if you're planning to use it in WPF.
http://code.google.com/p/csexwb2/
Here is sample from [link][1]
> public static class WinInetHelper
{
public static bool SupressCookiePersist()
{
// 3 = INTERNET_SUPPRESS_COOKIE_PERSIST
// 81 = INTERNET_OPTION_SUPPRESS_BEHAVIOR
return SetOption(81, 3);
}
public static bool EndBrowserSession()
{
// 42 = INTERNET_OPTION_END_BROWSER_SESSION
return SetOption(42, null);
}
static bool SetOption(int settingCode, int? option)
{
IntPtr optionPtr = IntPtr.Zero;
int size = 0;
if (option.HasValue)
{
size = sizeof(int);
optionPtr = Marshal.AllocCoTaskMem(size);
Marshal.WriteInt32(optionPtr, option.Value);
}
bool success = InternetSetOption(0, settingCode, optionPtr, size);
if (optionPtr != IntPtr.Zero) Marshal.Release(optionPtr);
return success;
}
[System.Runtime.InteropServices.DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool InternetSetOption(
int hInternet,
int dwOption,
IntPtr lpBuffer,
int dwBufferLength
);
}

Resources