Issues with ASP.NET Forms Authentication on Phonegap (Android) - android

I have an ASP.NET MVC/Web API backend where I have implemented a Forms Authentication for my Phonegap app. The login is executed by sending the users credentials via jQuery Ajax call like this:
$.ajax({
type: "POST",
url: "/api/authentication/login",
data: JSON.stringify({ Username: username, Password: password }),
contentType: "application/json; charset=utf-8",
dataType: "TEXT",
statusCode: {
200: function (response, status, xhr) {
// successfully authenticated
Backbone.history.navigate("/", { trigger: true });
}
}
});
The backends login method looks like this:
[ActionName("login")]
[AllowAnonymous]
public LoginResult Login(LoginCredentials credentials)
{
// doing all kinds of things here
// if valid credentials
FormsAuthentication.SetAuthCookie(loginID, true);
return loginResult;
}
I have this in my Web.config:
<authentication mode="Forms">
<forms
name=".ASPXAUTH"
loginUrl="/login"
defaultUrl="/home"
protection="All"
slidingExpiration="true"
timeout="525600"
cookieless="UseCookies"
enableCrossAppRedirects="false"
requireSSL="true"
>
</forms>
</authentication>
Now the problem with Android here is that the cookie is properly set and it does work on my authorized methods after the login, but sometimes (often) when I close the app and open it again, I'm no longer logged in. The cookie isn't there anymore, I can not see it in the request. This should not happen because I have set the timeout to 525600. I have noticed that this problem often occurs when I close the app immediately after login. In other hand if I log out and then log in without closing the app, the cookie is saved properly.
But, if I get the cookie to stick, most of the time the logout behaves strangely as well. This is how I do the logout request:
$.ajax({
type: "POST",
url: "/api/authentication/logout",
data: "{}",
contentType: "application/json; charset=utf-8",
dataType: "text"
success: function (response) {
// successfully logged out
Backbone.history.navigate("api/login", { trigger: true });
}
});
The backend:
[ActionName("logout")]
[AllowAnonymous]
public String Logout()
{
FormsAuthentication.SignOut();
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, "");
cookie.Expires = DateTime.Now.AddYears(-1);
HttpContext.Current.Response.Cookies.Add(cookie);
return "home";
}
Now similar to the problem with the login, the logout first seems to be successful and the cookie is no longer sent with any requests. But when I close the app and open it again, the cookie is back and I'm logged in again. I can see that the cookie has the same value as the one I thought I just removed by setting its expiration time to the past.
I have tried all kinds of tricks, like:
extra reloads after the login/logout (location.reload())
executing the logout/login request multiple times
executing request to other methods after the login/logout
1-10 second timeout between the login/logout request and the reload
all kinds of variations of the above
The authentication works as intended on iOS and Windows Phone. The problem occurs only on Android (tested on KitKat and Lollipop). No problem on the Android emulator, but on real devices and Visual Studios Android emulator this happens all the time.
I don't know in which direction to go from here. Is there something in the Android WebView that could cause this kind of behavior? Is there something else I could test out? Please help!
I'm more than happy to give more information if needed.
EDIT:
Inspired by Fabian's comment, I changed the logout method to this:
FormsAuthentication.SignOut();
HttpCookie cookie = HttpContext.Current.Response.Cookies[FormsAuthentication.FormsCookieName];
cookie.Expires = DateTime.Now.AddYears(-1);
HttpContext.Current.Response.Cookies.Clear();
HttpContext.Current.Response.Cookies.Add(cookie);
return "home";
Instead of creating a new cookie, I used the one in the response. It did not work.
I also tried something I found from here: http://techblog.dorogin.com/2013/01/formsauthentication-gotcha-with-signout.html That also did no difference, the path was not the problem. Still looking for a solution.
ANOTHER EDIT:
Still not able to find a solution for this. I had to make a horrible workaround.
Login: I make two reloads after the login and then a request to
a dummy method. This seems to work every time.
Logout: I use a flag placed in localStorage to determine if the user has logged out and perform a logout in the startup. This always removes the cookie correctly.
I'm not happy with these hacks and I'm still hoping for a better solution.

