.Net MAUI Web Authenticator Hangs - android

Following the official guidance, I've implemented the WebAuthenticator in my .NET Maui Android App to handle SSO auth with Facebook.
It's a fairly straightforward implementation, I've added the .Net core backend on a website with the mobile auth endpoint implemented as directed, and that appears to be working.
However when the WebAuthenticator.AuthenticateAsync method is calling the API, it's hanging:
private void OnFacebookClicked(object sending, EventArgs e)
{
try
{
WebAuthenticatorResult authResult = WebAuthenticator.Default.AuthenticateAsync(
new Uri("https://testwebsite.azurewebsites.net/mobileauth/Facebook"),
new Uri("xamarinessentials://")).Result;
string accessToken = authResult?.AccessToken;
}
catch (TaskCanceledException)
{
// User stopped auth
}
}
I don't get any kind of response back - no timeout, no exception, nothing just a straight hang.
Any advice?

Related

How can I use Azure ADB2C to implement mobile app AuthN/AuthZ?

The current Azure ADB2C Mobile app example here that forcibly opens a browser component outside the app and after Sign-In redirects back to the app.
Is there any way to skip this ugly and clunky Sign-In page altogether and do Sign-In/Sign-up directly from a mobile app component? I want to create my own Sign-In Activity so I only go to the portal to get the token through a REST uri and never have to open a browser outside my app.
You can create a resource owner policy that enables you to gather a user credential in your mobile app and then validate it in your Azure AD B2C tenant.
You can use a client library, such as AppAuth for Android or AppAuth for iOS, to manage the policy invocation for you.
This is an old question, but the question still resonated with me a year later. There is an answer to this:
.WithUseEmbeddedWebView(true)
The full snippet (taken and adapted from an Azure GitHub sample) is:
async void OnLoginButtonClicked(object sender, EventArgs e)
{
AuthenticationResult result;
try
{
//bool useEmbeddedWebView = !App.IsSystemWebViewAvailable;
result = await App.AuthenticationClient
.AcquireTokenInteractive(Constants.Scopes)
.WithPrompt(Prompt.SelectAccount)
.WithParentActivityOrWindow(App.UIParent)
.WithUseEmbeddedWebView(true)
.ExecuteAsync();
await Navigation.PushAsync(new LogoutPage(result));
}
catch (MsalException ex)
{
if (ex.Message != null && ex.Message.Contains("AADB2C90118"))
{
result = await OnForgotPassword();
await Navigation.PushAsync(new LogoutPage(result));
}
else if (ex.ErrorCode != "authentication_canceled")
{
await DisplayAlert("An error has occurred", "Exception message: " + ex.Message, "Dismiss");
}
}
}
Note that this isn't a complete working solution, but it does force the B2C template to load into a WebView rather than opening a browser and revealing your domain name. Put it in the shared code project.
You don't get to use your own login page, but it does at least appear to be part of your app.
Reference: Xamarin Considerations
Hopefully Microsoft will address this failing in future releases.

Consuming REST Webservice in Xamarin whilst debugging on Android phone

I am running into problems consuming a rest web service hosted on Azure in an Android Xamarin Application.
async Task ViewTimetableAsync(object sender, EventArgs e)
{
await DependencyService.Get<ITimetableService>().GetTimetableAsync(_user);
}
This is the code that is called in the XAML.cs class, it is a button event.
try
{
using (var client = new HttpClient(new NativeMessageHandler()))
{
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
}
else
{
var result = "Fail";
}
}
}
catch(Exception e)
{
var reson = e.Data;
}
This is the relevant code that is being used to call the REST web service. The code is hanging when GetAsync is called. No error is thrown, don't get any discernible information in the output.
I have included internet as a required permission in the Android manifest. I have included System.net.http as a NuGet package.
I am getting no traffic in Azure, so it is clearly not even reaching the web service. So why is this Http Client not sending the request? I am having to debug on an Android phone connected by USB using Visual Studio, and that seems to be running fine, the only issue being the inability to send out this request. The API is accessible from the phone as I brought it up in a browser, is it a setting in the phone itself that is possibly causing this?
Any help would be greatly appreciated.

Xamarin Forms - Azure login page returns not found?

