Thursday, August 24, 2017

.NetCore 2.0, AzureAD and OpenIdConnect

.NetCore 2.0, AzureAD and OpenIdConnect

If you develop with .NetCore you probably already know that there are some significant changes from .NetCore 1.1 to .NetCore 2.0
  1. 2.0 — Most .Net Packages are now under a single Namespace — Microsoft.AspNetCore.All
  2. More significant changes are in how we setup our Startup.cs — Configure , ConfigureServices and TokenValidated. 
  3. More, I'm just getting started with 2.0
I had to dig into the Class libraries to figure out how to get this to work in 2.0, the Microsoft docs have not caught up with .NetCore 2.0 just yet, the docs on Authentication still reference 1.1 for now, or maybe I just didn’t look hard enough. 

I’m going to focus on using OpenIdConnect to connect to AzureAD for user authentication in this basic post.

Project setup: 
I put all my Azure connect settings into the appsettings.json file for this post, you may want to put your settings in some other place depending on your security requirements.

app settings.js

“AzureAd” : {
“ClientId”: “
Your ClientId”,
“Tennant”: “
Your Tennant”,
“TennantId “: “
Your TennantId”,
“AADInstance”: “
Your AADInstance”,
“PostLogoutRediredtUri”:”
Your PostLogoutRedirectUri
}, “More settings here, removed for brevity”

You can find these settings on your Azure Portal: 

Azure Active Directory -> App Registrations -> "Your App" -> Properties 

The Code!

.NetCore 1.1

In .NetCore 1.1, I setup the Configure, ConfigureServices, TokenValidated methods of my startup.cs file like this: 

ConfigureServices()

services.AddAuthentication( SharedOptions => SharedOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme);

Configure()

app.UseCookieAuthentication(new CookieAuthenticationOptions()); 

app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions {
ClientId = _config[“AzureAd:ClientId”],
Authority = String.Format(_config[“AzureAd:AADInstance”],
 _config[“AzureAd:Tenant”]),
ResponseType = OpenIdConnectResponseType.IdToken, 
PostLogoutRedirectUri = _config[“AzureAd:PostLogoutRedirectUri”],
Events = new OpenIdConnectEvents {
OnTokenValidated = TokenValidated,
OnRemoteFailure = OnAuthenticationFailed
 } });

TokenValidated()