PhoneGap loads files from file:// protocol. Unfortunately, cross origin requests are not allowed and unless you open cross origin requests from all hosts *, this problem will not resolve.
There are multiple ways this can be fixed but they are really long.
Load Html from http://
Load entire website from web server instead of local storage. This removes all issues with cross origin requests. Benefit is you don't need to publish new version of app when you change UI. But you will have to implement very powerful caching and first time opening app will take longer time.
Intercept http:// and deliver local files
As you know, phonegap simply uses WebView, in all platforms, you can simply override Url protocol to inject files from your app's local storage. This will be faster, and browser will think that it is loading html from same resource.
Setup OAuth + custom header for authentication
Redirect to a login page hosted at your website say http://domain.com/api/login
After successful login, use PhoneGap localStorage (not browser's localStorage) to store authorization.
Navigate to your local html pages from app and for each json api request you send to server, send authorization header as separate header in ajax request.
Setup a Authorization module, where you can manually authorize asp.net request if your authorization was sent through custom header in http request

I believe I have found the solution. The Phonegap version on your config.xml file is cli-5.1.1, which includes Android Phonegap version 4.0.2 according to the documentation.
The problem with the versions is it seems the Android Phonegap team eventually fixed the cookie storage problem on version 5.2.0. It can be found in release notes as:
CB-10896 We never enabled cookies on the WebView proper
Therefore, updating your Phonegap to latest version should solve the problem.

According to MSDN:
The FormsAuthentication.SignOut method removes the
forms-authentication ticket information from the cookie.
And that's all you need to log the user out. You don't need to expire or remove your cookie itself. Simply change your Logout() to:
[ActionName("logout")]
[AllowAnonymous]
public String Logout()
{
FormsAuthentication.SignOut();
return "home";
}

Related

How do I get http content from a https website?

I get "Http failure response for https://www.google.com: 0 Unknown Error" when I request from Secured url.
I am trying to test my ionic/angular mobile app. When I tried with http requests I had problem with Android 9, but with Android 7 was working fine. Anyhow I need to set my backend to public https server. So now I'm testing with https request and none of 7 and 9 Android versions works.
I am using Angular 7 ,
"#ionic/angular": "^4.6.1",
"#ionic-native/core": "^5.0.0",
"rxjs": "~6.5.1"
I made these small functions in order to make my problem simpler.
inside my html file i have this code:
myFile.html
<ion-button
(click)="onStartTest()"
>Click me</ion-button>
<p id="testme"></p>
myFile.page.ts
onStartTest() {
this.taskService.onTest().subscribe(result => {
document.getElementById('testme').innerText = 'result ' + result;
console.log(result);
}, error => {
document.getElementById('testme').innerText = error.message;
console.log('Problem ', error.message);
});
}
myTask.service.ts
onTest() {
return this.http.get('https://www.google.com').pipe(
catchError(err => {
return throwError(err);
})
);
}
At first I tried my server's URL but I changed it to "https://www.google.com" just to verify that the backend is correct.
Also I have an interceptors.ts file that I am using it for authentication, but I am not logged in when I execute the onStartTest() function, but im gonna share it anw.
interceptors.ts
import {Injectable} from '#angular/core';
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '#angular/common/http';
import {Observable} from 'rxjs';
#Injectable()
export class TokenInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = localStorage.getItem('auth_token');
let newHeaders = req.headers;
if (token) {
console.log(token);
newHeaders = newHeaders.set('Authorization', 'Token ' + token);
const modified = req.clone({
headers: newHeaders
});
return next.handle(modified);
} else {
newHeaders = newHeaders.set('Content-Type', 'application/json');
const modified = req.clone({
headers: newHeaders
});
return next.handle(modified);
}
}
}
I think these are the necessary files to share for this problem.
I also tested the url of google with Postman just to be sure that I should get a status 200
I am also aware that there is an "add_header" directive (nginx) that adds 'Allow-access-control-origin' when the response code is 20x or 30x. According to my screenshot with Postman, google is responding with 200 status, but my app still gets status 0 error.
Ignore the first Error. It's a function I use with http when the app begins. Right now im testing https.
I tried superficially to use ionic-native library HTTP but my app totally crashed.
I also execute the command ionic serve --ssl but still nothing.
I read somewhere that for secured connection I need a certificate, but I understood that this is a server's work.
I tried to request from Dark Sky from Vanilla JavaScript and it works fine. So there is something wrong with angular/ionic side and not server's.
What am I missing? I really need to fix this problem soon!
I want to send a secured request to an https url and get the appropriate response.
Your main problem is that you are trying make an API to an unsecure call (http) location (http://192....../mobile/tasks) from a secure origin (https://localhost:8100).
This is clearly indicated in your error message and this is not allowed, and has been answered before
Your second problem is that, for testing purposes, you are trying to call a 3rd party https ressource from your website. This only works if the 3rd party ressource implement CORS, which is not the case for Google and api.darksky.net. Sending a GET request with Postman is useless, as Postman will not check for CORS headers before displaying the response. If you want to use Postman to check CORS, send an OPTIONS request to these ressources and you'll see that there are no CORS headers
So the answer is in MDN - CORS
For security reasons, browsers restrict cross-origin HTTP requests
initiated from scripts. For example, XMLHttpRequest and the Fetch API
follow the same-origin policy. This means that a web application using
those APIs can only request resources from the same origin the
application was loaded from, unless the response from other origins
includes the right CORS headers.
This means the back-end I was using needed some more configuration since I was using 'same-origin' policy script. I thought we had it because when we tried from the browser's console to fetch the request it was working fine, but on mobile it wasn't. We had a custom CORS configuration but we changed it to the django-cors-headers. Since we switched to django-cors-headers I could get correctly the response from HTTP and HTTPs requests.
The other answer and comments were really useful to focus to the right direction.

Ionic Cordova web app cookies not being stored on Android or iOS

My company has built a medium size Ionic application and now we're trying to make available natively for Android and iOS and I'm having an issue with persisting cookies.
When running the application Safari there are no issues at all, cookies persist just fine, but when building for devices, the cookies are not being persisted.
The way things work now is when a user logs in, a cookie is returned from the server. After this, the cookie should be sent with every subsequent request, ensuring that the user is still logged in. When I look at the initial response from the server after login, the 'set-cookie' header is there. However, all requests afterward don't contain the cookie, so the server returns a response of not logged in. This only happens when running natively on iOS or Android.
When I debug to see if the cookies are being persisted, I see that there are no cookies.
I know there was a similar issue with iOS 13, but I'm not using iOS 13, I'm using iOS 12. And my problem is on both Android and iOS. When doing Google searches, all the workarounds I see are for problems that are not the same as the one I'm having, and I've tried those workarounds anyways, and they don't work.
Has anyone experienced this issue before? Any ideas on what this might be and how to fix it would be greatly appreciated, I haven't gotten anywhere and I've been searching for an answer for days now.
Also too, because the cookie is HttpOnly I'm not able to access using TypeScript, so I can't persist the cookie manually without sending a non-HttpOnly cookie. Which isn't really an option at the moment.
i had some troubles with cookies and cordova too .
In my case, the plugin cordova-plugin-cartegraph-cookie-master helped me to get and set cookies.
Source Plugin:
https://www.npmjs.com/package/cordova-plugin-cartegraph-cookie-master
Install :
cordova plugin add cordova-plugin-cartegraph-cookie-master
Usage :
Get cookie value :
cookieMaster.getCookieValue('http://<some host>:<some port>', '<cookie name>', function(data) {
console.log(data.cookieValue);
}, function(error) {
if (error) {
console.log('error: ' + error);
}
});
Set cookie value :
cookieMaster.setCookieValue('http://<some host>:<some port>', '<cookie name>', '<cookie value>',
function() {
console.log('A cookie has been set');
},
function(error) {
console.log('Error setting cookie: '+error);
});

Symfony 2 api logout

I have an android application that uses a symfony based api. I have users that login through the phone, send requests to the server and then logout. The problem is that unless I delete the cookies in the android(which I am storing) the symfony server would not log out the user, despite the fact that the user goes to the logout route.
This is part of my security.yml file (And I have tried a lot of combinations with this file to no avail)
firewalls:
api:
anonymous: ~
provider: users
access_denied_url: /user/accessDenied
pattern: ^/api/user
form_login:
login_path: /api/user/login
success_handler: Authentication_Handler
failure_handler: Failure_Handler
check_path: /api/user/login_check
remember_me: false
logout:
path: /api/user/logoutuser
success_handler: logout_handler
target: /
I have also tried triggering controllers manually that call $this->get('session')->invalidate(); and or that redirect to the logout path(some results around the web suggested that was a good idea, but it did not work) It just seems that, whenever cookies exist, the symfony server just logs the user in, that seems like a security issue to me considering that I am saving the cookies into shared preferences on android. Please help
I believe I have located the problem. On logout the cookie headers are not sent, so the server has no idea whose session to invalidate. Making the android httpclient behave like a full fledged browser can be tricky...

Cookies wont persist after phonegap app is closed

i'm having a little problem with my phonegapp app on Android with python-django as backend. When i close the app i lost all the cookies that the server sets after the authentication (sessionid and csrftoken).
So, every time i open the app i have to do the login procedure. I try to save the sessionid in localStorage and then create my own header but this is not allowed by XMLHttpRequest specification. I need a simple workout for doing something like this:
function get_csrf_token(){
$.ajax({
url:"http://www.mywebsite.com/token",
type:"GET",
dataType:"text",
beforeSend: function(request) {
request.setRequestHeader("Cookie", 'sessionid=' + window.localStorage["sessid"]);
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert(JSON.stringify(XMLHttpRequest));
alert(JSON.stringify(textStatus));
alert(JSON.stringify(errorThrown));
},
success: function (token) {
window.localStorage["token"] = token;
return token;
}
});
}
Is this possibile with phonegap or i have to login every time i start the app?
You really aught to be using a different authentication method when dealing with mobile. It's a lot easier to try token-based authentication. Essentially you have a form on your mobile app that takes their username/password and pass it to a view you design that ensures validity and then responds with a token that the app can include in all subsequent calls.
You can implement this in Django fairly easy with a custom authentication backend, or you can use Django Rest Framework http://www.django-rest-framework.org/api-guide/authentication#tokenauthentication that has it built in.
What you're doing is definitely possible. It's impossible to set cookies on an XMLHttpRequest object. But if you set the cookie using document.cookie first, it will be sent on the cookie header for every subsequent request.
function get_csrf_token(){
document.cookie = 'sessionid=' + window.localStorage["sessid"];
$.ajax({
url:"http://www.mywebsite.com/token",
type:"GET",
dataType:"text",
...
});
}
The only thing you are going to want to watch out for while doing this is the possibility of an XSS attack. By storing the session id in localstorage, if your app were to have an XSS vulnerability, an attacker could possibly get the localstorage session id and send it to their server using JSONP or an image tag etc. Since you are using phonegap, you can mitigate this problem by using a whitelist policy that will only allow requests to go to your known servers.

Facebook oauth, FB.getLoginStatus & FB.logout on IE, Chrome, Firefox & Android browsers

I'm using FB's oauth to successfully authenticate users, but I'm having trouble reliably logging them out using what seems to be the recommended approach: FB.logout using the FB JDK.
Specifically, calling FB.logout (after loading the FB JDK and calling FB.init) successfully revokes the FB authentication granted during oauth if I'm using Chrome or Firefox. However, it does not seems to work in IE or in the Android browser. When I say it doesn't work in IE and Android browser, what I mean is that after calling FB.logout subsequent redirects to www.facebook.com/dialog/oauth.. load my callback page without prompting the user to enter credentials again. In Chrome and Firefox, they are 'correctly' prompted to do so at that point.
My various attempts to debug has led me to realize that calls to FB.getLoginStatus (even after calling FB.logout) will pass a valid response.session object to its callback in every browser. So, according to FB.getLoginStatus, the session is still active in every browser even after FB.logout. This makes me wonder my FB.logout works at all.
I know that oauth and the FB JDK are two different things and are not necessarily meant to play together nicely but I haven't seen any other alternatives to ending an FB oauth session, aside from calling FB.logout.
Anybody else seen these irregularities or have a solution? Thanks.
I found this answer after I posted the above:
FB.logout not working in IE8
It recommends putting the redirect after calling FB.logout in a setTimeout function. Basically, give the FB.logout function about 2000 ms to finish.
I tried it, and it fixed the problem in both IE and the Android browser. Basically, Chrome and Firefox have a fast enough JavaScript engine that FB.logout will finish executing before the new page loads in the browser, while IE and Android browser do not.
Following that logic, I realized that there is a reason FB.logout has a callback function and it is probably safer to use that to do any redirects (as opposed to playing with a setTimeout delay).
function mysignout()
{
FB.logout(function()
{
top.location.href = '../mobile.php'
});
}
Out of curiosity, I tested to see what is the timing difference on executing the callback in Chrome vs. IE. Chrome took 2511 ms to complete the logout IE took 3517 ms. I think what confused me is that I figured FB.logout just deleted the cookie locally and would take no time at all. Instead, it looks like it is some kind of ajax call to revoke authentication on the server and it takes a considerable bit of time.
Michael
Using your method you need to append a user access_token to your logout link. OR
Using the Login buttons auto logout link feature. autologoutlink='true'
<div id="fb-root"></div>
<script>
window.fbAsyncInit = function() {
FB.init({
appId : '135669679827333',
status : true, // check login status
cookie : true, // enable cookies to allow the server to access the session
xfbml : true, // parse XFBML
//channelUrl : 'http://WWW.MYDOMAIN.COM/channel.html', // channel.html file
oauth : true // enable OAuth 2.0
});
// redirect user on login
FB.Event.subscribe('auth.login', function(response) {
top.location.href = 'http://example.com/loggedin/';
});
// redirect user on logout.
FB.Event.subscribe('auth.logout', function(response) {
top.location.href = "http://example.com/loggedout/";
});
};
(function() {
var e = document.createElement('script'); e.async = true;
e.src = document.location.protocol +
'//connect.facebook.net/en_US/all.js';
document.getElementById('fb-root').appendChild(e);
}());
</script>
<fb:login-button autologoutlink='true' scope='email,publish_stream'></fb:login-button>
If you don't want to redirect the user after logout, or if you want to get sure, that the FB Session is clear on the client side, you can cleanup manually with these three commands:
FB._authResponse = null;
FB._userStatus = null;
document.cookie = 'fbsr_' + FB._apiKey + '=;'; // clear the facebook cookie
They can even be executed without FB.logout(), but in this case, the user is not logged out from facebook, so after a refresh, he will be logged in again.

Categories

Resources