Change Cefsharp Locale at runtime - cefsharp

You can't Cef.Shutdown() and reinitialize, you can't initialize multiple times so it seems you can't change the browser locale without completeley restarting your application.
The goal is to switch language of Cefsharp according to our application user language defined at login. E.g. Login with user German Cefsharp is in German, logout with User English Cefsharp should be in English but is still language of initialization.
This Github issue about this topic exists and it is in state Closed. The issue says something about partially implemented and testing and is from the year 2015. I didn't find anything how to do this in C# code.

Just adding to Mohammad Dimas answer.
ChromiumWebBrowser has a IsBrowserInitializedChanged event where you can obtain a RequestContext.
browser.IsBrowserInitializedChanged += Browser_IsBrowserInitializedChanged;
private void Browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e)
{
var browserHost = browser.GetBrowser().GetHost();
var requestContext = browserHost.RequestContext;
string errorMessage = "";
requestContext.SetPreference("intl.accept_languages", languageCode, out error);
}

EDIT:
Ok can you try this one? It will change the local language (navigator.languages) but not sure it will include that pdf viewer.
public static void SetLanguage(string languageCode)
{
Cef.UIThreadTaskFactory.StartNew(() =>
{
using (var context = Cef.GetGlobalRequestContext())
SetLanguage(languageCode, context);
});
}
public static void SetLanguage(string languageCode, IRequestContext context)
{
string error = null;
var success = context.SetPreference("intl.accept_languages", languageCode, out error);
if (!string.IsNullOrEmpty(error))
Log.WriteAsync(LogLevel.Error, string.Format("Error changing language: {0}", error));
}

Related

How to fix Applink only open Safari Xamarin iOS

I am facing problem with AppLink in Xamarin iOS/ I followed the article https://www.xamboy.com/2019/01/08/applinks-in-xamarin-forms/. Everything I configured seems fine:
Enable Associated Domains
Website configuration (apple-app-site-association). I also checked on https://branch.io/resources/aasa-validator/
Your domain is valid (valid DNS). Your file is served over HTTPS.
Your server does not return error status codes greater than 400.
Your file's 'content-type' header was found :)
Your JSON is validated.
iOS project
Go to the Entitlements.plist file, enable the property Associated Domain and add the domain of the website with the format applinks:mydomain.com and applinks:*.mydomain.com and applinks:mydomain and applinks:*.mydomain
PageOne.xaml.cs
private async void _viewmore_Tapped(object sender, EventArgs e)
{
string linkapp = _linkapp.Text;
await Browser.OpenAsync(linkapp, BrowserLaunchMode.SystemPreferred);
}
AppDelegate.cs
public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
var uri = new Uri(url.ToString());
var formsApp = Xamarin.Forms.Application.Current;
formsApp.SendOnAppLinkRequestReceived(uri);
return true;
}
App.cs
protected override async void OnAppLinkRequestReceived(Uri uri)
{
base.OnAppLinkRequestReceived(uri);
if (uri.Host.ToLower() == "mydomain.com" && uri.Segments != null && uri.Segments.Length == 3)
{
string action = uri.Segments[1].Replace("/", "");
bool isActionParamsValid = int.TryParse(uri.Segments[2], out int productId);
if (action == "ProductDetail" && isActionParamsValid)
{
if (productId > 0)
{
App.Current.MainPage = new NavigationPage(new MainView(1));
}
else
{
// it can be security attack => navigate to home page or login page.
App.Current.MainPage = new NavigationPage(new MainView(0));
}
}
}
}
Clear, rebuild,... however I try clicking the _viewmore_Tapped link. It opens again on a Safari page. I checked it on click: Open in it also doesn't have anything related to my application.
How can it open directly in the app? I have consulted the posts, however it does not solve the problem. Looking for solutions from everyone. Thank you

Core3/React confirmation email not sent

