User Session Management in RoR API for Android - android

I am building an API in RoR to be used by an android app. I have looked at various other similar questions on SO but found nothing which fits my exact use case. I need to identify a logged in user and respond to the request accordingly. This goes just beyond getting the user id to user categories, implicit preferences (different from settings) to give the user a more personalized experience.
In a web app this is done through a session cookie using which I can essentially call the following methods:
current_user.id # Gets user's id
current_user.category # Gets user's category
current_user.auth_level #Gets user's permission level
To do this, in the webapp I have the following setup:
In login action:
...
#user_session = session_setter(email, password)
...
def session_setter(email,password)
#session = UserSession.new(:email => email, :password => password)
#session.save
end
UserSession.rb
class UserSession < Authlogic::Session::Base
logout_on_timeout true
last_request_at_threshold 100
skip_callback :persist, :persist_by_cookie
skip_callback :after_save, :save_cookie
skip_callback :after_destroy, :destroy_cookie
end
session_store.rb (configured for memcached)
require 'action_dispatch/middleware/session/dalli_store'
App::Application.config.session_store :dalli_store, :memcache_server => ['127.0.0.1:11211'], :key => '_session'
This setup allows me to access current_user object once the user is logged in and access all columns in the table users.
For android, this is what I have figured out:
Create an api_key for each user who creates an account. This could have an expiry date. Maintain and store this key in the users table. The api_key is passed on to the app on the first request where it is stored in something like sharedPreferences. Every request should carry the api_key to the server. Put filters on actions which require the user to be logged in, to check if the key is present or not.
Now, here is my question: In this setup I would need to retrieve the users record from the table (or store it in the memcached with the api_key as a key value pair) on every request through the filter.Is there a way to use something like current_user with the api?
In short I want to to replicate the same strategy for maintaining logged in users for the api as I have for the web app.

API is supposed to be unaware of your web-app contexts.
API is the classical stateless HTTP.
You ask it a question, it gives you answers. Albeit, from your web app's state and data, but it has no concern with what goes on at that end.
An API cannot, or rather, should not be coupled with the user logged in/ logged out state.
It is your webapp's responsibility to query the API with as much information it needs to reflect the logged in state.
Eg.,
If your API has a method get_recommendations,
it should ideally take in multiple args, to be able to handle all cases
logged out users, will make the query, wrt the page being viewed. As in, a matrix movie scene being viewed, should give other matrix scenes as reco's
logged in users, will make the query, wrt other aspects. As in, how many action videos has been viewed by this user, can be passed in as an arg.
The API endpoint should have the ability to handle both these scenarios seamlessly.
Make other calls to other methods if need be, to find out the current user's (identified by a user id key passed in) details.
So, from a design perspective, you should not pass in anything other than the fact that this request is for a signed in user with id = X.
Let your API handle everything else.
API is nothing but a special function/method.
Methods can do pretty much anything, as long as they remain true to their description / signature.

Related

Parse anonymous User ID doesn't persist on Android

I'm using anonymous users with parse but user ID keeps changing every time the app is restarted. I can create a serious of records, tied to a user and see them in backend / the app itself. Then kill the app, restart it and see a new user in Parse backend. Obviously, all records are no longer displayed because this is a "new user".
How can I make anonymous user id persist?
Turns out, if you're using Parse SDK and their anonymous users, you need to check if user is logged in and then run anonymous user initialization only in that case. Otherwise, each time the app is restarted, a new anonymous user is created. Makes sense but wasn't in their docs.
When you 'create' a new user, i think that you are getting an implied 'logon' of that new user.
Note that the response to the logon includes a session token ( no expiration on token ).
Using 'sharedPreferences' or some other android persistence technique, when you restart the app you can just retrieve the token from your persistence layer and - at least in REST API - just include that token in your headers along with AppId, and REST key and you will get that users data.
If you are using the SDK and not rest api, there may be some other way to get back to a reference for the user.

Login into app and get session token from server for api requests

