AppAuth-Android logout and end session - android

Apparently the IOS version of this library AppAuth supports logging out using the end_session_endpoint but with the android version you are forced to implement one or find an alternative way of logging the user out.
Currently using the kotlin version of the library
on authorisationRequestBuilder if setPrompt method is not called, then during login you get the KMSI (keep me signed in) checkbox option.
Invalidating authState, persisted tokens and all other resources will not have an effect because hitting the authorise endpoint once again will automatically issue another token. This is because technically they are still logged in the server.
Using only the end_session_endpoint and no postLogoutRedirectUri provided. Is it possible to successfully log the user out and automatically redirect the user by closing the open chrome custom tabs immediately after the endpoint has been a success?
Currently calling the end_session_endpoint using AppAuth will successfully log the user out (You can see this in the open chrome custom tabs) but doesn't redirect or close the custom tabs.
How can this be achieved. I have configured the manifest with LogoutRedirectUriReceiverActivity but not sure how to initiate this. Other examples rely on the postLogoutRedirectUri being present.
Currently postLogoutRedirectUri doesn't exist on the discoveryDoc.

using IdentityServer4 you can include the idToken in the end session request. This way the user is logged out automatically without consent screens and such. Consequently you do not need a redirect after logout.
Your request would look like this:
GET /connect/endsession?id_token_hint=
source: https://identityserver4.readthedocs.io/en/latest/endpoints/endsession.html

Is it clear that the end_session_endpoint is a call to the server to logout? This sample code includes a callback (after end_session_endpoint) that removes any auth tokens stored within the app.
/*
* Do an OpenID Connect end session redirect and remove the SSO cookie
*/
fun getEndSessionRedirectIntent(metadata: AuthorizationServiceConfiguration,
idToken: String?): Intent {
val extraParams = mutableMapOf<String, String>()
val request = EndSessionRequest.Builder(metadata)
.setIdTokenHint(idToken)
.setPostLogoutRedirectUri(this.config.getPostLogoutRedirectUri())
.setAdditionalParameters(extraParams)
.build()
return authService.getEndSessionRequestIntent(request)
}

Related

Facebook login from react native app with symfony server