I'm trying to enable my users to authenticate on Azure AD.
I have registered my app on Azure mobile center, enabled indentity and took note of the Azure auth endpoint.
I also registered my app in the Azure AD app registration as a native app.
I finally went back to my Mobile center to enable Azure AD indentity, set the client ID to be the same as the Azure application ID.
I guess things are not clear enough here for me.
I think that my implementation of the authentication in Xamarin is OK because when I click on my button on my Android project
private async void LoginClick(object sender, EventArgs e)
{
if (App.Authenticator != null)
authenticated = await App.Authenticator.AuthenticateAsync();
}
}
A new window, with the "Authenticate" title, appears. However, I can't get to my endpoint and get the message :
The webpage
https://mobile-{someNumbers}.azurewebsites.net/.auth/login/aad might
be temporarly down or it may have been moved permanently to a new web
address
My authentication method is the most basic one, from a sample :
public async Task<bool> AuthenticateAsync()
{
bool success = false;
try
{
if (user == null)
{
user = await AuthenticationManager.DefaultManager.CurrentClient.LoginAsync(this, MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory);
if (user != null)
{
System.Diagnostics.Debug.WriteLine("Authenticated !");
}
}
success = true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
return success;
}
}
Has anyone met this issue before ?
Any pointers on how to be able to log in to the AD services ?
Thank you in advance!
According to your description, I assumed that you could leverage the server-flow authentication to test that you have all the settings correct. Browser at https://yoursite.azurewebsites.net/.auth/login/aad and find that whether you could get the successful authentication page. For more detailed tutorials about AAD authentication for Azure Mobile Apps, you could refer to Adrian Hall's blog here. Additionally, for Client-managed and Server-managed authentication for Mobile Apps, you could follow this official tutorial.

How do I authenticate/validate an Android app on an App Engine Server with OAuth 2?

I have a simple application that lets a user draw pictures. There are Android, IOS, and web-based versions. I also let users store their pictures on our App-engine servers and I want them to be able to collaborate with other users. I want to use Google accounts for authentication and the basis of some permission scheme.
I do not know how to validate/authenticate a user’s Google account on android (or IOS). I am hoping somebody can help or point me in the right direction. Here is what I understand so far:
On the Web-based client, I just use Google-web toolkits UserService. However for my app-engine servlets i'm not sure what I should use. Currently the servlets have code like this:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
OAuthService oauth = null;
oauth = OAuthServiceFactory.getOAuthService();
User user = oauth.getCurrentUser();
// Do stuff
}
In my android application I think I'm supposed to do something like:
1) Get the Account from the AccountManager
2) Call:
accountManager.getAuthToken
(account, // Account
"oauth2:https://www.googleapis.com/auth/userinfo.profile",//AUTH Token Type
null, // Options
this, // Activity
new MyAccountsManagerCallBack(), // call-back
null); // Handler
This will give me authorization token.
3) ?? profit ??
This is where I am lost. Do I send this authorization token as a clear-text query parameter to my app-engine server, then make a request from the web server to the userinfo/profile api? That doesn’t seem secure.
Is there some way to make the pervious code with OAuthService work?
The samples for OAuth 2 use the Google task API, however I want to use my app-engine API. I’ve found information for OAuth 1 using cookies, webviews, title, etc, but nothing on OAuth 2, and none of them really tell me how to validate server side.
I really have no clue what I should be doing here. I would appreciate any assistance.
To clarify, this is an example of my java servlet served on app engine:
public class ServletSecureData extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
UserService usersrvc = null;
usersrvc = UserServiceFactory.getUserService();
User user = usersrvc.getCurrentUser();
if(user == null)
throw new IOException();
Random r = new Random(System.currentTimeMillis());
int num = r.nextInt(10);
PrintWriter out = response.getWriter();
out.printf("Security !! %s radioactive man! %d", user.getEmail(), num);
out.close();
}
}
This servlet was protected with a security constraint defined in the web.xml file. I wanted to be able to access this servlet using an android client. I thought that I had to used Oauth but it turned out I needed to use an older deprecated service ClientLogin
I based my implementation off the code from this site: http://blog.notdot.net/2010/05/Authenticating-against-App-Engine-from-an-Android-app

