D365 FO connection from windows forms C# application - dynamics-365

I need to make a connection from C# Windows Forms application to an on premise D365 FO.
So far, I created an Azure account and registered an application, so by now I have "Application (client) ID", "Directory (tenant) ID" and created a Client Secret.
What do I need to do to connect to D365 FO by using Data management package REST API?

Take a look at the Authorization Helper, which is part of the sample console app provided by Microsoft for the data management api (see the last sentence in https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/data-entities/data-management-api). The Program.cs of the app shows how the Authentication Helper is used.
AuthorizationHelper.cs
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AuthorizationHelper
{
public class AuthorizationHelper
{
const string aadTenant = "https://login.windows.net/<your-tenant>";
public const string aadResource = "https://<yourAOS>.cloudax.dynamics.com";
const string aadClientAppId = "<client id>";
const string aadClientAppSecret = "<client secret>";
/// <summary>
/// Retrieves an authentication header from the service.
/// </summary>
/// <returns>The authentication header for the Web API call.</returns>
public static string GetAuthenticationHeader()
{
AuthenticationContext authenticationContext = new AuthenticationContext(aadTenant);
AuthenticationResult authenticationResult;
var creadential = new ClientCredential(aadClientAppId, aadClientAppSecret);
authenticationResult = authenticationContext.AcquireTokenAsync(aadResource, creadential).Result;
// Create and get JWT token
return authenticationResult.CreateAuthorizationHeader();
}
}
}
Program.cs
using ODataClient.Microsoft.Dynamics.DataEntities;
using System;
namespace DataPackageHandler
{
using AuthorizationHelper;
using Microsoft.OData.Client;
class Program
{
static void Main(string[] args)
{
string ODataEntityPath = AuthorizationHelper.aadResource + "/data";
Uri oDataUri = new Uri(ODataEntityPath, UriKind.Absolute);
var d365Client = new Resources(oDataUri);
d365Client.SendingRequest2 += new EventHandler<SendingRequest2EventArgs>(delegate (object sender, SendingRequest2EventArgs e)
{
var authenticationHeader = AuthorizationHelper.GetAuthenticationHeader();
e.RequestMessage.SetHeader("Authorization", authenticationHeader);
});
PackageImporter.ImportPackage(d365Client, #"..\debug\SampleData\usmf_asset-major-types-01.zip");
PackageExporter.ExportPackage(d365Client, #"..\debug\SampleData\");
Console.WriteLine("Press enter to exit...");
Console.ReadLine();
}
}
}

Related

AspNetBoilerplate - HttpContext is null in custom TenantResolveContributor

Documentation
I've read the documentation at https://aspnetboilerplate.com/Pages/Documents/Multi-Tenancy
I added a custom TenantResolveContributor to the application project and added it to the collection in WebModule.PreInitialize.
Configuration.MultiTenancy.Resolvers.Add<NameTenantResolveContributor>();
In WebModule.Initialize, I resolve my TenantAppService so it can be injected into RouteConfig.
TenantAppService tenantAppService = IocManager.Resolve<TenantAppService>();
RouteConfig.RegisterRoutes(RouteTable.Routes, tenantAppService);
When execution reaches RouteConfig, it correctly goes into NameTenantResolveContributor, but the HttpContext is null.
I'm not sure what I need to do to fix this.
Abp package version : 6.0.0
Base framework: .Net
Request is not available in this context
at System.Web.HttpContext.get_Request()
at DemoApp.MultiTenancy.NameTenantResolveContributor.ResolveTenantId() in >D:\src\ABP\DemoProject\6.0.0\src\DemoProject.Application\MultiTenancy\NameTenantResolveContributor.cs:line 40
at Abp.MultiTenancy.TenantResolver.GetTenantIdFromContributors()
The stack trace is oddly brief and undescriptive. When I copy the details, the message is
System.Web.HttpException
HResult=0x80004005
Message=Request is not available in this context
Source=<Cannot evaluate the exception source>
StackTrace:
<Cannot evaluate the exception stack trace>
using Abp.Configuration.Startup;
using Abp.Dependency;
using Abp.Domain.Repositories;
using Abp.Extensions;
using Abp.MultiTenancy;
using Abp.Text;
using Abp.Web.MultiTenancy;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
namespace DemoApp.MultiTenancy
{
public class NameTenantResolveContributor : ITenantResolveContributor, ITransientDependency
{
private readonly IMultiTenancyConfig _multiTenancyConfig;
private readonly ITenantStore _tenantStore;
public NameTenantResolveContributor(IMultiTenancyConfig multiTenancyConfig, ITenantStore tenantStore)
{
_multiTenancyConfig = multiTenancyConfig;
_tenantStore = tenantStore;
}
public int? ResolveTenantId()
{
string tenancyName = "";
string[] urlParts = null;
var httpContext = HttpContext.Current;
if (httpContext == null)
{
return null;
}
//This is where the error occurs
urlParts = httpContext.Request.Url.Segments.Select(x => x.TrimEnd('/')).Skip(1).ToArray();
tenancyName = urlParts[0].ToLower();
var tenantInfo = _tenantStore.Find(tenancyName);
if (tenantInfo == null)
{
return null;
}
return tenantInfo.Id;
}
}
}

Login via Xamarin Forms Application

we have an mvc 5 application with individual user authentication we also have a xamarin forms application. we need to be able to use the same login details thats created on the web application when we log in via the xamarin application we are creating. we have successfully been able to create web api controllers using existing models in the web application and read/write the data in the xamarin application. but the problem is that we are not able to provide the same authentication we have(username and password with role assigned to the user) to the xamarin application. how can we make an api controller that reads from our existing database..please note our application is hosted on azure with a sql database.
basically we want to provide a login to our web application via the mobile app.
You need to take a look at Adrian Halls book - chapter 2 covers custom authentication which is what you need.
https://adrianhall.github.io/develop-mobile-apps-with-csharp-and-azure/chapter2/custom/
The key points are setting the mobile app to use authentication in the Azure portal but don't set any of the authentication providers (this makes it custom)
You then need to implement your own custom authentication controller to handle the authentication call back like this example taken from Adrian's book;
using System;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Security.Claims;
using System.Web.Http;
using Microsoft.Azure.Mobile.Server.Login;
using Newtonsoft.Json;
namespace AWPBackend.Controllers
{
[Route(".auth/login/custom")]
public class CustomAuthController : ApiController
{
private MobileServiceContext db;
private string signingKey, audience, issuer;
public CustomAuthController()
{
db = new MobileServiceContext();
signingKey = Environment.GetEnvironmentVariable("WEBSITE_AUTH_SIGNING_KEY");
var website = Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME");
audience = $"https://{website}/";
issuer = $"https://{website}/";
}
[HttpPost]
public IHttpActionResult Post([FromBody] User body)
{
if (body == null || body.Username == null || body.Password == null ||
body.Username.Length == 0 || body.Password.Length == 0)
{
return BadRequest(); ;
}
if (!IsValidUser(body))
{
return Unauthorized();
}
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, body.Username)
};
JwtSecurityToken token = AppServiceLoginHandler.CreateToken(
claims, signingKey, audience, issuer, TimeSpan.FromDays(30));
return Ok(new LoginResult()
{
AuthenticationToken = token.RawData,
User = new LoginResultUser { UserId = body.Username }
});
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool IsValidUser(User user)
{
return db.Users.Count(u => u.Username.Equals(user.Username) && u.Password.Equals(user.Password)) > 0;
}
}
public class LoginResult
{
[JsonProperty(PropertyName = "authenticationToken")]
public string AuthenticationToken { get; set; }
[JsonProperty(PropertyName = "user")]
public LoginResultUser User { get; set; }
}
public class LoginResultUser
{
[JsonProperty(PropertyName = "userId")]
public string UserId { get; set; }
}
The actual custom authentication takes place in the IsValidUser function and should link to your existing internal authentication method (do not use the example here, this is for demonstration only)
Custom authentication has to use a client side flow which also meets your requirements.

Has anyone successfully implemented Azure Active Directory B2C for auth using Microsoft.Identity.Client 1.1.0-preview?

I have been struggling with this for several days (three actually). I have AAD B2C working on a web app and an api. I cannot get it running on my Xamarin mobile project. I am using the UWP project to test my configuration since it has the easiest app to troubleshoot on a Windows 10 machine. I am using Visual Studio 2015 Pro.
I am using the Microsoft.Identity.Client 1.1.0-preview.
I used this as my starting point for my attempt to implement.
https://github.com/Azure-Samples/active-directory-b2c-xamarin-native
Right now the project will compile and launch. When I click on Sign in, I get a WebView, but it doesn't look exactly right....
[First Image in Screenshots]
Here are my variables...
public class Constants
{
public static string ApplicationID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
public static string[] Scopes = {""};
public static string SignUpSignInPolicy = "B2C_1_Standard_SignUpSignIn";
public static string ResetPasswordPolicy = "B2C_1_Standard_PasswordReset";
public static string EditProfilePolicy = "B2C_1_Standard_EditProfile";
public static string Authority = "https://login.microsoftonline.com/[MyTennantName].onmicrosoft.com/B2C_1_Standard_SignUpSignIn";
public static string AuthorityEditProfile = "https://login.microsoftonline.com/[MyTennantName].onmicrosoft.com/B2C_1_Standard_EditProfile";
public static string ApiEndpoint = "https://[MyTennantName].onmicrosoft.com/apiservices";
public static UIParent UiParent = null;
}
My Login method is....
async void OnSignInSignOut(object sender, EventArgs e)
{
try
{
if (btnSignInSignOut.Text == "Sign in")
{
AuthenticationResult ar = await App.PCA.AcquireTokenAsync(Constants.Scopes, GetUserByPolicy(App.PCA.Users, Constants.SignUpSignInPolicy), Constants.UiParent);
UpdateUserInfo(ar);
UpdateSignInState(true);
}
else
{
foreach (var user in App.PCA.Users)
{
App.PCA.Remove(user);
}
UpdateSignInState(false);
}
}
catch (Exception ex)
{
// Checking the exception message
// should ONLY be done for B2C
// reset and not any other error.
if (ex.Message.Contains("AADB2C90118"))
OnPasswordReset();
// Alert if any exception excludig user cancelling sign-in dialog
else if (((ex as MsalException)?.ErrorCode != "authentication_canceled"))
await DisplayAlert($"Exception:", ex.ToString(), "Dismiss");
}
}
However before I can even enter my password I get the following....
[Second image in Screenshots]
My application definition looks like this...[Third image in screenshots]
I don't think it is recognizing my tenant and trying to log me in with a Microsoft account. I have double checked my Tenant name and Application ID.
Screenshots
I don't have enough reputation to post more than one link and one picture.
Also, the Azure AD B2C api application works for a web app. I have created a web app that can authenticate and works with the API.
It looks like while modifying the authorization value in the Sample you removed the /tfp/ part.
You should update your values as follows:
public static string Authority = "https://login.microsoftonline.com/tfp/[MyTennantName].onmicrosoft.com/B2C_1_Standard_SignUpSignIn";
public static string AuthorityEditProfile = "https://login.microsoftonline.com/tfp/[MyTennantName].onmicrosoft.com/B2C_1_Standard_EditProfile";

SignalR in SharePoint

My project has a need for realtime user interaction and I think SignalR will solve my need. I'm technically on a SharePoint 2007 project, although I'm exclusively in application pages and thus barely use SharePoint at all. Regardless, I'm stuck in a 2.0 framework app pool in IIS.
My first approach was to try to create a 4.0 application as a sub-site. Unfortunately, that failed miserably. That approach works in a non-SharePoint world, but it appears that SharePoint has hijacked too much of the request pipeline for this approach to work for me.
So now I'm going down the path of creating a separate IIS Site that's 4.0 and using IIS rewrite rules to fake my app into thinking a particular subdirectory (/realtime/) is local and not a separate site so that I don't have to deal with cross domain request issues. The problem is I can't get IIS rewrite rules to rewrite to another http host (e.g. http://www.mySharepoint.com/_layouts/MySite/realtime/Hello.aspx to http://realtime.mySharePoint.com/Hello.aspx).
Any help with approach #1 or approach #2 or any alternative ideas would be greatly appreciated.
Here is what I did... Web App with signalR .net4.0, then your SharePoint Web App .net 2.
Add this to the global.asax in your Signalr project
RouteTable.Routes.MapHttpHandlerRoute("spproxy","spproxy/{*operation}", new SharePointRProxyHandler());
If you want to raise an event from SharePoint you can do a http POST to this new route URL for example
http://localhost:38262/spproxy
It will pass any posted data onto the httphandler below, that will then broadcast it to your clients.
Here is the code for MapHttpHandlerRoute
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace System.Web.Routing
{
public class HttpHandlerRoute : IRouteHandler
{
private String _virtualPath = null;
private IHttpHandler _handler = null;
public HttpHandlerRoute(String virtualPath)
{
_virtualPath = virtualPath;
}
public HttpHandlerRoute(IHttpHandler handler)
{
_handler = handler;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
IHttpHandler result;
if (_handler == null)
{
result = (IHttpHandler)System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath(_virtualPath, typeof(IHttpHandler));
}
else
{
result = _handler;
}
return result;
}
}
public static class RoutingExtensions
{
public static void MapHttpHandlerRoute(this RouteCollection routes, string routeName, string routeUrl, string physicalFile, RouteValueDictionary defaults = null, RouteValueDictionary constraints = null)
{
var route = new Route(routeUrl, defaults, constraints, new HttpHandlerRoute(physicalFile));
RouteTable.Routes.Add(routeName, route);
}
public static void MapHttpHandlerRoute(this RouteCollection routes, string routeName, string routeUrl, IHttpHandler handler, RouteValueDictionary defaults = null, RouteValueDictionary constraints = null)
{
var route = new Route(routeUrl, defaults, constraints, new HttpHandlerRoute(handler));
RouteTable.Routes.Add(routeName, route);
}
}
}
Or you could just post directly to a httphandler and get the handler to do a connection.Broadcast
namespace SharePointRProxy
{
/// <summary>
/// Summary description for SharePointRProxyHandler
/// </summary>
public class SharePointRProxyHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
IConnectionManager connectonManager = AspNetHost.DependencyResolver.Resolve<IConnectionManager>();
IConnection connection = connectonManager.GetConnection<MyConnection>();
object payload = null; //Add payload here 'context.Request.Params["data"] ?'
JavaScriptSerializer jss = new JavaScriptSerializer();
var payloadJSON = jss.Serialize(payload);
connection.Broadcast(payloadJSON);
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
You could also use either an event handler calling a .net 4.0 web service or an http handler to grab requests from SharePoint and pass them over to a .net 4.0 application running your signalr code.
You can see an example of using an http handler here: http://spmatt.wordpress.com/2012/04/12/harnessing-signalr-in-sharepoint/

How do I create an COM visible class in C#?

I using Visual Studio 2010 (.NET 4). I need to create a COM object (in C#) and have no idea how to get started (what type of project to use,etc.)
OK I found the solution and I'll write it here for the common good.
Start VS2010 as administrator.
Open a class library project (exmaple - MyProject).
Add a new interface to the project (see example below).
Add a using System.Runtime.InteropServices; to the file
Add the attributes InterfaceType, Guid to the interface.
You can generate a Guid using Tools->Generate GUID (option 4).
Add a class that implement the interface.
Add the attributes ClassInterface, Guid, ProgId to the interface.
ProgId convention is {namespace}.{class}
Under the Properties folder in the project in the AssemblyInfo file set ComVisible to true.
In the project properties menu, in the build tab mark "Register for COM interop"
Build the project
now you can use your COM object by using it's ProgID.
example:
the C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace Launcher
{
[InterfaceType(ComInterfaceType.InterfaceIsDual), Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")]
public interface ILauncher
{
void launch();
}
[ClassInterface(ClassInterfaceType.None), Guid("YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYY"), ProgId("Launcher.Launcher")]
public class Launcher : ILauncher
{
private string path = null;
public void launch()
{
Console.WriteLine("I launch scripts for a living.");
}
}
}
and VBScript using the COM:
set obj = createObject("PSLauncher.PSLauncher")
obj.launch()
and the output will be:
I launch scripts for a living
Creation Steps
Start Visual Studio 2013 as administrator
Install Visual Studio extension Microsoft Visual Studio Installer Projects
Create a class library project (WinFormActivex)
Create your example window form (MainWindow)
Create a new component interface(ILauncher)
Create a new security interface (IObjectSafety)
Create the component control (Launcher) that implement interfaces and launch the window.
Check that all GUIDs are generated by you
Check that the project is marked for COM
Create the setup project (LauncherInstaller) with the primary output of WinFormActivex with the property Register = vsdrpCOM
Install LauncherInstaller
Run your test page in explorer (test.html)
MainWindow
You can create a normal Form, here is pre-generated.
public partial class MainWindow : Form
{
public MainWindow()
{
InitializeComponent();
}
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.textBox2 = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(42, 23);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(100, 20);
this.textBox1.TabIndex = 0;
//
// textBox2
//
this.textBox2.Location = new System.Drawing.Point(42, 65);
this.textBox2.Name = "textBox2";
this.textBox2.Size = new System.Drawing.Size(100, 20);
this.textBox2.TabIndex = 0;
//
// MainWindow
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 261);
this.Controls.Add(this.textBox2);
this.Controls.Add(this.textBox1);
this.Name = "MainWindow";
this.Text = "MainWindow";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.TextBox textBox2;
}
ILauncher
using System.Runtime.InteropServices;
namespace WinFormActivex
{
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("94D26775-05E0-4B9C-BC73-C06FE915CF89")]
public interface ILauncher
{
void ShowWindow();
}
}
IObjectSafety
[ComImport()]
[Guid("51105418-2E5C-4667-BFD6-50C71C5FD15C")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IObjectSafety
{
[PreserveSig()]
int GetInterfaceSafetyOptions(ref Guid riid, out int pdwSupportedOptions, out int pdwEnabledOptions);
[PreserveSig()]
int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions);
}
Launcher
Please generate your GUID here.
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("D100C392-030A-411C-92B6-4DBE9AC7AA5A")]
[ProgId("WinFormActivex.Launcher")]
[ComDefaultInterface(typeof(ILauncher))]
public class Launcher : UserControl, ILauncher, IObjectSafety
{
#region [ ILauncher ]
public void ShowWindow()
{
var f = new MainWindow();
f.StartPosition = FormStartPosition.Manual;
f.Location = Screen.AllScreens[0].Bounds.Location;
f.WindowState = FormWindowState.Normal;
f.WindowState = FormWindowState.Maximized;
f.ShowInTaskbar = false;
f.Show();
}
#endregion
#region [ IObjectSafety ]
public enum ObjectSafetyOptions
{
INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001,
INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002,
INTERFACE_USES_DISPEX = 0x00000004,
INTERFACE_USES_SECURITY_MANAGER = 0x00000008
};
public int GetInterfaceSafetyOptions(ref Guid riid, out int pdwSupportedOptions, out int pdwEnabledOptions)
{
ObjectSafetyOptions m_options = ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_CALLER | ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_DATA;
pdwSupportedOptions = (int)m_options;
pdwEnabledOptions = (int)m_options;
return 0;
}
public int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions)
{
return 0;
}
#endregion
}
test.html
Please check that your CLSID match (Launcher) GUID.
<html>
<head>
<objectname="activexLauncher" style='display:none' id='activexLauncher' classid='CLSID:D100C392-030A-411C-92B6-4DBE9AC7AA5A' codebase='WinFormActivex'></object>
<script language="javascript">
<!-- Load the ActiveX object -->
var x = new ActiveXObject("WinFormActivex.Launcher");
alert(x.GetText());
</script>
</head>
<body>
</body>
</html>
References
Stack Overflow question I always use as reference
Activex tag you should read
Old Microsoft guide
Article on creating the acrivex control with security options
Article about creating the window
You could use a class library project. Declare a type with methods that will be exposed as a COM object.
Make sure that the assembly has been made COM-visible:
And finally register it using regasm.exe:
regasm.exe /codebase mylib.dll
Now the assembly is exposed as a COM object and the type you declared can be consumed by any client that supports COM.

Resources