Our hybrid application uses our corporate Google accounts for sign in. That in-turn uses Active Directory Foundation Services to authenticate our corporate accounts. Everything works nicely until sign-out. The ADFS server holds the user credentials in a cookie or session on the server. In iOS, I can completely invalidate everything by re-creating the WKWebView and providing it with a new fresh WKProcessPool, which then requires the user to re-sign in (required for shared devices)...
let config = WKWebViewConfiguration()
let pool = WKProcessPool()
config.processPool = pool
let webView - WKWebView(frame: UIScreen.mainScreen().bounds, configuration: config)
Is there anything equivalent to that with XWalkView on Android? I am currently programmatically removing the old XWalkView and adding a new XWalkView to the main view when a logout occurs, calling clearCache, and using the cookie manager to remove all cookies, but I'm still logged into the ADFS server.
// Remove old xWalkView if it exists
if(xWalkView != null){
((ViewGroup) xWalkView.parent()).removeView(xWalkView);
xWalkView.onDestroy();
xWalkView = null;
}
// Create a new xWalkView
xWalkView = new xWalkView(this, this);
xWalkView.clearCache(true);
XWalkCookieManager cookieManager = new XWalkCookieManager();
cookieManager.removeAllCookie();
cookieManager.flushCookieStore();
// Add to the main view
RelativeLayout layout = (RelaviteLayout)findViewById(R.id.layoutMain);
layout.addView(xWalkView, 0);
Any help would be greatly appreciated.
Related
I have a Xamarin.Forms 5.0 application (for both iOS and Android). I am not sure if this is a Xamarin issue or just Android. On iOS it is working fine.
In the app I use a webview to display single page webapplication. Inside that spa I have a login page with a "remember me" checkbox. When this is checked, the backend creates persistent cookie instead of a sessioncookie. The login is done with a XHR request to the backend.
Seems all working fine, but when the app is restarted, it doesn't know the cookie anymore and the user has to login again.
When I do a full reload of the page in the webview (after login), it looks like the cookie is persisted. After restarting the app, the user is logged in automatically, so the cookie is available.
So the problem seems to be that new cookies in the response of XHR requests are not persisted, while the response cookies of a normal page request are.
Anybody any ideas about this?
Added some code
I created a simple Xamarin.Forms project with an Android app.
Added this in the MainPage.xaml:
<StackLayout>
<Button Clicked="Button_Clicked" Text="(Re)Load"></Button>
<WebView x:Name="wv"
WidthRequest="1000"
HeightRequest="1000"
></WebView>
</StackLayout>
And in the codebehind:
private void Button_Clicked(object sender, EventArgs e)
{
wv.Source = "https://---.---.nl/portal";
}
This loads a SPA webapplication. Not much code to show.
As i know, UWP and iOS would share their cookie containers automatically between the WebView and native http client, however Android does not.
The WebView could use CookieManager to handle cookies. If you want to share cookies between the two, you will have to manually process the information. You need to know the Uri of the domain for these cookies.
private void CopyCookies(HttpResponseMessage result, Uri uri)
{
foreach (var header in result.Headers)
if (header.Key.ToLower() == "set-cookie")
foreach (var value in header.Value)
_cookieManager.SetCookie($"{uri.Scheme}://{uri.Host}", value);
foreach (var cookie in GetAllCookies(_cookieContainer))
_cookieManager.SetCookie(cookie.Domain, cookie.ToString());
}
public void ReverseCopyCookies(Uri uri)
{
var cookie = _cookieManager.GetCookie($"{uri.Scheme}://{uri.Host}");
if (!string.IsNullOrEmpty(cookie))
_cookieContainer.SetCookies(new Uri($"{uri.Scheme}://{uri.Host}"), cookie);
}
It is not very clear, at least for me, when the WebView flushes the cookies and when not.
The simple solution for me was to flush the cookies everytime the onPause lifecycle event of the activity containing the webview is called. This occurs when another activity is started, or the user switches to another app or closes the app.
protected override void OnPause()
{
base.OnPause();
Android.Webkit.CookieManager.Instance.Flush();
}
I am trying to implement a custom auth flow using Amazon Cognito and lambda scripts. The custom authentication is triggered but the request session array is empty instead of triggering the SRP_a challenge like it does using the javascript sdk. If the SRP_a challenge does not happen, it does not perform the PASSWORD_VERIFIER challenge which is necessary for the username/password being used to authenticate. Note: the custom portion of the authentication happens after the username/password auth happens.
Cognito's documentation says that this flow is possible on javascript, iOS and Android but only provides examples in javascript. Below I have included the the Android code that is doing the authentication.
CognitoHelper cognitoHelper = CognitoHelper.getInstance(getApplicationContext());
username = loginView.getUsername();
//Must use toLowerCase to make the username not case sensitive
cognitoHelper.setUser(username.toLowerCase());
password = loginView.getPassword();
AuthFlowType authFlowType = AuthFlowType.fromValue(String.valueOf(AuthFlowType.CUSTOM_AUTH));
HashMap<String, String> authenticationParameters = new HashMap<>();
authenticationParameters.put(CognitoServiceConstants.AUTH_PARAM_PASSWORD,password);
authenticationParameters.put(CognitoServiceConstants.AUTH_PARAM_USERNAME, username);
SRP_aHelper srp_aHelper = new SRP_aHelper(cognitoHelper.getUserPoolID());
authenticationParameters.put(CognitoServiceConstants.AUTH_PARAM_SRP_A,srp_aHelper.getA().toString(16));
InitiateAuthRequest initiateAuthRequest = new InitiateAuthRequest();
initiateAuthRequest.setClientId(cognitoHelper.getClientID());
initiateAuthRequest.setAuthFlow(authFlowType);
initiateAuthRequest.setAuthParameters(authenticationParameters);
CognitoUser cognitoUser = cognitoHelper.getUserPool().getUser(username);
thisDevice = cognitoUser.thisDevice();
//getCIPClient returns a AmazonCognitoIdentityProviderClient
InitiateAuthResult initiateAuthResult = cognitoHelper.getCipClient().initiateAuth(initiateAuthRequest);
im working on an android app with some server-side business logic. Im using android studio and im creating that kind of app for a first time.
I am trying to use server-side application to login to a different system and return me a cookie, so my android application can tell, whether the set credentials are correct.
Here's my endpoint provided method.
/** Returns user with cookie set either to null or actual cookie from AIS */
#ApiMethod(name = "login")
public User login(#Named("loginName") String name, #Named("password") String password) {
AISCommunicator aisCommunicator = new AISCommunicator();
String cookieVal = aisCommunicator.login(password,name);
User user = new User();
user.setCookie(cookieVal);
//user.setCookie("asdasdasd");
return user;
}
AISCommunicator is a serverside bean. At the moment it's part of a code
CookieManager manager = new CookieManager();
manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
CookieHandler.setDefault(manager);
is marked as incorrect by Android studio, which tells me, that to use it, i need to declare minSdk level 9, while currently i have 1. How can i do that? I have set minSdk in my client's app, but it seems like it does not influence the serverside bean.
Anyway, the code is still runnable for some reason and the endpoint Bean returns 404 not found error at the moment.
Ignore Android Studio's error. This is one of its known and unfixed bugs.
I need to make Crosswalk on Android (in a Cordova project) use some
cookies I gathered via an Apache HttpClient.
However I'm not sure how to achieve this. I tried experimenting with
the XWalkCookieManager but I think everything I do with the
XWalkCookieManager is ignored. Where would I initialize the cookie
manager? Is this even supported yet?
//cookieStore contains the cookies I got via a request from the Apache
// HttpClient.
List<Cookie> cookies = cookieStore.getCookies();
for (int i = 0; i < cookies.size(); i++) {
Cookie cookie = cookies.get(i);
String cookieString = buildCookieStringFromCookie(cookie);
// This is a XWalkCookieManager I initialized earlier but it doesn't do anything
//as far as I can tell.
cookieManager.setCookie(cookie.getDomain(),cookieString);
}
I would really appreciate any help, this is the only thing that
currently keeps me from using Crosswalk successfully.
I have also tried using the standard Android cookie Manager via CookieManager.getInstance().setCookie(cookie.getDomain(), cookieString); but this seems to be ignored by Crosswalk as well.
Best,
Andreas
//Edit for future reference:
The issue was that Crosswalk expects the cookie url to start with https:// and the native Android webview doesn't (or the other way around, I'm not sure anymore). Now what is working fine is to set the cookie twice, once without https and once with https:
mCookieManager.setCookie(cookie.getDomain(), cookieString);
mCookieManager.setCookie("https://" + cookie.getDomain(), cookieString);
You can create an instance of XWalkCookieManager and set the differents cookies in it.
It seems to be shared accross the XwalkViews.
// Your can use this inside the onCreate() method
private XWalkCookieManager mCookieManager;
mCookieManager = new XWalkCookieManager();
mCookieManager.setAcceptCookie(true);
mCookieManager.setAcceptFileSchemeCookies(true);
// Pass it to your request executor
httpRequestExecutor.setXWalkCookieManager(mCookieManager);
//You're now able to add your cookies to this manager rather than to your cookieManager
How we extract the cookies from the HttpResponse :
responseHeaders = response.getAllHeaders();
for (int i = 0; i < responseHeaders.length; i++)
{
Header header = responseHeaders[i];
if (header.getName().equalsIgnoreCase("set-cookie"))
{
mCookieManager.setCookie(request.getUrl(), header.getValue());
}
}
The implementation of XWalkCooikeManager is a little different with Android CookieManager.
You should add schemes(http|https) before cookie.getDomain().
This is because the implementation of CookieManager in Android WebView use WebAddress(url).toString(), which will add schemes (http|https) into the url.
As the explanation in CooieManagerAdapter:
WebAddress is a private API in the android framework and a "quirk" of the Classic WebView implementation that allowed embedders to be relaxed about what URLs they passed into the CookieManager, so we do the same normalisation before entering the chromium stack.
Authentication and app engine, there is a lot to be read about it, but a lot seems to be outdated!
Even the google pages https://developers.google.com/appengine/docs/java/endpoints/consume_android#making-authenticated-calls
Here, they talk about 'GoogleAccountCredential.usingAudience', but nowadays, you should use GoogleAuthUtil (as far as I know, please correct me if I'm wrong).
I am trying to set up an app engine as a backend to my Android app (and in future, my iOS app).
I am using Android Studio, used the 'new module' and chose app engine with cloud messaging there.
I created a simple endpoint, and have a function there, here is some code:
public class ReviewEndpoint {
// Make sure to add this endpoint to your web.xml file if this is a web application.
private static final Logger LOG = Logger.getLogger(ReviewEndpoint.class.getName());
/**
* This method gets the <code>Review</code> object associated with the specified <code>id</code>.
* #param id The id of the object to be returned.
* #return The <code>Review</code> associated with <code>id</code>.
*/
#ApiMethod(name = "getReview")
public Review getReview(#Named("id") Long id) {
// Implement this function
Review r = new Review();
r.setData("test!");
As you can see, this is nicely generated by Android Studio. I implemented some stuf like creating the 'review' object and return it at the end.
On the Android side, I can do this:
ReviewEndpoint.Builder b = new ReviewEndpoint.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null);
ReviewEndpoint ep = b.build();
Review review = ep.getReview(1L).execute();
data = review.getData();
and yes, I get 'test!' :)
Now, I want to have this authenticated. I want to know which user wrote what, so I thought I am going to use GMail account and Facebook later.
Here I'm stuck. I am able to get a token from the user on Android:
token = GoogleAuthUtil.getToken(MainScreenActivity.this, mAccount.name, "oauth2:https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/userinfo.profile");
then you are able to add this token as credential to the request:
Credential cr = new Credential(BearerToken.authorizationHeaderAccessMethod()).setAccessToken(token);
ReviewEndpoint.Builder b = new ReviewEndpoint.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), cr);
Then in the app engine I tried to get the user info, but how?
Will it be supplied as 'bearer'? How do I get this bearer token? Should I then do API request to get the data on the server?
this does not work:
OAuthService service = OAuthServiceFactory.getOAuthService();
try {
User user = service.getCurrentUser();
can anyone give me a heads up?
So finally, today, I found out how to do it! I had questions on Stackoverflow on this before and never had an answer, but these to sites gave me the answer:
https://developers.google.com/appengine/docs/java/endpoints/auth
https://developers.google.com/appengine/docs/java/endpoints/consume_android
The first shows what needs to be done on the app engine side. The second page will tell you how to get the credentials. I was quite close. I am not sure if the adjusting of the build.gradle file mentioned in the second link is necessary. What I added to the App Engine:
#Api(name = "reviewEndpoint", version = "v1", ...<<some more stuff here >>
scopes = {Constants.EMAIL_SCOPE},
clientIds = {Constants.WEB_CLIENT_ID, Constants.ANDROID_CLIENT_ID},
audiences = {Constants.ANDROID_AUDIENCE})
and then get the credentials:
// Initialize the scope using the client ID you got from the Console.
final String scope = "server:client_id:" + Constants.WEB_CLIENT_ID;
credential = GoogleAccountCredential.usingAudience(activity,scope);
You have to add the e-mail address of the user:
credential.setSelectedAccountName("some-mail-address#gmail.com");
you can get the e-mail address using the account picker (also example shown when you follow the link)
and next. you do a call to the endpoint, using the credential, I think Play Services will validate the user, because if I use an e-mail that is not logged in on the device, it will not work. The following code will throw an GoogleAuthIOException :
ReviewEndpoint.Builder b = new ReviewEndpoint.Builder(
AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), id_token);
ReviewEndpoint ep = b.build();
Review review;
review = ep.getReview(1L).execute();
for testing, I've put the e-mail address I get at the server side as a string in the review object, and there it gave me the e-mail address instead of the user object being null. Ow! I forgot to tell you, you need a user argument on the app engine side. Even though you do not see the 'user' argument in the 'getReview' call above, it will be added by App Engine.
So this is how my getReview looks now:
#ApiMethod(name = "getReview")
public Review getReview(#Named("id") Long id, User user) {
// Implement this function
Review r = new Review();
r.setData("user == " + (user == null ? "NULL " : user.toString()));
Hope this will help someone