Hi Rick, Thank you for your excellent post and I have some diffirent idea about the flash of WebView2 here. It is base your achievement 😃.
I will be glad to discuss with you.
If it's on this blog it's free to use.
I'm not going to put a bloody license on every bit of code I post.
Hi Rick, What is the license for the above DebounceDispatcher code? Thanks in advance!
Thanks Rick, finally an article which cleared the clutter and highlighted the essential pieces.
My setup: My app is a .net 4.6 framework app and is not using system.web.mvc, instead it is a layered app. It is called by a frontend in angular, which calls api routes which in turn land on the .net app controllers which are based on system.web.http and uses token based authentication. Currently, we use AuthenticationFilterAttribute for implementing authentication.
We now need to integrate this app with external login providers like google, fb etc.
My question is as follows:
Do i need to add system.web.mvc to use owin startup class, ChallengeResult (inhereting from HttpUnauthorizedResult which is only in system.web.mvc and and all related code)?
Can I continue to use token based authentication, or will Cookie based authentication need to be used in order to have the external 3rd parties integrated?
Will I need to remove the AuthenticationFilterAttribute and just have the owin startup be r responsible for authentication?
Thanks for your help.
Thanks so much, the cmdlet worked for me, everything else was correctly set, but yet the Sharing was missing in mine.
agree with Jason... Still relevant in .NET 6 and also works great for Razor Pages (not just MVC). thank you!
For those looking for an alternative using EF Core 5+ there is the ChangeTracker.Clear method. You can use this method to clear all entities from the change tracker once you no longer need them instead of creating a new DbContext in cases where turning off change tracking is not an option. This method is much more efficient than iterating through all the tracked entities and detaching them and is the preferred method of clearing the tracker.
Hi Rick.
Just wanted to tell you how great a resource you and your blog have been throughout my entire development career. Fifteen years now and I still find your posts helpful and relevant. So thank you for sharing, teaching, and leading the way for many of us.
I have updated it a bit to support also clipped output which is very useful with non wrapping consoles such as Windows Terminal. The code is located here: https://github.com/Siemens-Healthineers/ETWAnalyzer/blob/main/ETWAnalyzer/Infrastructure/ColorConsole/ColorConsole.cs. Perhaps ETWAnalyzer could help you to extract some weird TPL tasks states from tests which are running with ETW profiling. See https://github.com/Siemens-Healthineers/ETWAnalyzer/blob/main/ETWAnalyzer/Documentation/DumpCPUCommand.md what you can do with the dump command. With async code I have found -firstlastduration interesting because some methods of the compiler generated state machine are executing sync first and the rest after the await much later which gives you a good estimate how long the async methods was really executing.
Hi Rick,
Thank you for your post!
The only special character I cannot find the solution for is plus (+). When I use plus character in route (or encoded value %2B) the routing does not working at all - it opens the default page.
For example:
www.sitename.com/1 - ok
www.sitename.com/1/ - ok
www.sitename.com/1/2 - ok
www.sitename.com/1+2 - routing not working
www.sitename.com/1%2B2 - routing not working
Is there any way to encode plus character to use it in the route?
Thanks,
Michael
@George - yes that should also work for the same reason that Task.Delay() works as the first line in the handler.
Task.Delay()
Hi there i am new to Wpf and async paradigm and i was wondering if the following code would work properly ?
private async void TreeFavorites_MouseMove(object sender, MouseEventArgs e) { await MouseMoveTask(sender, e); } private async Task MouseMoveTask(object sender, MouseEventArgs e) { if (e.LeftButton != MouseButtonState.Pressed) return; if (Model.ActiveEditor == null || !(await Model.ActiveEditor.IsEditableDocument())) return; var selected = TreeFavorites.SelectedItem as FavoriteItem; if (selected == null) return; var mousePos = e.GetPosition(null); var diff = startPoint - mousePos; if (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance * 2 || Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance * 2) { var effect = DragDropEffects.Move; if (Keyboard.IsKeyDown(Key.LeftCtrl)) effect = DragDropEffects.Copy; var treeViewItem = WindowUtilities.FindAnchestor<TreeViewItem>((DependencyObject) e.OriginalSource); if (treeViewItem == null) return; // add both files and text var dragData = new DataObject(DataFormats.FileDrop, new string[] {selected.File}); // *** Hangs here - until moved out of WebView when dropping dragData.SetText(selected.File + "|" + selected.Title); DragDrop.DoDragDrop(treeViewItem, dragData, effect); } }
Rick, find "the the" in your blog text and you will find a repeated "the" word... 😉
You mind adding in big, black, bold, letters that app.UseRouting(); has to be placed after app.use
yea, it's "obvious" in retrospect, but it's not something one might actually notice for several hours of testing...
It would be interesting to see a "reference" implementation using a JoinableTaskFactory, since the VisualStudio.Threading.Analyzers Package will flag both async void (with https://github.com/microsoft/vs-threading/blob/main/doc/analyzers/VSTHRD100.md) and Dispatcher calls (with https://github.com/microsoft/vs-threading/blob/main/doc/analyzers/VSTHRD001.md) as issues.
Hi, first if all thanks this post was very useful. But i have one question if you know how did you determine if is a http 1.1 or 2.0 request the one that was made.
Have you tried using a TaskCompletionSource? It should be a better solution than Task.Delay(1).
TaskCompletionSource
Task.Delay(1)
Hi, thank you for your article, I've been using "Publish" for years but I never understood how to select a specific server or publishing profile in general. When you click on Publish I don't have choices, it will publish the file right away using the current profile. This is a major draw-back as I need to publish the whole project anyway the very first time in order to be able to select the profile/server I want to work with, after that I can use Publish single file. Thank you.
@Gunnar - you can specify the name of the cookie that is created when you authenticate. It's one of the optional configuration values you can provide in the AuthenticationProperties structure.
AuthenticationProperties
Say you want to integrate two systems that both have cookie authentication. The user has to be logged in to both systems at the same time.
Instead of JWT_OR_COOKIE it would be something like COOKIE1_AND_COOKIE2…
How would you approach that?
Hey Rick! Been wondering for past 30 minutes what might be wrong with my connection. I even tested my previous projects and connection worked all fine. Despite setting up everything as per my previous projects the connection open method always failed until I found your solution.
Thanks for this!
Hi! Thanks for the detailed explanation. I am working on a demo API that uses Jwt authorization. I am stuck however, on assigning roles to users on registration/creation. In addition, I envisaged an app where only a user with the "Admin" role can successfully interact with the /registration endpoint. Your example clearly demonstrates how to add claim to the Jwt that will be returned to the user and the user object retrieved from the Db already has the list of roles as part of its property which is very different from what I am trying to achieve. I have spent the last 3 days hopping from one blog to blog and stackoverflow too, I get even more confused because I saw similar issues like this one https://stackoverflow.com/questions/21734345/how-to-create-roles-and-add-users-to-roles-in-asp-net-mvc-web-api that bring even more confusion as the use of RoleManager is not very clear from these posts. Can you,perhaps in your free time, explain how RoleManager/IdentityRole would come in handy in this context?
I still have this exception with .NET 4.8 4.8.4770.0. I have opened a MS call for this one. Lets see if that is a regression of an earlier fix, or if it is a long standing issue.
I hadn't come across your posts in a while, but this issue totally hit me today and my first reaction was "OH THAT GUY!" - thanks as always.
I know this was for non-Identity auth but just for the sake of it - and just because I went through the same multi-auth pain only a couple of months ago, there are only a couple of important changes to use Identity. Namely:
AddCookie
AddDefaultIdentity<T>()
AddEntityFrameworkStores
return CookieAuthenticationDefaults.AuthenticationScheme;
return IdentityConstants.ApplicationScheme;
Perhaps I'm a bit stupid or naive but it took me ages to realise/understand that the Identity auth uses a different name for its cookie scheme. [The Authenticate endpoint is a little different 'cos you need to defer to, or use, the Identity code for the cookie but the key is that scheme name.]
"In my case, the scenario is my Swagger/OpenAPI endpoint which should require a login to access. This endpoint is served directly by the Web Server and consumed in the browser, so it can't use a Bearer token."
You can add authentication to OpenAPI by adding a OpenApiSecurityRequirement to the operation's Security dictionary. The key needs to match a security definition added in the AddSwaggerGen options callback, where you define the flow.
The following is how I've configured our OpenID/OAuth client credentials endpoint definitions. (I've removed some internal sensitive code around policy and scope names, but it should be close enough )
public void Apply(OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context) { ... var requiredScopes = new[] { "Scope1", "Scope2" }; var scheme = new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } }; operation.Security.Add(new OpenApiSecurityRequirement() { [scheme] = requiredScopes.ToList() }); }
services.AddSwaggerGen(c => { c.AddSecurityDefinition("oauth2", new Microsoft.OpenApi.Models.OpenApiSecurityScheme { Type = Microsoft.OpenApi.Models.SecuritySchemeType.OAuth2, Flows = new Microsoft.OpenApi.Models.OpenApiOAuthFlows { ClientCredentials = new Microsoft.OpenApi.Models.OpenApiOAuthFlow { TokenUrl = ..., Scopes = new Dictionary<string, string> { ["Scope1"] = "First scope", ["Scope2"] = "Second scope", } } } } }
THANK YOU, I was just starting down the path of trying to solve this.
Also, if you know your non-SPA (cookie-based) app is residing in a different path (aspx, for example), you can set the path attribute of the generated cookie to match that path. Then, only requests to /aspx/* would contain the cookie and SPA will remain unaffected.
aspx
path
/aspx/*
@Dalibor - Good point but it depends on how sensitive the application is. In my application for example, 90% of interaction will be read only unauthenticated and only very few requests that are authenticated, so it wouldn't matter much. For an app that is 99% SPA and 1% something else it might make sense to separate the cookie out - not only for security but also to reduce request size perhaps.
Good point to make here. I'll add a note into doc.
Nice article, but if you have different authentications for different endpoints wouldn't it be easier simply to flag them to use different schemas in the Authorize attribute (https://docs.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme?view=aspnetcore-6.0)? You could then have separate login for each. As it is you are supporting authentication to your API endpoint with Cookies which is not strictly needed and potentially increases the attack surface (sorry if sounding pedantic here, its not my intent, I'm just curious).
@Dean - Cookies are persisted in the user profile that's specific to your WebView when you create the environment. If you reuse the browser environment in the same folder each time then any browser state should persist across usages. Perhaps you're using a new location each time, or wiping out the browser folder each time?
Rick, really nice article. You have a great way of explaining things in a very understandable way. Well done. I would be very interested to know how to deal with cookies. I'm writing a winforms desktop application and logging in to a membership website with a standard username and password. However, upon each restart of the application I'm forced to log in again. Right now I don't see a way to save and restore cookies in order to retain the login status. Any ideas?
@Rick - that's not possible with the WebView - there's no DOM object model that is accessible from .NET. You can only do this work inside of the Web Browser using JavaScript that you invoke. You can inject the JavaScript into the document if necessary (ie. if you don't control the source) and then call that from .NET to perform a task and or return a result.
Hi Rick interesting Article since I am not able to find hardly and information on using Webview2 in the vb.net application. My application donwloads a map from google for the users based on information they enter and google has not blocked IE11 from accessing the data. I do have 2 questions for you. Would it be possible in java (which I know little of.) to be able to pass out of the java something like this.
Dim document As System.Windows.Forms.HtmlDocument = Webbrowser.Document WebSource.Text = document.Body.InnerHtml
And so far I have not found how to create a routine like this in java.
For Each element As HtmlElement In Webbrowser.Document.GetElementsByTagName("button") If element.InnerText = "Next" Then element.InvokeMember("click") End If Next Do Thread.Sleep(400) Loop Until Webbrowser.IsBusy = False
Any help or pointers to where to find how to handle this would be appreciated.. Rick
This was an excellent post I came across two years ago and have used it half-dozen times since. It has saved my bacon from a lot of hassles, especially when refactoring a large project with a lot of schema changes.
Deleting the .vs file works, but the problem comes back quickly. I didn't see the problem at all until updating Visual Studio (Microsoft Visual Studio Community 2019, Version 16.11.11) > Does anyone know of a way to permanently fix this?
THanks - been battling with this for months!
great post! thanks for the example, not all heros wear cape
I have issues installing and binding a certificate with powershell. The certificate installs correctly and the binding is correct as well but on binding, the SSL certificate is not selected. I get the below error:
SSL Certificate add failed, Error: 1312 A specified logon session does not exist. It may already have been terminated.
My script:
$hostname="caudaint.int.capinet" $iisSite="Cauda.BranchView" $secureString = convertto-securestring "pass" -asplaintext -force Import-PfxCertificate -FilePath "D:\Data\SSL\cbcaudaappint01.int.capinet.pfx" -CertStoreLocation Cert:\LocalMachine\My -Password $secureString $cert = (Get-ChildItem cert:\LocalMachine\My | where-object { $_.Subject -like "*$hostname*" } | Select-Object -First 1).Thumbprint $guid = [guid]::NewGuid().ToString("B") netsh http add sslcert hostnameport="${hostname}:443" certhash=$cert certstorename=MY appid="$guid" New-WebBinding -name $iisSite -Protocol https -HostHeader $hostname -Port 443 -SslFlags 1 $cert
Anyone knows what could be causing this issue?
@Dan - Not sure what you're asking. This is a static Web site, there's no CORS on the client - CORS is handled on the server. This doesn't handle any server configuration - the server is going to be there already and you're just pushing content. For the IIS Site that is receiving the files you can affect configuration via web.config with the deployment but that's about it.
web.config
Hi Rick. Thanks for sharing the post! How do you deal with cross origin issues when the Angular frontends access the backend? Do you enable CORS? Or do you somehow have everything on the same origin? Also, how do you handle securing the API calls from the Angular frontends?
Yeah that bugs me too. The profiles have moved several times in recent versions and to make things worse, VS 2022 can't read profiles directly: It creates .pubxml files, but when you try to import a profile it looks for .publish-settings which are different. The only way this works is via a setting in the .csproj.user file that points at an existing .pubxml file:
.pubxml
.publish-settings
.csproj.user
<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <NameOfLastUsedPublishProfile>.\Properties\PublishProfiles\IISProfile.pubxml</NameOfLastUsedPublishProfile> </PropertyGroup> </Project>
Thank you! This saved me! Spent many hours playing with all possible settings, but turns out my web.config was corrupted somehow and this is what did the trick:
The fix in my case was to copy the web.config.default to web.config and voila everything snapped back to working.
These values are defined in the .NET master configuration web.config file which lives in:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\web.config
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\web.config
that made all my dependent tests suddenly to fail, when everything was building... doh... thanks for the post, a lifesaver 😉
This looks like a really good, robust solution and it's going in our project today. In a previous life, I had a custom reviver function to do this in my custom Http library. In the modern world, with Axios, that's no longer an option, so globally modifying the JSON object will do the trick. Axios is hermetically sealed, which is a bit surprising.
If I can make 2 comments: Native javascript date serialization no longer sucks, and, as a result, moment.js is no longer necessary (or maintained). Second, it would be great if this was an npm.
Publish profiles now seem to be located under Project Properties (the little wrench icon, not the Properties page). Took 4-eva to find em, too; why does MSFT make the stupidest simplest repetitive task so hard to find in help/bugs/etc.? At least as of 16 and 17 versions of VS.
this is still relevant in .NET 6 - thanks for the post! love your blog
@Doug - WebSurge on its own doesn't 'fish out' any values. But you can:
I show both in the video. The latter requires that the site exposes the OpenAPI/Swagger endpoints in order to be able to import from there.
This is awesome, thanks. I'll be playing with this pretty much all weekend long!
Hi Rick!
Dumb question time 😃. I have not used WebSurge, but am pivoting to where I may. I noticed what appeared to be tests that indicated knowledge of your album site. I was curious to know if testing a site required prior knowledge or does WebSurge "fish out" the links and then present you with the ability to manually add good or bad values?
Thanks! And thanks again for your contributions over the years to the community, writ large.
Regards,
Doug