I am trying to set up a proper facebook login in my react native app. For now I have it working in a pure webview with oauth login to my server but I want to use the native facebook login to be able to take advantage of the facebook app.
I am using the following libraries:
Server side
https://github.com/FriendsOfSymfony/FOSUserBundle
https://github.com/hwi/HWIOAuthBundle
https://github.com/FriendsOfSymfony/FOSOAuthServerBundle
App side
https://github.com/magus/react-native-facebook-login
So my facebook login is perfectly functional on my website as well as on my app in a webview for OAuth by calling /oauth/v2/auth in a webview and handling the token.
But it is kinda messy because in the webview you have to type your full email + password combo.
So right now I'm just getting an authorization error when calling /login/facebook-check in a webview on the Login success event (by the native plugin), I could use some help to finish this.
Finally made it work. The hack was to use all the existing services to work together.
I made a custom controller. Some security checks are needed but still this works:
/**
* #Route("/api/facebook-connect/{accessToken}", name="api_facebook_connect", defaults={"_format" = "json"})
* #Rest\View()
*/
public function facebookLoginAction($accessToken, Request $request)
{
$token = new OAuthToken($accessToken);
$token->setResourceOwnerName('facebook');
$oauthUserProvider = $this->get('app.oauth.provider.user_provider');
$ressourceOwnerMap = $this->get('hwi_oauth.resource_ownermap.main');
$userChecker = new UserChecker();
$oauthProvider = new OAuthProvider($oauthUserProvider, $ressourceOwnerMap, $userChecker);
$token = $oauthProvider->authenticate($token);
$this->get('security.token_storage')->setToken($token);
$client = $this->get('doctrine.orm.entity_manager')->getRepository('AppBundle:Client')->findOneBy([], ['id' => 'DESC']);
$oauth2server = $this->get('fos_oauth_server.server');
$accessToken = $oauth2server->createAccessToken($client, $this->getUser(), 'user', 3600);
return $accessToken;
}
Will update this as I clean this up.
I'm not sure about how to properly handle the server-side part, however here are a few details about how we integrated Facebook login in one of our app:
We first started by using https://github.com/magus/react-native-facebook-login but later switched to https://github.com/facebook/react-native-fbsdk, which is maintained by Facebook and allow to access to other Facebook services (in particular, we used the Share API)
In both case (react-native-fbsdk or not), the flow was like this:
We have a Connect with Facebook button (a normal button, nothing fancy - we're not using the one provided by the modules).
When clicked, we call the Login method with the appropriate permissions. This should work out of the box, meaning that you'll have either a webview displayed (if you don't have the Facebook app) or the native Facebook app shown.
If the user declines the login to the app, nothing will happen.
If the user accepts, the app will receive the Access Token that can be used to issue calls to the Facebook API on the behalf of the user. This looks like this using react-native-fbsdk:
// This can be put in a Facebook login button component or a service,
// and should be called when the user wants to connect with Facebook
LoginManager.logInWithReadPermissions(permissions).then((result) => {
if (result.isCancelled) {
this.props.onCancel();
return;
}
AccessToken.getCurrentAccessToken().then((data) => {
this.props.onLogin(data);
});
}, (error) => {
console.warn('Facebook Error', error);
});
We then send the access token to our server which is able to fetch the profile of the user via the Facebook Graph API, creating a user account on the server if needed (i.e: if it's the first time the user log on).
// Called once we got the access token from the data in the previous
// step (this.props.onLogin(data)).
loginWithFacebook(facebookAccessToken) {
return fetch(`${apiHost}/api/login/facebook`, {
method: 'GET',
headers: {
// We pass the access token using the Authorization header:
Authorization: `Bearer ${facebookAccessToken}`,
},
}).then(() => {
// Whatever, for example get the user info returned by the server
// and store them.
});
}
On the server, we get the Access Token from the headers and use it to get the user profile (populating the account of the user for our app, for example with his avatar and name) and associate the user account with the facebook id of the user.
If the user have the Facebook app and already accepted the app, nothing will be asked to him the next time he tries to log in. You just click on the login button and get logged to the app :)
If the user don't have the Facebook app, the Facebook SDK always display a webview with the login page.
Hope this can help!

Manage server-side authentication from Android client

I have set up third party OAuth authentication on my Node.js server using passport.js.
It works well from browser. The flow is the following:
Get request to myserver.com/auth/instagram
Passport redirects me to instagram website to login
I type my credentials
I am redirected to the callback url, which is my server.com/auth/instagram/callback
I do further processing and finally res.send(myAccessToken) to the client.
I am having troubles implementing this in Android. To type third-party credential, a browser page will be needed. But then how am I going to get the final result in my app?
Option 1
Start the browser.
Intent i = new Intent(ACTION_VIEW);
i.setData(Uri.parse("https://myserver.com/auth/instagram"));
startActivity(i);
Issues:
I cannot easily set proper headers needed for my server.
I cannot programmatically read the final response! The action is given to the browser app, which goes to the end and will never notify my app. The user is shown a blank page with myAccessToken printed.
Option 2
Connect using some http library.
final Request request = createRequest()
.url("https://myserver.com/auth/instagram")
.get()
.build();
Response response = client.newCall(request).execute();
Issues:
Does not work! Even if I set okhttp to follow redirects, I get a 200 response pointing to some instagram url. Of course the user is never prompted his instagram credentials, because no browser ever opened.
How to deal with this?
Notes:
I have used instagram as an example, the third party service is not relevant to the question. I am looking for a general solution to receive the result callback in my Android app.
I am not willing to implement client-side auth. I am willing to trigger steps 1.-5. from my Android app and receive the final result in a Java callback.

Mobile App webframe Authentication with Rails Devise