This question applies to a core3/react project with an external identity provider, created as follows.
dotnet new react --auth Individual --use-local-db --output conf
and modified to support an external identity provider. The package is added
dotnet add package Microsoft.AspNetCore.Authentication.MicrosoftAccount
and startup is modified
services.AddAuthentication()
.AddMicrosoftAccount(options =>
{
options.ClientId = Configuration["Authentication:Microsoft:ClientId"];
options.ClientSecret = Configuration["Authentication:Microsoft:ClientSecret"];
options.CallbackPath = "/signin-microsoft";
})
After following the instructions provided by Microsoft I tested my work by registering as a user. No errors were thrown but the promised confirmation email never arrived.
Following the troubleshooting advice at the end of the instructions I set a breakpoint at the start of the SendEmailAsync method of my implementation of IEmailSender and repeated the exercise. The breakpoint is not hit.
If I manually confirm the account by updating the database,
I am able to log in.
The Forgot Password link takes me to a password recovery page and using this hits my breakpoint and successfully sends a password reset email with a link that works.
Clearly my implementation of IEmailSender works and is correctly registered. It's not exactly the same as the sample code because I have my own Exchange server and didn't use SendGrid, but it sent an email successfully for password reset and I can repeat this any number of times without a hitch.
Against the slim possibility that it is somehow the cause of the problem, here's my implementation
public class SmtpEmailSender : IEmailSender
{
public SmtpEmailSender(IOptions<SmtpOptions> options)
{
this.smtpOptions = options.Value;
}
private SmtpOptions smtpOptions { get; }
public Task SendEmailAsync(string email, string subject, string htmlMessage)
{
var smtp = new SmtpClient();
if (!smtpOptions.ValidateCertificate)
{
smtp.ServerCertificateValidationCallback = (s, c, h, e) => true;
}
smtp.Connect(smtpOptions.Host, smtpOptions.Port, SecureSocketOptions.Auto);
if (smtpOptions.Authenticate)
{
smtp.Authenticate(smtpOptions.Username, smtpOptions.Password);
}
var message = new MimeMessage()
{
Subject = subject,
Body = new BodyBuilder() { HtmlBody = htmlMessage }.ToMessageBody()
};
message.From.Add(new MailboxAddress(smtpOptions.Sender));
message.To.Add(new MailboxAddress(email));
return smtp.SendAsync(FormatOptions.Default, message).ContinueWith(antecedent =>
{
smtp.Disconnect(true);
smtp.Dispose();
});
}
}
Registration in startup.cs looks like this.
services.AddTransient<IEmailSender, SmtpEmailSender>();
services.Configure<SmtpOptions>(Configuration.GetSection("SmtpOptions"));
SmptOptions is just settings hauled out of appsettings.json and injected into the ctor. Obviously that aspect works or password reset emails wouldn't work.
There can't be anything wrong with the registration because the app stops producing a message about needing to read and follow the account confirmation instructions I linked.
To see whether the problem was caused by some inadvertent side-effect of my code I created an instrumented stub of IEmailSender
public class DummyEmailSender : IEmailSender
{
private readonly ILogger logger;
public DummyEmailSender(ILogger<DummyEmailSender> logger)
{
this.logger = logger;
}
public Task SendEmailAsync(string email, string subject, string htmlMessage)
{
logger.LogInformation($"SEND EMAIL\r\nemail={email} \r\nsubject={subject}\r\nhtmlMessage={htmlMessage}\r\n{new StackTrace().ToString().Substring(0,500)}");
return Task.CompletedTask;
}
}
I also updated service registration to match.
This is the simplest possible instrumented stub, and the observed behaviour is the same, it's invoked when the Forgot Password form is submitted and is not invoked when the Confirm Registration form is submitted.
Has anyone ever got the horrible thing to work? How?
Immediately before the failure, this URL https://wone.pdconsec.net/Identity/Account/ExternalLogin?returnUrl=%2Fauthentication%2Flogin&handler=Callback looks like this
Inspecting the page we find the Register button posts a form to /Identity/Account/ExternalLogin?returnUrl=%2Fauthentication%2Flogin&handler=Confirmation
The code for this is available from the dotnet repository.
After cloning the repo https://github.com/dotnet/aspnetcore.git I read the build instructions and succeeded in building dotnet 5 preview. Then I ran clean before switching to the tagged branch release/3.1 to build debugging packages for core3.1 but this fails because the tagged branch brings into play a version of msbuild that's just slightly too old and the remedy suggested by the error message doesn't seem to work. Since my grip on PowerShell is weak (the build script is PowerShell) I am reduced to code inspection. The pertinent code looks like this.
public override async Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
// Get the information about the user from the external login provider
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
ErrorMessage = "Error loading external login information during confirmation.";
return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
}
if (ModelState.IsValid)
{
var user = CreateUser();
await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
var result = await _userManager.CreateAsync(user);
if (result.Succeeded)
{
result = await _userManager.AddLoginAsync(user, info);
if (result.Succeeded)
{
_logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider);
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
// If account confirmation is required, we need to show the link if we don't have a real email sender
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("./RegisterConfirmation", new { Email = Input.Email });
}
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
ProviderDisplayName = info.ProviderDisplayName;
ReturnUrl = returnUrl;
return Page();
}
It looks like it ought to work. What do we know?
No unhandled errors are thrown, it makes it through to RegisterConfirmation which puts up a message about the email that never comes.
CreateUser is invoked and succeeds. We know this because the user is created in the database. So it definitely gets past there, which implies that ModelState isn't null and .IsValid is true.
IEmailSender.SendEmailAsync is not actually invoked, despite the code above.
If result.Succeeded is true there should be a log message saying something like "User created an account using Microsoft Account provider"
It redirects to https://localhost:5001/Identity/Account/RegisterConfirmation?Email=accountname#outlook.com
I'm seeing log messages for most things. Trying to register a second time after the first pass creates the user but fails to send the email, a warning about a DuplicateUserName appears on the console and in the event log. Setting the confirmation directly in the database we are able to log in and then interactively delete the account, and logs appear for these activities.
But no logs appear for confirmation. What's really doing my head in is the fact that it then redirects to https://localhost:5001/Identity/Account/RegisterConfirmation?Email=accountname#outlook.com
That's crazy. In order to get to there, userManager.AddLoginAsync() must return true and the very next line in that case is a write to the logger about creating the user account.
This makes no sense.
You should send confirmation email yourself, it doesn't send automatically.
After registering your user:
string token = await userManager.GenerateEmailConfirmationTokenAsync(user);
string urltoken = Base64UrlEncoder.Encode(token);
string link = string.Format(emailOptions.ConfirmationUrl, user.Id, urltoken);
string body = $"<a href='{link}'>confirm</a>";
await emailSender.SendEmailAsync(user.Email, "confirmation", body);
I created a whole new project and worked the exercise. It works perfectly.
What's the difference? The failing version was added to an existing project that has been jerked back and forth between 3.0 and 3.1 several times in the course of troubleshooting CICD issues. Clearly it's damaged in some unobvious way and this is a non-issue.
The only reason I haven't deleted the whole question is others may fall down this hole.

