Goal
I am trying to load Sharepoint site pages in Xamarin WebView with URL like SharePoint Android/iOS app showing sitepages without asking user to log in at webview.
Progress
The app uses ADAL's AcquireTokenAsync() to get the access token from Azure AD and set the WebView source as SharePoint modern site pages URL. As the user first-time signs in to the app with AcquireTokenAsync(), the webview is load site page successfully.
Issue:
After the user closes and relaunches the app, it uses AcquireTokenSilentAsync() to authenticate. But when the webview loading the site page with URL, Microsoft ask the user to log in again at webview.
Notes
After authentication with AcquireTokenSilentAsync(), there are some cookies missing in CookieManager and NSHttpCookieStorage.
I also tried with MSAL and the issue still persist.
Question
Why there are cookies missing when the app using AcquireTokenSilentAsync(). How can achieve the goal without asking user to log in twice?
Here is some snapshot of code for authentication and webview.
var platformParams = new PlatformParameters((Activity)Forms.Context);
try
{
authResult = await authContext.AcquireTokenSilentAsync(resource, clientId);
}
catch (Exception)
{
authResult = await authContext.AcquireTokenAsync(resource, clientId, uri, platformParams);
}
var sitePageWebView = new WebView
{
Source = "https://company.sharepoint.com/SitePages/page-title.aspx",
MinimumHeightRequest = 1000,
HorizontalOptions = LayoutOptions.FillAndExpand
};
Related
In Xamarin.Forms application, I am doing login through OidcClient, I am facing one issue with Android. After the first successful login, the user does not go through the login again. When oidcClient LoginAsync gets called it automatically approves the user. Probably it is caching the login information in the browser. I don't want that, User should always go through the whole login process again. It is behaving perfectly in iOS.
LoginRequest request = new LoginRequest();
var cancellationToken = new CancellationTokenSource();
request.FrontChannelExtraParameters.Add("prompt", "login");
var result = await oidcClient.LoginAsync(request, cancellationToken.Token);
I'm using MSAL in a react based SPA and a PCF component which is imported into a canvas app and this canvas app is embedded into Dynamic365 Field service CRM.
I have created a Azure AAD app registration and added these redirectUri in Single Page Application
localhost:3001
*.crm.dynamics.com/main.aspx //main.aspx because i referenced this Issue #190 earlier.
So when i open my app in localhost:3001, it work fine. It popup a login window ask for login and when close it gives a access token in response. but in second condition pop up is not redirecting back to it's main page but opens in the popup itself.
I have used this inside PCF component which i am using in D365 Field service.
import { PublicClientApplication } from "#azure/msal-browser";
const msalConfig = {
auth: {
clientId: 'client_id',
authority: 'https://login.microsoftonline.com/tanent_id',
redirectUri: '*.crm.dynamics.com/main.aspx',
}
};
const msalInstance = new PublicClientApplication(msalConfig);
var loginRequest = {
responseMode:"query",
scopes: ["openid", "offline_access", "https://storage.azure.com/user_impersonation"],
};
msalInstance.loginPopup(loginRequest)
.then(response => {
console.log(response)
})
.catch(err => {
console.log(err)
});
Reproduction Steps
Create a PCF component with above Code Snippets.
Add this PCF component inside a canvas app.
Embed new created canvas app inside a D365 Field Service CRM.
When you open your PCF component inside the D365 Field Service CRM which runs on this *.crm.dynamics.com/main.aspx, It will open a login Popup. we have to login from that popup.
This popup does not close but open the redirect URL inside the popup only.
and I have added redirect URI inside App registration like this
I have already used every variant like *.crm.dynamics.com/main.aspx and *.crm.dynamics.com/ and *.crm.dynamics.com.
Expected Behavior
A login popup pops up and after the user logs in the pop up closes itself and the loginPopup promise resolves (or rejects).
Actual Behavior
A login popup pops up and after the user logs in the pop up redirects back to my site in the pop up.
How can I create a login page using Xamarin.Forms?
I'm debugging on Android device and
on the page I try to query the given username and password from an MSSQL database in MSSQL
and if the login be successful, the page will navigate to a new page, else will show authentication failed.
How can I do this in Xamarin.Forms?
(Xamarin LoginFlow Example linked at bottom of answer)
You would have multiple Xamarin.Forms pages and use a NavigationPage to push or pop them from the (lifecycle) stack.
Ref: https://developer.xamarin.com/api/type/Xamarin.Forms.NavigationPage/
Think of each Page a complete self-contained mini application. So the Login page might handle the getting the UserID/Password interactively from the user, performing a authorization check via a server via a Rest api that performs your SQL query. If login is successful it pushes a new Forms-based page on the NavigationPage stack. i.e.
SplashScreen -> LoginPage -> MainPagePage
LoginFlow Xamarin example
This sample demonstrates how to manipulate the navigation stack in order to only display the main page of the application once the user has successfully logged in.
For more information about the sample see Hierarchical Navigation.
What you need is the following functionalities:
Web APIs (Web Service for Login - Hosted on internet or local network - Connected with your MSSql Server)
A Login Screen in Xamarin.Forms Application (You can design it on your own or use the above mentioned demo)
On Login Screen's 'Login' button click, you need to call the Login Web Service from Xamarin.Forms application.
This requires networking (Internet Permission on Android Project, through Manifest.xml). You can use Microsoft.Net.Http for Web Service Calls.
(Look at this example http://www.c-sharpcorner.com/UploadFile/nirmal.hota/consuming-restful-web-service-in-xamarin-forms-project-using/ )
If your server responses with JSON and you need to parse JSON then you can use Json.Net (Newtonsoft.Json).
Note: What I am trying to tell you is that you need to use Web Services that are connected to the database and hosted on a server. From Xamarin.Forms application, you have to call this web-service with appropriate parameters. This requires networking, so you can use Microsoft.Net.Http (or there are other plugins also available). To get response from the Web Service, if it is in Json then you can use Newtonsoft.Json to parse Json.
There is a full example of an app with login page: xamarin-forms-samples/LoginDemo/ provided by Xamarin.
So lets say you have a LoginController on asp.net web api (if you don't know how to create an asp.net web api project etc. this is not the place to learn it:) )
[RoutePrefix("v1/login")]
public class LoginController : ApiController
{
[Route("")]
[HttpPost] //I'm using post!!!!!! you may use get etc.
public IHttpActionResult Login(UserLoginData request)
{
var userData = CheckFromDb(request);
return Json(userData);
}
}
It checks your request from db (UserLoginData is a simple class lets say holds username and password), than if user exists you return another class (lets say it is UserData hat holds name,surname, birthday etc.). If it can not find login it may return null. It's up to you.
So it will be available on your host machine like
localhost:34252/v1/login
(34252 your port-may change for you)
So you have to call it from the device (xamarin.forms) code like this
public string Post(string url, UserLoginData userLoginData)
{
//url is login url as defined above and userLoginData holds your
//user interface (textbox) data :)
using (var client = new HttpClient())
{
client.Timeout = new TimeSpan(0, 0, 30);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
StringContent content;
content = new StringContent(JsonConvert.SerializeObject(userLoginData), Encoding.UTF8, "application/json");
HttpResponseMessage response = client.PostAsync(url, content).Result;
if (!response.IsSuccessStatusCode)
{
throw new Exception("call failed");
}
//this is your response as json
var responseString = response.Content.ReadAsStringAsync().Result;
//you can convert it and check to a specific class if you need
var userData = JsonConvert.DeserializeObject<UserData>(responseString);
}
}
So UserLoginData and UserData classes both must exists on both sides. You can use Newtonsoft.Json library for json parsing (jsonconvert is it's method). Hope this helps you
And android device simulator networking is a bit tricky. You may not connect to localhost directy. It may take an address like 10.0.2.* for androidemulator or 10.71.34.* for xamarin simulator. Please check your network cards for ip addresses and findthe correct one via "cmd.exe" and "ipconfig /all" command.
Today I tried to login on a page containing an auth form.
I'm using RestSharp on Windows Phone 7 and I tried almost everything but it didn't work.
In a browser on my desktop computer, when I use the login page (http://myanimelist.net/login.php) and enter my valid credentials, I'll be redirected to the panel page (http://myanimelist.net/panel.php)
On Windows Phone 7 (and 8) when I try to use RestSharp to auth myself, I'm redirected to the panel page but with the following error :
Error: You must first login to see this page.
In fact I'm not authenticated and I don't have the right to see the panel page.
I tried the same thing in a WPF application and there it worked.
In my WPF app I've the following code:
var client = new RestSharp.RestClient();
client.BaseUrl = "http://myanimelist.net/login.php";
client.Authenticator = new SimpleAuthenticator("username", "mylogin", "password", "mypassword");
client.CookieContainer = new CookieContainer();
var request = new RestRequest(Method.POST);
var response = client.Execute(request);
The property "response.Content" will contain the page with some informations and a Welcome message with my login. It means that I'm authenticated.
But...
With the following code in Windows Phone 7, the property "response.Content" will contain a page with some informations and the following message in it:
Error: You must first login to see this page
And here is the WP7 code used:
var client = new RestSharp.RestClient();
client.BaseUrl = "http://myanimelist.net/login.php";
client.Authenticator = new SimpleAuthenticator("username", "mylogin", "password", "mypassword");
client.CookieContainer = new CookieContainer();
var myRequest = new RestRequest(Method.POST);
client.ExecuteAsync(myRequest, response =>
{
var t = response.Content;
});
Did I miss something? Is there a difference between RestSharp on WP7 and a WPF app?
I used Fiddler to check what happens and I can see that the cookie is never set on WP7 (in the WPF app the cookie is set).
Edit:
I found something interesting, when I get the response, I can see in Fiddler that a cookie is set in the WPF app and in the WP7 app.
So I tried to add the cookie in my WP7 app before to make my request and it worked.
I added the value I could see in Fiddler but I can't find a way to retrieve these values with RestSharp.
Here is what I tried to add before to execute my request:
myRequest.AddParameter("A", "theFirstValue", ParameterType.Cookie);
myRequest.AddParameter("B", "theSecondValue", ParameterType.Cookie);
In fact I found a way to do it.
In the response I've the cookie in the Header (Set-cookie). I extract the cookie from the header and add it to my next request which requires the cookie.
I am struggling with something and I am not sure if this is possible or not with the current WP7 API. Basically my WP7 App uses WebRequest class to issue web requests to log into a third party website using username and password credentials of the user. I store session state/cookie information in a CookieContainer object so that I can make further requests for data from the web site and this all works fine, pretty standard scenario so far.
But what I would like to do now is offer a link to take the user to the secure area of web site without needing the user to log in again when accessing the site from the phones browser. If I simply call a webbrowsertask it takes me to the website but the browser has no knowledge of the cookies I stored in my app so the web site redirects the user to the log in page. What I need/want to be able to do is provide the CookieContainer object I have stored from my app requests through to the webbrowsertask so that I can go straight to the page I want.
WebBrowserTask webBrowserTask = new WebBrowserTask();
webBrowserTask.URL = "https://www.xxx.com/loginarea";
webBrowserTask.Show();
Any suggestions/thoughts on how I can do this?
Thanks.
I don't think you can get the cookies to the main IE web browser
However, if you were prepared to host the browser control inside your app, then you should be able to inject the cookie values using javascript.