Webservice credentials - OpenID/Android AccountManager?

I'm building a webservice and would like to use the user's google account credentials.
The service runs on GAE and will have a web client and an Android native client.
This is my first attempt of something like this and I've been reading about OpenID and the Android AccountManager library.
I'm still not sure what are my options in terms of storing the users in my Datastore. What Identifier should I use ? Is it possible to use OpenID on a native Android application ?
Any help and/or pointers would be appreciated. Thanks.
We had a similar requirements on the last project: GAE backend with GWT frontend and Android/iPhone clients. Also, we did not want to store user credentials.
So we choose to use OpenID, which is unfortunately a Web standard and does not play well with mobile devices, but is doable.
On the GAE side we simply enabled federated login which gave us OpenID.
On mobile devices, when user needs to login we present to them a list op OpenID authenticators (Google, Yahoo, etc..). Then we open a native browser (not embedded browser) and direct user to chosen OpenID authentication site. The upside is that user's browser usually already has username/pass remembered, so this step just requires user to press one button.
This is all pretty straightforward. Now here is the tricky part:
After user confirms login, OpenID redirects back to our GAE return url (you need to provide this url when request is made). On this url we create a custom URL, for example:
yourappname://usrname#XXXYYYZZZ
where XXXYYYZZZZ is auth token. We get this token from the return page where it's stored as an ACSID cookie: we used some JSP to read this cookie and wrap it into above custom URL.
Then we register our Android and iPhone apps to handle the yourappname:// URLs, so that when user cliskc this link, our app is invoked and the link is passed to it. We extract user name and token from this link and we use it in REST requests to the GAE backend.
If you have any more questions I'd gladly update this post.
Update:
The user session cookie on production AppEngine is named ACSID, while on development AppEngine server it's named dev_appserver_login.
I spent about a week to find a suitable and modern looking way for this - without web browser and by using android account manager.
If you would like to use Google account and AccountManager to identify the user you can:
Get his token to Google Contacts (auth token type is "cp") through AccountManager on background thread:
public String getUserToken(Activity activity)
{
AccountManager accountManager = AccountManager.get(activity);
AccountManagerFuture<Bundle> amf = accountManager.getAuthTokenByFeatures("com.google", "cp", null, activity, Bundle.EMPTY, Bundle.EMPTY, null, null );
Bundle bundle = null;
try {
bundle = amf.getResult();
String name = (String) bundle.get(AccountManager.KEY_ACCOUNT_NAME);
String type = (String) bundle.get(AccountManager.KEY_ACCOUNT_TYPE);
String token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
return token;
} catch (OperationCanceledException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (AuthenticatorException e) {
e.printStackTrace();
}
return null;
}
Pass received UserToken to the server over secured channel.
Validate the token at the server by google using gdata library (Google Data API library):
public String getUserId(String token)
{
ContactsService contactsService = new ContactsService("Taxi");
contactsService.setUserToken(token);
IFeed feed = null;
try {
feed = contactsService.getFeed(new URL("https://www.google.com/m8/feeds/contacts/default/full?max-results=10000"), ContactFeed.class);
} catch (IOException e) {
e.printStackTrace();
} catch (ServiceException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
if (feed == null)
return null;
String externalId = feed.getId();
IPerson person = feed.getAuthors().get(0);
String email = person.getEmail();
String name = person.getName();
String nameLang = person.getNameLang();
return externalId;
}
Google token can expire (usually after an hour), so if you failed to validate the token at the server, you must send response back to client, invalidate the token and get a new one. Use account manager to invalidate the token:
public void invalidateUserToken(Context context, String token)
{
AccountManager accountManager = AccountManager.get(context);
accountManager.invalidateAuthToken("com.google", token);
}
I think this blog post does exactly what you want. It worked for me. Both of the solutions posted here are viable and clever, but I think this does it exactly how the asker was asking.
Essentially, you're just getting an authToken using the "ah" scope, and passing it to the right webpage to get the ACSID cookie that will let you access any AppEngine page that uses UserService for authentication.
http://developer.android.com/search.html#q=AccountManager&t=0
http://developer.android.com/resources/samples/SampleSyncAdapter/index.html
at the bottom of this page you will find all needed code
best regards

Categories

Resources