AADSTS50059: No tenant-identifying information found when acquiring the code using "{prompt", "none"}"

So I use ADAL library to get id token.
I got the code sample from here
sample code
However, if I set the query string prompt to none. I would get this annoying message AADSTS50059: No tenant-identifying information found in either the request or implied by any provided credentials. If the user is not logged in when getting the code. And the screen will hang in the Microsoft login window.
I need to set it as "prompt", "consent" so even not logged in the user can still perform sign in/consent. But I wan to simply the process, not to get the user go through this sign in/consent every time.
Is there a way to do it so that for not already sign in user an call back error is returned instead of this error and hanging there forever?
According to the doc, {prompt", "none"} should be a valid configuration.
I copy the sample code here for convenient purpose:
protected void Page_Load(object sender, EventArgs e)
{
if (Request.Params["code"] != null)
{
var accesstoken = AcquireTokenWithResource(resource: "https://graph.microsoft.com/");
Response.Write(accesstoken);
}
}
protected void Button2_Click(object sender, EventArgs e)
{
GetAuthorizationCode();
}
public void GetAuthorizationCode()
{
JObject response = new JObject();
var parameters = new Dictionary<string, string>
{
{ "response_type", "code" },
{ "client_id", "clientid" },
{ "redirect_uri", "http://localhost:8099/WebForm1.aspx" },
{ "prompt", "none"},
{ "scope", "openid"}
};
var requestUrl = string.Format("{0}/authorize?{1}", EndPointUrl, BuildQueryString(parameters));
Response.Redirect(requestUrl);
}
public string AcquireTokenWithResource(string resource)
{
var code = Request.Params["code"];
AuthenticationContext ac =
new AuthenticationContext(string.Format("https://login.microsoftonline.com/{0}", "tenantID"
));
ClientCredential clcred =
new ClientCredential("clientID", "clientSecret");
var token =
ac.AcquireTokenByAuthorizationCodeAsync(code,
new Uri("http://localhost:8099/WebForm1.aspx"), clcred,resource).Result.AccessToken;
return token;
}
private string BuildQueryString(IDictionary<string, string> parameters)
{
var list = new List<string>();
foreach (var parameter in parameters)
{
list.Add(string.Format("{0}={1}", parameter.Key, HttpUtility.UrlEncode(parameter.Value)));
}
return string.Join("&", list);
}
protected string EndPointUrl
{
get
{
return string.Format("{0}/{1}/{2}", "https://login.microsoftonline.com", "tenantID", #"oauth2/");
}
}
Can you check the detailed logs of this error. If you are you using ADAL login the it could be local storage caching issue. as when ADAL login is successful it caches the login info into your browser’s local storage to eliminate the need to log in again anytime soon but in certain situations where you will be authenticating against multiple Azure AD instances it will mix-up the authentication. To fix this you will need to clear the browser's storage cache by using the developer tools(F12) then browse to “Application” tab, and then find your tenant from the “Local Storage” -section. After removing all the storage entries for ADAL refresh the page that threw the error before and you should be greeted with a fresh login screen.
Hope it helps.

Using speech synthesizer in ASP.NET web application gets stuck

In an MVC web application I use the SpeechSynthesizer class to speak some text to a .wav file during a function called by a controller action handler that returns a view. The code executes, writes the file, and the action handle returns, but the development server usually, but not always, never comes back with the return page. This is the text-to-speech code:
string threadMessage = null;
bool returnValue = true;
var t = new System.Threading.Thread(() =>
{
try
{
SpeechEngine.SetOutputToWaveFile(wavFilePath);
SpeechEngine.Speak(text);
SpeechEngine.SetOutputToNull();
}
catch (Exception exception)
{
threadMessage = "Error doing text to speech to file: " + exception.Message;
returnValue = false;
}
});
t.Start();
t.Join();
if (!returnValue)
{
message = threadMessage;
return returnValue;
}
I saw a couple of posts for a similar problem in a service that advised doing the operation in a thread, hence the above thread.
Actually, using the SpeechSynthesizer for other things can hang as well. I had a page that just enumerated the voices, but it would get stuck as well. Since there is no user code in any of the threads if I pause the debugger, I have no clue how to debug it.
I've tried Dispose'ing the SpeechSynthesizer object afterwards, calling SetOutputToDefaultVoice, to no avail. I've tried it on both Windows 8.1 and Windows 8, running with the development server under the debugger, or running IIS Express separately.
Any ideas? Is there other information I could give that would be helpful?
Thanks.
-John
Try
Public void Speak(string wavFilePath, string text)
{
using (var synthesizer = new SpeechSynthesizer())
{
synthesizer.SetOutputToWaveFile(wavFilePath);
synthesizer.Speak(text);
return outputFile;
}
}
Task.Run(() => Speak("path", "text")).Result;
It worked for me in IIS Express

LiveAuthClient broken?

It seems very much that the current version of LiveAuthClient is either broken or something in my setup/configuration is. I obtained LiveSDK version 5.4.3499.620 via Package Manager Console.
I'm developing an ASP.NET application and the problem is that the LiveAuthClient-class seems to not have the necessary members/events for authentication so it's basically unusable.
Notice that InitializeAsync is misspelled aswell.
What's wrong?
UPDATE:
I obtained another version of LiveSDK which is for ASP.NET applications but now I get the exception "Could not find key with id 1" everytime I try either InitializeSessionAsync or ExchangeAuthCodeAsync.
https://github.com/liveservices/LiveSDK-for-Windows/issues/3
I don't think this is a proper way to fix the issue but I don't have other options at the moment.
I'm a little late to the party, but since I stumbled across this trying to solve what I assume is the same problem (authenticating users with Live), I'll describe how I got it working.
First, the correct NuGet package for an ASP.NET project is LiveSDKServer.
Next, getting user info is a multi-step process:
Send the user to Live so they can authorize your app to access their data (the extent of which is determined by the "scopes" you specify)
Live redirects back to you with an access code
You then request user information using the access code
This is described fairly well in the Live SDK documentation, but I'll include my very simple working example below to put it all together. Managing tokens, user data, and exceptions is up to you.
public class HomeController : Controller
{
private const string ClientId = "your client id";
private const string ClientSecret = "your client secret";
private const string RedirectUrl = "http://yourdomain.com/home/livecallback";
[HttpGet]
public ActionResult Index()
{
// This is just a page with a link to home/signin
return View();
}
[HttpGet]
public RedirectResult SignIn()
{
// Send the user over to Live so they can authorize your application.
// Specify whatever scopes you need.
var authClient = new LiveAuthClient(ClientId, ClientSecret, RedirectUrl);
var scopes = new [] { "wl.signin", "wl.basic" };
var loginUrl = authClient.GetLoginUrl(scopes);
return Redirect(loginUrl);
}
[HttpGet]
public async Task<ActionResult> LiveCallback(string code)
{
// Get an access token using the authorization code
var authClient = new LiveAuthClient(ClientId, ClientSecret, RedirectUrl);
var exchangeResult = await authClient.ExchangeAuthCodeAsync(HttpContext);
if (exchangeResult.Status == LiveConnectSessionStatus.Connected)
{
var connectClient = new LiveConnectClient(authClient.Session);
var connectResult = await connectClient.GetAsync("me");
if (connectResult != null)
{
dynamic me = connectResult.Result;
ViewBag.Username = me.name; // <-- Access user info
}
}
return View("Index");
}
}

Resources