There is a website with a lot of users. In order to use the private pages the user has to login with username and password.
Now I'm working on a android app that displays content from the website via an API in a native way, no WebView or similar. The workflow would be the following:
App is started
App checks if a sessionkey exists.
If the key exists, ask the website API if the sessionkey is valid and belongs to the user
=> if so, allow the usage of the app
If no sessionkey exists, ask for username+passwort, ask the API if it's correct and get a new sessionkey via the API => allow app usage
User clicks on e.g. "account balance"
-> API request is done with the stored sessionkey and the result is displayed
User clicks on "payout", fills in a form and gets his money
-> API request is done with the stored sessionkey and the result is displayed
Now I'm searching for a best practice solve for this. I don't want my sessionkeys to be simply hijack-able and I want to be sure that the sessionkey realy belongs to this user. As private data can be seen/modified via the app, privacy is important.
I took a look at oAuth but this isn't what I need I think because I need a consumer key and consumer secret that is unique for every user and I can't integrate this into the app. I don't want to have a new window/browser for entering username+password and handle the response via a callback like oAuth is doing.
Any hints on this?
Thanks a lot!
For a good starting point on http authentication best practices, have a look at this article:
"Principles for Standardized REST Authentication".
If you want to send private data, you need to use HTTPS. That will also take care of securing your session tokens, etc. If you need to check what token belongs to what user, you naturally need to have this mapping on the server side. Most server side platforms give you a session map or similar, keyed by session ID, etc. and you can store pretty much anything in it, including user details.

What is the purpose of the "developer payload"? What can it be used for?

