How to fix Applink only open Safari Xamarin iOS - xamarin.forms

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

Related

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.

Await method before app starts in the same UI thread

I'm trying to check which page should load my app at the beginning, first of all I check a database table if I find the login information stored I want to push the once named StartPage(), as I'm working with the database the method includes an await if there isn't any data stored I want to push the LoginPage(). I have tried following this example Xamarin.Forms Async Task On Startup . My code is :
public App()
{
int result;
InitializeComponent();
ThreadHelper.Init(SynchronizationContext.Current);
ThreadHelper.RunOnUIThread(async () => {
MainPage = new ActivityIndicatorPage();
result = await InitializeAppAsync();
if (result == 0)
{
PushLoginPage();
}
else
{
PushStartPage();
}
});
}
public void PushStartPage()
{
NavigationPage nav = new NavigationPage(new StartPage());
nav.SetValue(NavigationPage.BarBackgroundColorProperty, Color.FromHex("#D60000"));
MainPage = nav;
}
public void PushLoginPage()
{
MainPage = new Login();
}
public void PushLoginPage(string email, string password)
{
MainPage = new Login(email, password);
}
private async Task<int> InitializeAppAsync()
{
if (ViewModel == null)
ViewModel = new MainViewModel(this);
return await ViewModel.LoginViewModel.PushInitialPage();
}
But throws the following exception and as the author of the article says, is not recommended to do it.
Exception
Another option tried was overriding the OnStart() method but didn't work either.
protected override async void OnStart()
{
Task.Run(async ()=> { await InitializeAppAsync(); });
}
The PushInitialPage method:
public async Task PushInitialPage()
{
if (_app.Properties.ContainsKey("isLogged"))
{
var user = await UserDataBase.GetUserDataAsync();
var result = await Login(user.Email, user.Password);
if (result.StatusCode != 200)
{
return 0;
///PushLoginPage();
}
else
{
return 1;
//PushStartPage();
}
}
else
{
return 0;
}
}
When the OS asks your app to show a page, it must show a page. It can't say "hold on a minute or two while I talk to this remote server over an iffy network connection." It has to show a page Right Now.
So, I recommend bringing up a splash page - your company or app logo, for example. When the splash page shows, then call InitializeAppAsync, and based on the result, switch to the login or start page or nice user-friendly offline error page.
In Xamarin.Forms we have properties called 'Application.Current.Properties'. By using this we can able to save the any data type. So once user login in to the application you can set one flag and set it is true. Then after every time when user login in to the application you can check this flag and navigate your respective page.
Sample Code :
App.cs :
public App()
{
if (Current.Properties.ContainsKey("isLogged"))
{
if((bool)Application.Current.Properties["isLogged"])
{
// navigate to your required page.
}
else
{
// naviate to login page.
}
}
else
{
// naviate to login page.
}
}
At first time application open it checks the 'isLogged' property is presented or not, if not it will move to the login page. When user login into the application by using his credentials, we need to create 'isLoggin' property and set as true. Then after if user try to login it checks the condition and navigates to the respective page.
Saving Property SampleCode :
Application.Current.Properties["isLogged"] = true;
await Application.Current.SavePropertiesAsync();
write above code for after login into the application. If a user log out from the app you need to set 'isLogged' flag is false.

Change Cefsharp Locale at runtime

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));
}

redirect_uri_mismatch in Google APIs in ASP.NET