private Task TokenValidated(TokenValidatedContext context)
{
string issuer = context.SecurityToken.Issuer;
string subject = context.SecurityToken.Subject;
DateTime validFrom = context.SecurityToken.ValidFrom;
DateTime validTo = context.SecurityToken.ValidTo;
string tenantID = context.Ticket.Principal.FindFirst(“http://schemas.microsoft.com/identity/claims/tenantid").Value;
IEnumerable<string> approvedTenantIds = new List<string>
{
_config[“AzureAd:TenantId”],
_config[“AzureAd:ClientId”]
};
if (!approvedTenantIds.Contains(tenantID))
throw new SecurityTokenValidationException();
return Task.FromResult(0);
}

.NetCore 2.0

Configure()
In 2.0, we no longer need to specify app.UseCookieAuthentication nor app.UseOpenIdConnectAuthentication,
instead all authentication is now available with

app.UseAuthentication(); 

ConfigureServices()

services.AddAuthentication(options=> {
 options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
 options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
 })
 .AddCookie()
 .AddOpenIdConnect(options => {
 options.Authority = String.Format(Configuration[“AzureAd:AADInstance”], Configuration[“AzureAd:Tenant”]);
 options.ClientId = Configuration[“AzureAd:ClientId”];
 options.ResponseType = OpenIdConnectResponseType.IdToken;

 options.Events = new OpenIdConnectEvents
 {
 OnTokenValidated = TokenValidated,
OnRemoteFailure = OnAuthenticationFailed
 };

TokenValidated()In .NetCore 1.1 TokenValidatedContext was located in the namespace 

Microsoft.AspNetCore.Authentication.OpenIdConnect

In .NetCore 2.0 TokenValidatedContext is now under

Microsoft.AspNetCore.Authentication.RemoteAuthenticationContext<OpenIdConnectOptions>

So we need to change how we find the tenantId, everything else is the same as above.

From: 
string tenantID = context.Ticket.Principal.FindFirst(“http://schemas.microsoft.com/identity/claims/tenantid").Value;

To:
string tenantID = context.Principal.FindFirst(“http://schemas.microsoft.com/identity/claims/tenantid").Value;

I did make an addition, I added this to the 2.0 version.
string audience = context.SecurityToken.Audiences.FirstOrDefault();
string upn = context.Principal.FindFirst(ClaimTypes.Upn)?.Value;

OnAuthenticationFailed is the same in both 1.1 and 2.0

 private Task OnAuthenticationFailed(AuthenticationFailedContext context)
 {
 context.HandleResponse();
 context.Response.Redirect(“/Home/Error/?message =” + context.Exception);
 return Task.FromResult(0);
 }

For more information on .NetCore 2.0 changes
https://docs.microsoft.com/en-us/aspnet/core/aspnetcore-2.0

Hope this helps.

Wednesday, January 4, 2017

Microsoft PowerApps + Flow - My opinion on "No code Enterprise App Development"

Microsoft designed PowerApps and Flow for PowerUsers who may or may not know how to write code.
In my opinion LOB ( Line of Business ) Developers such as myself can benefit from this new offering as well.

How?

Time to Market, Yes PowerApps is essentially a closed market,  there is no way to sell the apps you create in PowerApps, they are intended solely for your organization, so your market is your co-workers. 

UPDATE: As of May 2017, you can add your app to AppSource.com, more information is available here https://docs.microsoft.com/en-us/powerapps/dev-appsource-test-drive

Being the only developer for my department, all design, development, testing, and support falls on my shoulders, this is both good and bad. The average time it takes to gather requirements, to design, to develop, compile user feedback and make improvements depends on the application, for me this typically takes any where from 3 months to over a year before the app can be put into production.
All the while you have to keep up with your other duties, design in new requirements on existing apps where appropriate, take support calls etc... Typical right!

In PowerApps you connect to your data stores through connectors. There are many connectors already available in the PowerApps Portal.

*Not all connectors are shown here, there are many more connectors available and more being developed.


If one of the existing connectors does not meet your needs, then you can create a custom connector by following the guidelines here: https://powerapps.microsoft.com/en-us/tutorials/register-custom-api/

If your data source is an On Premise source you can create a Gateway that will reside on your OnPremise server, at the time of this post only SQL Server and SharePoint are supported.

PowerApps is a NoCode Solution, well sort of.
You can add functionality to your app through Excel style formulas, there are many out the box formulas available, please see: https://powerapps.microsoft.com/en-us/tutorials/formula-reference/ for available functions.

You can create really robust data centric applications fairly quickly, what would have took me at least 3 months in C#, took about 10 days in PowerApps,  I ported 4 apps at the same time in a single weekend, not bragging here, just pointing out the power of PowerApps.

Granted the apps I mentioned above are ports of two of my existing InfoPath forms and one managed code solution I developed with Visual Studio LightSwitch. For the LightSwitch port I connected to my Azure SQL DB instead of the existing OnPremise SQL Server DB, the new PowerApps app UI now interfaces a custom ASP.NETCore WebApi I developed and published to Azure.

The fourth app happened at the last minute, not a recommended design approach, but I wanted to play with the CDS(Common Data Service).

 I had already gathered the requirements on how the PowerApps should work, that's partly the reason why I was able to develop these PowerApps apps so quickly, but even still, I was really impressed at how much I was able to accomplish in a short amount of time.

Compliance
Depending on your company, you have to consider company policies, data management policies, Legal requirements due to Privacy Management Compliance Policies, does your app contain or surface personally identifiable information, some of my app designs have had to go through architecture review boards made up of various technology leaders from other departments including the IT and Legal departments.

Custom code is the number one support ticket according to IT, therefore if you can develop with an out of the box technology the more likely it is your app will be approved, you will not have to have your app go through the architecture review board process.

PowerApps is now Generally Available within the O365 environment. You will need to check with your IT department as to it's availability within your organization.

Support
As mentioned in the last paragraph Custom code generates the most support tickets according to my IT department. One issue is that if you are on vacation or if you leave the company, you have to make sure another developer is able to take over your source code, in reality this does not always happen, especially if you are a one man team or if custom development is farmed out.

No plans yet but if I were to leave the company, it would be relatively easy for someone to pick up where I left off with PowerApps.

Why not Out of The Box Ready Made Solutions
If this meets your needs, great!
We use out of the box applications all the time.
In my experience more often than not, custom development is necessary. Out the box solutions may be missing some critical functionality required by your business and extending these types of apps can take a really long time, you may not have access to the source code, you might have to involve third party developers, depending on your data you have make sure the dev(s) have network access, proper permissions, you may need to make sure NDA's are signed and properly filed, development can be prohibited by the licensing, prohibited by licensing fees, contracts, there are a number of other reasons as to why you cannot modify the Out the box solution whenever you want.

Maintenance
How complex does your app really need to be?  Not all apps need to use managed code solutions with months and months of design and development time behind them.

How do I get started with PowerApps? 

You have to have an O365 Account.

You can build your UI using PowerApps and connect to almost any data source, SharePoint, SQL Server in the cloud or on Premise through a gateway, you can surface data using the prebuilt connectors or through your own custom API, Social Networks like Facebook, Twitter, Yammer, Slack, use the Entities feature in your PowerApps Console etc...
I say almost anything because the list is still growing and it all depends on whether or not you have the proper permissions to the data source of course.

Also if you work with SharePoint, most of my development these days is done in SharePoint, Microsoft announced at Ignite that PowerApps will replace InfoPath and
Flow will replace SharePoint Designerhttps://channel9.msdn.com/Events/Ignite/2016/BRK2051?ocid=player, but PowerApps and Flow are not limited to just SharePoint as mentioned above.

Is PowerApps a replacement for Visual Studio LightSwitch?
In my opinion NO. PowerApps is significantly different from LightSwitch, it's more like InfoPath but without the Post-backs and PowerApps is not limited to surfacing data from just SharePoint.

Another thing is you have to use the Visual Studio IDE to build LightSwitch apps. Building PowerApp apps with Visual Studio is not available at this time, and I have no clue if ever will be.

PowerApps are developed in either the browser on the PowerApps portal, or through a desktop application available for download from the Microsoft store.

Another big difference I see is that your PowerApps production apps are only available from within your organization, there is no way to make your apps available in one of the online app stores for download by 3rd parties at the time of this writing.

There is a PowerApps management console for mobile you can install on either your iPad and/or iPhone, this is available from both the Apple App store or Google Play. The management app provides you access to all your organizations apps that are made available to you in one place, that way you don't have to install each individual application.

I say the apps your organization makes available to you because admins have the ability to setup compliance rules, limit the apps to specific groups and or personnel or enable the app(s) for the entire organization.

Some features are still a work in progress!
Support for SharePoint libraries is not available at this time and I've had some issues with surfacing Images from SharePoint, QR Codes etc... According to the user forums, support is coming, no ETA though.
More information about what works and what does not work can found in the Community forums and the PowerApps website.


References:

Microsoft PowerAppshttps://powerapps.microsoft.com/en-us/
Microsoft Flowhttps://flow.microsoft.com/en-us/



Thursday, December 22, 2016

Microsoft PowerApps and Flow - TIPS

I will update this post as time goes on.

TIP #1 - Limit the data surfaced to the current user.

NOTE: Context Variables are scoped to a single screen, if you need the variable to be scoped to your entire application, use a Collection Variable instead.

How?
Use the Filter Function and a Context Variable, you can even use the text value of a Textbox instead of a Context Variable if you want.

1. Set a Context variable to be created when the Home or First Screen of your app is made visible, then set the value of this Context variable to User().FullName, User = Current Logged in User. 

ex. In the Advanced tab of your screen properties set the OnVisible property to 
UpdateContext({CurrentUser: User().FullName})

In my humble opinion: The OnVisible property would also be a good place to refresh your datasource connection so that you are seeing the latest and greatest, you can have multiple functions set in this property, just make sure you put a ; at the end of each function, except the last function.

ex: UpdateContext({CurrentUser: User().FullName}) ; Refresh(DataConnection)

2. In your forms Properties section, under the Advanced tab, set Items to 
Filter(SharePointList, Initiator = CurrentUser)

At the time of this post, PowerApps does not support SharePoint Person or Group fields, therefore I manually created and set an Initiator field in my SharePoint List.
You can change the Initiator by triggering a Workflow every time a new row is inserted into your list but for now I set the field manually.

Now your Form or Gallery control will only show the records that the current user submitted. 

Alternate ex. Use the alternate for the Created By Person field, Filter(SharePointList, Author.DisplayName = CurrentUser)

Please Note: This throws the following informational message but seems to still work in my case.

TIP #2 - PowerApps Web Studio

The Web Studio is only available for Microsoft Edge, Chrome or IE11 at this time.


TIP #3 - Reset a dropdown control to it's default by changing it's Reset property with a Context Variable.

A Context Variable is scoped to the current screen.

1. Set the OnVisible property of the screen to UpdateContext({resetDropdown: true})


2. Set the Reset property of the dropdown control to the value of the context variable, resetDropdown

3. Add another button to update the Context value to a different value so that it can be reset when you navigate back to the screen below. UpdateContext({resetDropdown: false})

TIP #4 - Reset a dropdown control to it's default by changing it's Reset property with a Collection Variable.

A Collection Variable is globally scoped to the current app.



1. Set the OnVisible property of the calling screen to ClearCollect(ResetDropDown,First(ResetDropDown).Value = false)





2. Set the Action OnSelect property of the calling button to ClearCollect(ResetDropDown,First(ResetDropDown).Value = true)





3. Set the Dropdown Controls' Reset property of the calling button to First(ResetDropDown).Value






4. Set the Back Button OnSelect property to ClearCollect(ResetDropDown,First(ResetDropDown).Value = false)




Initially setting the controls' Reset property to true does not trigger a Reset of the control. The Reset of the control must be triggered by updating it's Reset property.

TIP #5 - Use a Toggle Control to Filter your Gallery



Hope this is helpful!

- Keith


.NetCore 2.0, AzureAD and OpenIdConnect

.NetCore 2.0, AzureAD and OpenIdConnect If you develop with .NetCore you probably already know that there are some significant changes fr...