The Android docs say that its meant for "supplemental information about an order" but at the same time it also says not to use this to send "actual data or content".
So what is the purpose of this "developer payload"? Why does this feature exist? Can you describe a real-world practical example of how I can use it in my own In-app Billing implementation?
As aromero mentioned the developer payload field is limited in size. This is why the docs recommend not to use this key to send data or content.
What you do instead is save the content to a database somewhere (e.g. on the user's device or your own server), and then put the record's index in the developer payload field. When you receive it back via the PURCHASE_STATE_CHANGED broadcast intent, you can associate it with the data in your database.
Note that the developer payload is not sent by the Market when using any of the test android item ids. You have to be using real in app purchase items.
Also, according to this (I have not verified this yet), you will not be receiving the developerPayload in DEBUG MODE. You need to sign your application to RELEASE MODE to receive developerPayload.
Lastly, as you commented below, the returned JSONObject (in response to GetPurchaseInformation) already includes orderId, productId, purchaseTime and more. So "developer payload" should actually be used for anything but to identify the purchase... i.e. the answer is the opposite of what has been suggested below. What you can use "developer payload" is to add some information not in the JSONObject, like purchaser's additional details (e.g. GPS location if enabled, device brand & model, etc.).
The accepted answer is misleading and the last paragraph is plain wrong.
Here's what the official documentation has to say about it.
You should pass in a string token that helps your application to identify the user who made the purchase, so that you can later verify that this is a legitimate purchase by that user. For consumable items, you can use a randomly generated string, but for non-consumable items you should use a string that uniquely identifies the user.
When you get back the response from Google Play, make sure to verify that the developer payload string matches the token that you sent previously with the purchase request. As a further security precaution, you should perform the verification on your own secure server.
The payload may help you prevent to identify users who circumvented Google Play Service API or your app somehow by sending the payload to your server where you can check whether this user ever purchased the item. Presumably circumventing the GPS will get your app fooled with the purchase certificate. But if you have all the user IDs of people who actually did honestly purchase the item saved on your server - it would be easy to validate the purchase based on the user ID. The problem here - google made it impossible to rely on it unless you have all your users "logged in" in some way.
The docs provide a real example:
A developer-specified string that can be specified when you make a
REQUEST_PURCHASE request. This field is returned in the JSON string
that contains transaction information for an order. You can use this
key to send supplemental information with an order. For example, you
can use this key to send index keys with an order, which is useful if
you are using a database to store purchase information. We recommend
that you do not use this key to send data or content.
You can use this field to identify the item the user is purchasing. When you issue a REQUEST_PURCHASE request you can put additional information using DEVELOPER_PAYLOAD. When you get the response from PURCHASE_STATE_CHANGED you'll get this info back in the developerPayload field, so you can identify the order.
This field is limited to 256 chars and it's unencrypted (you can verify the signature though), it's not meant to store actual content.
I hope this will help:
Security Recommendation: When you send a purchase request, create a
String token that uniquely identifies this purchase request and
include this token in the developerPayload.You can use a randomly
generated string as the token. When you receive the purchase response
from Google Play, make sure to check the returned data signature, the
orderId, and the developerPayload String. For added security, you
should perform the checking on your own secure server. Make sure to
verify that the orderId is a unique value that you have not previously
processed, and the developerPayload String matches the token that you
sent previously with the purchase request.
More information here.

How would I go about creating a login

The set-up:
I have an android application that so far can register a user by inserting values into a remote mysql database. I'm now trying to implement the log in.
I was thinking that I can add a "logged in" column to the user table in the database that would store whether or not the user was logged in. Then I would have a trigger that would log the user off after a certain amount of time has been elapsed.
The application's use is to retrieve files based upon if the user has access to a certain file. For this I have an "access" column in the user table table specifying the access a user has to a certain file. I was thinking that when a user clicks an item in a list the application would send their login information and the server would determine if the information was correct then check to see if they had access to the specified file then send back the file if the information is correct.
The problem I'm having though is that checking the registration information takes about 2 seconds alone(due to connecting to the socket and sending a string over the network) and if I try to check both the login and the access id it would take slightly longer.
I feel as if I'm trying to reinvent the wheel but I can't find any viable resources on this matter. Criticisms? Suggestions?
(I wouldn't mind doing a complete redesign I just need to know where to start)
Never connect a client to a db-server. There's no way to intercept hacking attempts, because privileges are very basic (SELECT, UPDATE, etc., they ignore the query):
UPDATE users SET name='%s' WHERE userID=%i // where %i will be defined as the real userID
Above should be a valid query to update the user's account-information, however, a hacker can easily intercept this and change it into:
UPDATE users SET name='%s' WHERE userID=15 // ... or any other variable
Instead, you should create a web based API which will validate each query, or better, support only specific API-commands:
account/update.json?name=%s

Handling login and remember me with the AccountManager

I've integrated android's account management in my application and I can manage accounts from the Accounts & Sync settings.
I want to have the classic login activity that forwards the user to his home activity on successful login, having the option to remember the user's password. However, the AccountAuthenticatorActivity must return its result to the AccountManager with the credentials and the rest of the account information, calling an explicit finish() and returning the intent.
How can I give the AccountManager the info it needs without having to finish() my login activity?
AccountManager is not meant to be called by an interactive application, but rather by a Sync Adapter. (A great tutorial is "Did You Win Yet? » Writing an Android Sync Provider" Part 1 and Part 2 which gives great code examples but doesn't do as great a job of explaining the data flow.) It's like this:
You develop a ContentProvider which wraps a database. You build a SyncAdapter (this is a background Service) to connect to a server and fetch data and sync the ContentProvider to match the server. Then, your UI queries to the ContentProvider to show the fetched data. There are some methods to directly query for specific information as well, if you want to search and cache results for example. See Developing RESTful Android Apps for a nice hour-long session on how the data model should look. They give three architecture examples, starting from a "naïve" implementation then progressing to the proper SyncAdapter model.
As for authentication itself, the way SyncAdapter uses the AccountManager is to obtain an authentication token. This is a (often) a big hexidecimal value, that is passed as part of the HTML headers in lieu of a username/password pair. Think of it as a one-session unique key. Posession of the key is proof of authentication, and they expire periodically. When they expire, you reauthenticate and fetch a new one. SyncAdapater asks AccountManager for an auth token for a specific account-type / username combination. AccountManager auths with the server (asking the user for a new password if necessary due to change) and returns the token to the SyncAdapter, which uses it from then on.
If this model isn't appropriate for your application, you need to manually handle login/logout in your app code instead. Kind of a pain, I know.
#jcwenger That is not entirely correct. You can use the AccountManager from an interactive application as well. For example, you can add accounts without invoking the account manager interface by using AccountManager's addAccountExplicitly() method.
On the "Did You Win Yet?" article you can clearly see that the account manager is invoked from the application's own Activity. This is useful if the application has its own account management interface.
My version of the 'classic flow' using AccountManager:
I use my AuthenticatorActivity both for the normal case where it's used via. Accounts & Sync Settings, but I also open it out for use by applications that rely upon the same Accounts.
I have a separate apk with the Authenticator implemented and other apps (separate apks) that rely upon these Accounts.
Cases handled:
the normal case: the user adds/authenticates via. Accounts & Sync (as per the Android sample project)
handle authentication requests from external apps:
How? I provide an intent filter in the Authenticator app's Manifest so other apps can instantiate the AuthenticatorActivity via. startActivityForResult (they must include an intent extra that indicates who they are (their app's package)). In the AuthenticatorActivity I detect this case and refrain from calling setAccountAuthenticatorResult when the authentication process has come to a end because I reserved it's use for the normal case above. The user enters their credentials and presses Sign In: AccountManger is checked for a matching account and if matched I
persist that Account's username as the active user for the calling app's package. I then return an intent to the calling app via. setResult indicating success, the username and account type. In the case where the Account didn't exist I go through the process that the normal case goes through, i.e. calling addAccountExplicitly and then set the active user and then as before calling setResult and finish.
Hope this helps someone.

Categories

Resources