I am currently working on implementing a mobile app for our site that uses Ruby on Rails and Devise. The idea here is, at first, create a mobile login form that on successful login opens a web frame that is authenticated and allows the normal use of the (mobile optimised) site. Theoretically that should be possible.
I am having trouble with the following issues:
How do you get the pure session key for the user session via a json request? What methods can be used to manually generate it from devise, something that the sign_in(:user, user) method does?
Is it even possible to take that key and put it into the browser cookie the way it normally happens in devise, but on the mobile side?
I know that this is not the standard method of making mobile applications for the site, but I believe it should be possible.
You might want to consider using Devise Token Auth and treating your mobile application like just another webapp that requests permission from your main site. DTA is particularly nice since it takes care of managing the session tokens (renewing/expiring) and passing them onto the app requiring access. The issue is overriding your session controllers so that it automatically logs in after you already log in on the mobile app (or just rewriting your log in so it occurs in conjunction with the Rails site, rather than before). Considering you're already using Devise, this may also be more refactoring than you'd like.
If you want to put your authentication form on the mobile UI and pass the credentials over to the web frame, you need a way to pass data from the mobile app to the web frame.
How you accomplish this depends on what platform you're building on. I'm not really a mobile developer so I don't know for certain how difficult / easy these options are:
When opening the web frame, instantiate it with session data
Find a way to call methods on the client from the web frame. Something like getSessionData.
You could generate a fingerprint for the web frame, have the mobile UI send this data to the server, and then have the web frame authenticate with the server by sending the fingerprint.
Again, I'm not entirely sure how possible all these options are.
You should use token authorization and Android deep linking. It will allow you to login in the web browser and send a token to your app via deep linking.
OK, so I decided to make a webframe solution as follows, basically you post the login and password to a certain sign_in method specially designed to generate one-time sign in tokens for the application. You need two methods in the system to do that:
routes.rb
devise_scope :user do
get "sign_in_with_token/:token" => "sessions#sign_in_with_token"
post "get_login_token" => "sessions#get_login_token"
end
sessions_controller.rb (don't forget to add the method that increases the failed_sign_in_count on wrong password, otherwise that can allow brute force attacks)
def get_login_token
user = User.find_by_email(sign_in_params["login"])
password = sign_in_params["password"]
if user and user.valid_password?(password)
token = SecureRandom.hex(16)
user.update_attribute(:authentication_token, token)
render json: {token: token}, status: 200
else
render json: {error: "error"}, status: 403
end
end
and the method to sign in with that token
def sign_in_with_token
#user = User.where(authentication_token: params[:token], email: Base64.decode64(params[:email])).first
if #user
#user.update_attribute(:authentication_token, nil)
sign_in(#user, bypass: true)
end
redirect_to '/' # or user_root_url
end
That way the mobile app will work like this:
use the generic web frame to send ajax requests to the server and get that token for the user email if password is correct.
make a /sign_in_with_token/#{token from ajax}?email=#{base46 encoded email} link inside the app.
open that link inside the web frame and use the app as though you were logged in normally. Now the app can save email and password locally and use that logic to get the token again for another session. Later logging in will also be able to set the app id so that push notifications can be sent.
Appreciate any feedback or criticism on this solution.

Issues with ASP.NET Forms Authentication on Phonegap (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";
}

Cakephp and custom authentification for mobile application

Hey i'm working on a web application in combination with an android app. Now i want in my mobile app that the user have to log in with the same user data like on the web application. So i want to send a username and a password to the controller and in the login action of this controller the user should be verified and the additional user id should be send back to the application (the id is used for several operations in the app). I looked for the Auth Component of CakePHP but i don't find any solution for my problem. I hope you can help me.
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow('index','view');
$this->set('logged_in', $this->Auth->loggedIn());
$this->set('current_user',$this->Auth->user());
if($this->name == 'Specific') {
// for the specific controller
$this->Auth->authenticate = array('Basic');
} else {
// everything else
}
}
checkout KVZ's rest plugin it may be of interest https://github.com/kvz/cakephp-rest-plugin
Not sure about what you need to do on the cakephp side of things, but if you want to have a set of account credentials stored securely and persistently on an Android device, I suggest you take a look at the AbstractAccountAuthenticator class.
The AuthComponent has a login() method you can use to manually login users. Depending on the Cake version you're using this method also checks the credentials you supply (actually Auth is completely restructured in 2.0 in a way that's much more useful for situations like yours, it's worth taking a look!).
You can send in the login credentials from your android app any way you please (XML, POST parameters), extract them in your login action and send them to the AuthComponent (or, in 2.0, write a custom authenticator object to handle it all).

Categories

Resources