I am trying to get my head around Android AccountManager and OAuth.
What i would like to do is not let the phone have access to the password. (That is what Google suggests: "Be Smart About Security!")
So i checkout the Google sample application SampleSyncAdapter and start reading through the code. then i see this happen in AuthenticatorActivity:
private AccountManager mAccountManager;
private String mPassword;
/**
* ... Sets the
* AccountAuthenticatorResult which is sent back to the caller. We store the
* authToken that's returned from the server as the 'password' for this
* account - so we're never storing the user's actual password locally.
*
* #param result the confirmCredentials result.
*/
public void handleLogin(View view) {
....
mPassword = mPasswordEdit.getText().toString();
....
Log.d(TAG, "mPassword set to Account:" + mAccountManager.getPassword(account));
}
private void finishLogin(String authToken) {
....
mAccountManager.addAccountExplicitly(account, mPassword, null);
....
}
This Log message is "mPassword set to Account:test".
This is in some way understandable when you read the rest because of this
protected String doInBackground(Void... params) {
....
return NetworkUtilities.authenticate(mUsername, mPassword);
....
}
if the password was a token this would not work.
Also i would expect the rest of the code to work differently in Authenticator on getAuthToken()
I Assume i am completely wrong about something but i just want to use AccountManager to store the result of an OAuth "Dance" so that i can use this Account to authenticate my JSON RESTful service.
Can any one shine a light on this?
From the documentation we can read this:
It's important to understand that AccountManager is not an encryption service or a keychain. It stores account credentials just as you pass them, in plain text. On most devices, this isn't a particular concern, because it stores them in a database that is only accessible to root. But on a rooted device, the credentials would be readable by anyone with adb access to the device.
Thus, as I understand, here is a problem of misuse of the words (password and token). I guess the procedure is the following:
You ask a user to provide a login and password.
In your application you somehow send this login and password to your server.
Basing on this information your server generates a token and sends back to your application.
AccountManager stores this token in plain text and then this token is used to authenticate your user.
Related
i am currently using a custom webview on my Xamarin Forms App. I need to communicate with an api that needs an access token to return values. So far so good, I am able to login, receive my login data including my first access and refresh token via callback and store it on the smartphone in a sqlite database.
But how can I notice if the access token changes?
Since I have only stored the access Token on login, how can I react if the session of the webview updates the access token with the refresh token?
I need the new token for my native api calls, without a new login, since the webview refreshes the access token I somehow want to receive the current access token from my webview local storage.
Is there any way to grab the current access token from the webview local storage?
Generally, when we use the outdated token request API, the background server will verify the token requested. If the token expires, it will return the corresponding feedback data, and then the APP will pop up the user with such tips as: Token expired, please login again..
Then the APP will jump to the login page and login again to get the latest token.
Update 1:
Well, since Token can only be obtained at login time, so when you request a API and find the token has expired, you can login again and abtain the latest token.
But we don't recommend this ,because it defeats one of the purpose of token:Using for authentication.
For example, if a user logs in and doesn't use it for a long time, and another person with ulterior motives get the phone and use the APP, he can successfully use the APP and get some private information, even if the token expires.
Update 2:
Yes, it is possible to get the userKey field stored in LocalStorage.
For example, if you want to get the userKey field stored in LocalStorage, you can do like this:
1.Write an interface to accept Js callbacks
public class MyJSInterface : Java.Lang.Object, Java.Lang.IRunnable
{
Context context;
public MyJSInterface(Context context)
{
this.context = context;
}
[JavascriptInterface]
[Export]
public void Run()
{
Toast.MakeText(context, "Hello from C#", ToastLength.Short).Show();
}
}
2.Add to WebView and rename to "shixintest" :
mWebView.AddJavascriptInterface(new MyJSInterface(this), "shixintest");
3.call JS
private void getLocalStorageUserKey()
{
if (mWebView != null && TextUtils.IsEmpty(APPEnvironment.GetBeforeLoginUserKey()))
{
mWebView.LoadUrl(
"javascript:(function(){
var localStorage = window.localStorage; window.shixintest.getUserKey(localStorage.getItem('userKey'))})()");
}
I have an app which do the canonical email/password login and signup to the server.
Note: I need to do the login / auto-login every time the user open the app.
Now I want to add login/signup with fb.
Currently I'm using the android Account manager with a custom authenticator where I save mail and password. If a user log in with fb I will not have any password to save in the account: I have to save something else instead or I'll leave that field empty? In the latter if I'll add another auth system as Twitter how can I know to which system the user belong?
Speaking about the server, which data I'll have to send to it to authenticate Fb users? I thought about the couple email/id, but it doesn't seem too strong to me...
Thank you for your time
how can I know to which system the user belong
Use SharedPreferences in order to save which system the user belong to:
public static int TWITTER_ACCOUNT = 0;
public static int FB_ACCOUNT = 1;
onLogin(){
if(fb){
SharedPreferences sp = getSharedPreferences(context);
sp.edit().putString(PREF_ACTIVE_ACCOUNT, FB_ACCOUNT).apply();
}
}
which data I'll have to send to it to authenticate Fb users
Why not sending the access_token?
If a user log in with fb I will not have any password to save in the account
Just keep the access token. you should use fb access token to get data from FB or login to your system. have a look at oauth2
Also do note that you are missing the point if your users need to login each time the app starts
To save user data in AccountManager use:
public void setUserData(final Account account, final String key, final String value)
public String getUserData(final Account account, final String key)
I'm starting to write an app whereby a mobile app (Android/IPhone) will communicate with the GAE backend (Python) through a series of Web API calls using JSON.
I can't use Google Accounts for authentication so I need to implement my own auth. I have an idea of how to do this, but I'm not sure if there is a better way.
Can anyone help with some code examples/suggestions of how to achieve the below please?
Method
Mobile app calls a Login method on the server which authenticates and creates a session key in the store and returns this to the app - not sure how to generate the key/session or where on the request/response it should be.
On every call, the app passes this key for the server to authenticate and allows the action if it passes.
User should not have to login on mobile again unless they explicitly logout or lose the key.
Login Method - without key generation
class Login(webapp.RequestHandler):
def post(self):
args = json.loads(self.request.body)
email = args['e']
pwd = args['p']
ret = {}
user = User.gql('WHERE email = :1', email).get()
if user and helpers.check_password(pwd, user.password):
ret['ret_code'] = 0
ret['dn'] = user.display_name
else:
ret['ret_code'] = 1
self.response.headers['Content-Type'] = 'application/json'
self.response.out.write(json.dumps(ret))
I think you should use features webapp2 providing to implement your custom registration.
from webapp2_extras import auth
from google.appengine.api import users
class RegisterHandler(webapp2.RequestHandler):
def post(self):
email=self.request.POST['email']
password=self.request.POST['password']
#Let webapp2 handle register and manage session
user = auth.get_auth().store.user_model.create_user('own:'+str(email), password_raw=password,email=email)
#user (True, User(key=Key('User', 80001), auth_ids=[u'own:useremail#mail.com'],email='useremail#mail.com',password=u'hashed_password',...))
if not user[0]: #user is a tuple
self.response.write(user[1]) # Error message
else:
#You can extend your User Model e.g UserProfile(User): or have a UserProperty in your profile model as the example.
profile=UserProfile(user=users.User(user[1].email)).put()
self.response.write(str(profile.key()))
class LoginHandler(webapp2.RequestHandler):
def post(self):
email = self.request.POST.get('email')
email = self.request.POST.get('password')
# Try to login user with password
# Raises InvalidAuthIdError if user is not found
# Raises InvalidPasswordError if provided password doesn't match with specified user
try:
auth.get_auth().get_user_by_password('own:'+email, password)
#Return user_session with User id,
except InvalidPasswordError, InvalidAuthIdError:
#Error
You can check user logged in by:
if auth.get_user_by_session():
#Logged in
else:
#Not logged in
On your client application(Android, IOS). You only have to store the response cookie and send it for every sub sequence requests.
Good luck :)
Have a look at webapp2 and webapp2 extras with sessions, auth and JSON
I cannot see why you would need a session?
Sessions on App Engine are persisted in the data store, so if you can keep your requests stateless, I encourage you to do so.
As you will have your own user service which will authenticate the users, I suggest you use Digest authentication, as the secret is never included in the request.
There are libraries implementing Digest for most client and server platforms.
If you dont explicitly want to use Sessions etc. you can simply use the Datastore. Try following this:
Get a unique deviceID/email to identify each unique user.
On request from a specific user, generate a random authentication key, and store it attached to the user's email/deviceID and probably the current timestamp and a loggedIn flag.
SO you have:
User email/id: someone#example.com
password: xxxxxxxxxx
Key : 2131231312313123123213
Timestamp: 20:00 12-02-2013
loggedIn : boolean value
This can be database model. Now whenever the user logs in:
Check email, password combination.
If valid, generate random key, and update the datastore with the new key
update timestamp with current time, and set loggedIn to True
Now return the key back to the client (Android/iPhone) in a JSON object.
Now on every request, Check the received key against the one in your datastore, and if loggedIn flag is set to true. If both OK, process the request.
Also, on Logout:
Just set the loggedIn flag in the datastore to False.
Hope this helps :)
Try gae-sessions for session management. It creates secure cookies for you and allows you to easily associate data with each user. Just provide your own logic for the initial authentication.
It was built specifically for App Engine and is pretty popular and super fast/scalable.
https://github.com/dound/gae-sessions
There are many ways to do this.
1) When you check the users login details if it checks out you can then create a random UUID or string and store the User object in memcache with the random string as the Key and the User Object as the value. Then return the random string along with your response headers. On the mobile when you are parsing the response, get this header and store it in the local cache. On all further requests keep sending this key back in the request header and in your controller get the User object from memcache using this key and proceed. If the object is not in memcache you can send back a response which prompts the user to log in.
2) If you dont want to use memcache you can store the User object in the session and on the client side while parsing the response get the session id from the response. Its usually JSESSIONID. Then store that and resend it with further requests. In the controller you can check if the current session has the user object else force login.
1) Another way to go would be to return the appengine key for the user along with the response and resend it.
Just google get response header from response. Then get the SESSIONID/JSESSIONID header, store and add the field with the same name and value to all further request headers. Thats the easiest way.
My first answer on stackoverflow and no code exapmles, dangit if only i knew python.
I want to obtain a Google Authtoken from the AccountManager that I can send to my Webservice (not hosted on App Engine) to authenticate the User (I just need the email address and eventually his name, if no permission is required for this).
What do I have to use for the "authTokenType" Paramter of the "getAuthToken" method?
And which google Api do I have to use to get the Users Email?
This is doable using OpenID Connect, however it's sort of experimental, so details could change in the future. If you get an OAuth token for the 'https://www.googleapis.com/auth/userinfo.email' or 'https://www.googleapis.com/auth/userinfo.profile' scope you can use it to get user info from https://www.googleapis.com/oauth2/v1/userinfo (including email). Of course the user needs to authorize this.
You should theoretically be able to get the token from AcccountManager using the "oauth2:https://www.googleapis.com/auth/userinfo.profile" as the token type, but that doesn't appear to work on my device (Galaxy Nexus with stock 4.0.4). Since getting a token via the AccountManager doesn't work (at least for now), the only reliable way is to use a WebView and get one via the browser as described here: https://developers.google.com/accounts/docs/MobileApps
There is a demo web app here that does this: https://oauthssodemo.appspot.com
(late) Update: Google Play Services has been released and it is the preferred way to get an OAuth token. It should be available on all devices with Android 2.2 and later. Getting a profile token does work with it, in fact they use it in the demo app
I have had problems with this as well, since I was not able to find anything like a reference. Perhaps this can help you (code copied from an Android example on using the account manager):
Somewhere in an event handler of your Android app, issue a request for an auth token to get the user's email address in Android:
_accountMgr = AccountManager.get(this);
Account [] accounts = _accountMgr.getAccounts();
Account account = accounts[0]; // For me this is Google, still need to figure out how to get it by name.
_accountMgr.getAuthToken(account, AUTH_TOKEN_TYPE, false, new GetAuthTokenCallback(), null);
In the callback, extract the access token:
private class GetAuthTokenCallback implements AccountManagerCallback<Bundle> {
public void run(AccountManagerFuture<Bundle> result) {
Bundle bundle;
try {
bundle = result.getResult();
final String access_token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
// store token somewhere you can supply it to your web server.
} catch (Exception e) {
// do something here.
}
}
}
Make some request to your web server, supplying the access token.
On the web server, validate the access token and obtain the email address:
curl -d 'access_token=<this is the token the app sent you>' https://www.googleapis.com/oauth2/v1/tokeninfo
You should get something like this:
{
"issued_to": "<something>.apps.googleusercontent.com",
"audience": "<something>.apps.googleusercontent.com",
"scope": "https://www.googleapis.com/auth/userinfo.email",
"expires_in": 3562,
"email": "<users email address>",
"verified_email": true,
"access_type": "online"
}
or if something went wrong:
{
"error": "invalid_token",
"error_description": "Bad Request"
}
You can get the User's name with the Google+ People API. (It will not provide the user's email address).
If this is OK, you can use "Know who you are on Google" as the authTokenType.
There is a sample application provided by Google that demonstrates how to use the AndroidAccountManager in conjunction with the Google+ APIs.
Link: http://code.google.com/p/google-plus-java-starter/source/browse/#hg%2Fandroid
We are implementing a project where the users post and get some information from a server. The scenario is that the user can create account/login both manually (giving email and password) and with facebook credentials using SSO. I implement mostly the Android part, but my questions are general.
Let’s say that I have a button where SSO is called prompting the user to give his credentials. So in order to create account what should I send to the server? Get the FB email of the user and set as password the Access Token that I received? Is that Access Token unique and permanent for every FB account, meaning the each time I use the same FB credentials I get the same Access Token?
Is there any additional work that needs to be done on the server side? Or can the server handle the users that use their FB accounts similarly as it handles the others?
Every clarification will be really helpful. Thank you in advance!
Are you using the Facebook Android SDK? If so, manual login (with user email/password) and SSO (via the Android Facebook app) are very similar, in particular with respect to token handling.
For example if you check out the example in the SDK (at sdk\examples\simple\src\com\facebook\android) you will see that the code does something like the following (split between three files).
private static final String TOKEN = "access_token";
private static final String EXPIRES = "expires_in";
private static final String KEY = "facebook-session";
Facebook session = new Facebook(APP_ID);
SharedPreferences savedSession = context.getSharedPreferences(KEY, Context.MODE_PRIVATE);
session.setAccessToken(savedSession.getString(TOKEN, null));
session.setAccessExpires(savedSession.getLong(EXPIRES, 0));
if (session.isSessionValid()) {
session.authorize(mActivity, mPermissions, new LoginDialogListener());
}
So you have to save the session token in SharedPreferences after each successful login (that is also in the example), but the token handling and login (authorize()) is the same for both manual and SSO login (depending on the activityCode parameter).
The token has a expiration timestamp, I guess the easy way is to generate the user account with the data fb will send you and store the fb token and expiration date as user attributes.