I am trying to upload video on my YouTube channel using ASP.NET Web Form. I created developer account and tested it working using JavaScript based solution which requires login every-time to upload a video.
I want users of my website to upload video directly on my channel and every auth should be in code behind, user should not be prompted to login. For this I wrote following class:
public class UploadVideo
{
public async Task Run(string filePath)
{
string CLIENT_ID = "1111111111111111111111.apps.googleusercontent.com";
string CLIENT_SECRET = "234JEjkwkdfh1111";
var youtubeService = AuthenticateOauth(CLIENT_ID, CLIENT_SECRET, "SingleUser");
var video = new Video();
video.Snippet = new VideoSnippet();
video.Snippet.Title = "Default Video Title";
video.Snippet.Description = "Default Video Description";
video.Snippet.Tags = new string[] { "tag1", "tag2" };
video.Snippet.CategoryId = "22"; // See https://developers.google.com/youtube/v3/docs/videoCategories/list
video.Status = new VideoStatus();
video.Status.PrivacyStatus = "unlisted"; // or "private" or "public"
using (var fileStream = new FileStream(filePath, FileMode.Open))
{
var videosInsertRequest = youtubeService.Videos.Insert(video, "snippet,status", fileStream, "video/*");
videosInsertRequest.ProgressChanged += videosInsertRequest_ProgressChanged;
videosInsertRequest.ResponseReceived += videosInsertRequest_ResponseReceived;
await videosInsertRequest.UploadAsync();
}
}
void videosInsertRequest_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
{
switch (progress.Status)
{
case UploadStatus.Uploading:
//Console.WriteLine("{0} bytes sent.", progress.BytesSent);
break;
case UploadStatus.Failed:
//Console.WriteLine("An error prevented the upload from completing.\n{0}", progress.Exception);
break;
}
}
void videosInsertRequest_ResponseReceived(Video video)
{
Console.WriteLine("Video id '{0}' was successfully uploaded.", video.Id);
}
public static YouTubeService AuthenticateOauth(string clientId, string clientSecret, string userName)
{
string[] scopes = new string[] { YouTubeService.Scope.Youtube, // view and manage your YouTube account
YouTubeService.Scope.YoutubeForceSsl,
YouTubeService.Scope.Youtubepartner,
YouTubeService.Scope.YoutubepartnerChannelAudit,
YouTubeService.Scope.YoutubeReadonly,
YouTubeService.Scope.YoutubeUpload};
try
{
// here is where we Request the user to give us access, or use the Refresh Token that was previously stored in %AppData%
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets { ClientId = clientId, ClientSecret = clientSecret }
, scopes
, userName
, CancellationToken.None
, new FileDataStore("Daimto.YouTube.Auth.Store")).Result;
YouTubeService service = new YouTubeService(new YouTubeService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "YouTube Data API Sample",
});
return service;
}
catch (Exception ex)
{
//Console.WriteLine(ex.InnerException);
return null;
}
}
}
Now using this class into Page_Load() of default.aspx, as given below:
protected void Page_Load(object sender, EventArgs e)
{
try
{
string path = "C:/Users/abhi/Desktop/TestClip.mp4";
new UploadVideo().Run(path).Wait();
}
catch (AggregateException ex)
{
//catch exceptions
}
}
When I run this (default.aspx) page, i see http://localhost:29540/default.aspx spins, so I used them on Google Developer Console as given below:
Upon running http://localhost:29540/default.aspx opens a new tab which displays "redirect_uri_mismatch" error as given below:
At this point if I look in browser address, I see redirect_uri is set to http://localhost:37294/authorize and I just manually change this to http://localhost:29540/default.aspx which generates a token.
So, can you suggest where to make changes in above code so that request uri fills up correctly from my app side.
A day waste then I came to know below redirect URL is working for all localhost web applications. So you need to use below URL on google developer console web application's "Authorized redirect URIs".
http://localhost/authorize/
For anybody still having this issue in 2022, I figured out a solution. If you are using https://localhost:portnumb as your redirect uri, just use https://127.0.0.1:sameportnumb as your redirect uri. It ended up working for me

How to prevent open redirection attacks?

what is the best approach to prevent open redirection attacks.Currently i am developing asp.net website.I want to make sure not to redirect the users to external links up on successful login?
Edit: Is it possible implement the solution without changing the existing code?
I'm assuming you're using the login control.
You should hook-up a check that the ReturnUrl parameter is a local url (and not one pointing to a different domain). The loggedin event would be a good place to do something like this:
void OnLoggedIn(object sender, EventArgs e)
{
string returnto = Request.QueryString["ReturnUrl"];
if (returnto != "" and isLocalUrl(returnto)) Response.Redirect(returnto);
}
where you can use the definition of IsLocalUrl given here
private bool IsLocalUrl(string url)
{
if (string.IsNullOrEmpty(url))
{
return false;
}
Uri absoluteUri;
if (Uri.TryCreate(url, UriKind.Absolute, out absoluteUri))
{
return String.Equals(this.Request.Url.Host, absoluteUri.Host,
StringComparison.OrdinalIgnoreCase);
}
else
{
bool isLocal = !url.StartsWith("http:", StringComparison.OrdinalIgnoreCase)
&& !url.StartsWith("https:", StringComparison.OrdinalIgnoreCase)
&& Uri.IsWellFormedUriString(url, UriKind.Relative);
return isLocal;
}
}

Resources