I'm developing an Android app and a RESTful API with Laravel 5 Framework.
I've got a trouble with the login activity: the flow is that the user ask a 8th characters code and the server web sends him a SMS with it. Then user can do the login using this code like a password.
This is the code that asks a code:
private void askCode(String mobile) {
GsonRequest<String> jsObjRequest = new GsonRequest<String>(
Request.Method.GET,
WebAPIRoute.authGetCode + "/" + mobile,
String.class, null,
new Response.Listener<String>() {
#Override
public void onResponse(String code) {
txtResponse.setText("Code asked successfully.");
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
Toast.makeText(getBaseContext(), volleyError.getMessage(), Toast.LENGTH_SHORT).show();
}
});
this.requestQueue.add(jsObjRequest);
}
And this the method in the RESTful API to generate a code:
public function getCode($mobileNum)
{
//genero un numero casuale da mandare con l'sms
$code = mt_rand(10000000, 99999999);
Session::put('code', $code);
sendCode($mobileNum, $code); //send code by SMS
return response()->json(array("success"=>true));
}
The code generated is stored into Laravel's Session (configurated with file driver).
When the user wants to login, the app call this method:
private void saveUser(final String code, final String mobile, final String name) {
HashMap<String, String> params = new HashMap<String, String>();
params.put("nickname", name);
params.put("mobile", mobile);
params.put("code", code);
GsonRequest<String> jsObjRequest = new GsonRequest<String>(
Request.Method.POST,
WebAPIRoute.authValidateCode,
String.class,
params,
new Response.Listener<String>() {
#Override
public void onResponse(String authtoken) {
final Account account = new Account(accountName, mAccountType);
String authtokenType = mAuthTokenType;
// Creating the account on the device and setting the auth token we got
// (Not setting the auth token will cause another call to the server to authenticate the user)
mAccountManager.addAccountExplicitly(account, code, null);
mAccountManager.setAuthToken(account, authtokenType, authtoken);
Bundle data = new Bundle();
data.putString(AccountManager.KEY_ACCOUNT_NAME, accountName);
data.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
data.putString(AccountManager.KEY_AUTHTOKEN, authtoken);
data.putString(PARAM_USER_PASS, code);
data.putBoolean(ARG_IS_ADDING_NEW_ACCOUNT, true);
final Intent res = new Intent();
res.putExtras(data);
setAccountAuthenticatorResult(res.getExtras());
Intent i = new Intent(getBaseContext(), MyEventsActivity.class);
startActivity(i);
}
}
,
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
Log.e(TAG, volleyError.getMessage(), volleyError);
showMessage("Errore nell'autenticazione. Riprova piu` tardi.");
}
});
requestQueue.add(jsObjRequest);
}
The method of API that validate the code is this:
public function validateCode() {
$code = trim(Input::get('code'));
$nickName = trim(Input::get('nickname'));
$phoneNum = trim(Input::get('mobile'));
if (empty($phoneNum))
abort(400, 'mobile parameters not provided.');
if (empty($code))
abort(400, 'code parameters not provided.');
if (empty($nickName))
abort(400, 'nickname parameters not provided.');
$validCode = Session::get('code');
Log::info('code: ' . $code . " validCode: " . $validCode);
if($code == $validCode) {
Session::forget('code');
// Retrieve the user by the attributes, or instantiate a new instance...
$user = User::firstOrCreate(['Mobile' => $phoneNum]);
//aggiorno i campi nickname e password col nuovo codice
$user->Nickname = $nickName;
$user->password = $code;
//save!
$user->save();
//viene usata l'autenticazione di Laravel e gli serve una password
$token = JWTAuth::attempt(['Mobile' => $phoneNum, 'password' => $code]);
return response()->json($token);
} else {
//return response()->json(array('success' => false, 'error' => 'The code isn\'t correct.'));
abort(401, 'The code isn\'t correct.' . $validCode);
}
}
I've tested the RESTful API with Chrome and Firefox and the login works. With the app no. Infact the issue is that Session::get('code'); in validateCode returns always an empty value. I checked the Session file generated with Session::put('code', $code); and is correct. But when Session::get('code') is called, Laravel generates another Session file and seems not use the previous.
I disabled the CSRF Middleware in RESTful API.
What is wrong?
Storing the session on the server side is pointless. API's are suppose to be stateless so the second you finish the first request for the code and store it in a session on the server side, the session will end and the next request will not remember anything you set.
If you wanted to keep the code login and avoid using tokens, then you will have to send a unique code from the android app which identifies the user. Then generate the code on the server side and store it in a table with user_identifier and generated_code and create a model to access it e.g.
AttemptedLogin
user_id | generatedCode
0001 | 87392042
0032 | 83214320
Then on add this to saveUser
params.put("user_id", user_id); // can be the android device ID or even a unique timestamp
Finally on the server side in validateCode replace $validCode line with the following:
$user_id = trim(Input::get('user_id'));
....
$validCode = AttemptedLogin::where('user_id', $user_id)->first();
if($code == $validCode->generatedCode) {
$validCode->delete();
....
Related
PaytmOrder paytmOrder = new PaytmOrder(orderIdString, midString, txnTokenString, txnAmountString, callBackUrl);
TransactionManager transactionManager = new TransactionManager(paytmOrder, new
PaytmPaymentTransactionCallback(){
#Override
public void onTransactionResponse(Bundle bundle) {
Log.e("server response", ""+bundle);
String status = bundle.getString("STATUS");
String transactionid = bundle.getString("TXNID");
String amount = bundle.getString("TXNAMOUNT");
if(status!=null){
Log.e("STATUS", status);
}
}
The above is my code
I am getting the amount after transaction but remained at payment page .
Payment page is to redirecting me to my android app.
How to do this ?
Kindly use the below call back URL, the same URL mentioned on paytm developer docs as well.
For Staging
https://securegw-stage.paytm.in/theia/paytmCallback?ORDER_ID=<ORDER_ID>
For Production
https://securegw.paytm.in/theia/paytmCallback?ORDER_ID=<ORDER_ID>
Ruby 2.2.4
Rails 5.0.0.1
retrofit:1.9.0'
otto:1.3.8'
Hello People,
I am just a beginner in programming and I have the task to create an android app with Android Studio, where you can login and logout as a user. Therefore
I programmed a Token-based authentication in RoR 5 API with Json Web Token (JWT). This tutorial helped me: http://tutorials.pluralsight.com/ruby-ruby-on-rails/token-based-authentication-with-ruby-on-rails-5-api ). After putting the right credentials in curl I finally receive the token: {"auth_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE0NjA2NTgxODZ9.xsSwcPC22IR71OBv6bU_OGCSyfE89DvEzWfDU0iybMA"}
Now after this works fine I tried to implemend the login-function in android studio, which should interact with my RoR server.
I followed strictly a tutorial (https://www.sitepoint.com/retrofit-a-simple-http-client-for-android-and-java/ )which uses Retrofit for authentication with a php server. However I want to implement this for my RoR server. Nevertheless I tried this code out. I was happy, that the program can communicate with my ruby on rails server. If I login on android app with the right credentials, I see on the console that it will be accepted.
However I dont know how I get a message on android, that the login was succesful plus a welcome message with the name of the user. As I said I use JWT in RoR which gives a long token back. Is there a possibility that retrofit can interpret this token? Or do I need to install JSON Web Token for android? Here is a part of the code in ruby on rails:
app/commands/authenticate_user.rb
class AuthenticateUser
prepend SimpleCommand
def initialize(email, password)
#email = email
#password = password
end
def call
JsonWebToken.encode(user_id: user.id) if user
end
private
attr_accessor :email, :password
def user
user = User.find_by_email(email)
return user if user && user.authenticate(password)
errors.add :user_authentication, 'invalid credentials'
nil
end
end
lib/json_web_token.rb
class JsonWebToken
class << self
def encode(payload, exp = 24.hours.from_now)
payload[:exp] = exp.to_i
JWT.encode(payload, Rails.application.secrets.secret_key_base)
end
def decode(token)
body = JWT.decode(token, Rails.application.secrets.secret_key_base)[0]
HashWithIndifferentAccess.new body
rescue
nil
end
end
end
And here comes a part of my android app, which code is from the tutorial I followed:
public class Communicator {
private static final String TAG = "Communicator";
private static final String SERVER_URL = "http://127.0.0.1/retrofit";
public void loginPost(String username, String password){
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(SERVER_URL)
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();
Interface communicatorInterface = restAdapter.create(Interface.class);
Callback<ServerResponse> callback = new Callback<ServerResponse>() {
#Override
public void success(ServerResponse serverResponse, Response response2) {
if(serverResponse.getResponseCode() == 0){
BusProvider.getInstance().post(produceServerEvent(serverResponse));
}else{
BusProvider.getInstance().post(produceErrorEvent(serverResponse.getResponseCode(), serverResponse.getMessage()));
}
}
#Override
public void failure(RetrofitError error) {
if(error != null ){
Log.e(TAG, error.getMessage());
error.printStackTrace();
}
BusProvider.getInstance().post(produceErrorEvent(-200,error.getMessage()));
}
};
communicatorInterface.postData("login", username, password, callback);
}
public void loginGet(String username, String password){
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(SERVER_URL)
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();
Interface communicatorInterface = restAdapter.create(Interface.class);
Callback<ServerResponse> callback = new Callback<ServerResponse>() {
#Override
public void success(ServerResponse serverResponse, Response response2) {
if(serverResponse.getResponseCode() == 0){
BusProvider.getInstance().post(produceServerEvent(serverResponse));
}else{
BusProvider.getInstance().post(produceErrorEvent(serverResponse.getResponseCode(), serverResponse.getMessage()));
}
}
#Override
public void failure(RetrofitError error) {
if(error != null ){
Log.e(TAG, error.getMessage());
error.printStackTrace();
}
BusProvider.getInstance().post(produceErrorEvent(-200,error.getMessage()));
}
};
communicatorInterface.getData("login", username, password, callback);
}
}
This is the Php script from the android tutorial. Maybe I need to transfer this into ruby
<?php
//Post Method here
if(isset($_POST['method']) == 'login'){
$username = $_POST['username'];
$password = $_POST['password'];
if($username == "admin" && $password == "admin"){
$response = array('returned_username' => "-admin-",
'returned_password' => "-admin-",
'message' => "Your credentials are so weak [USING_POST]!",
'response_code' => "1");
echo json_encode($response);
}else{
$response = array('response_code' => "-1",
'message' => "invalid username or password");
echo json_encode($response);
}
}
The json encoding in php includes an array (username, password, message, responsecode). How can I include the same array with JWT on ruby on rails? Any idea? And how I get a message on android, that the login was succesful
In the line JsonWebToken.encode(user_id: user.id) if user, you're passing a hash with a key user_id. You could extend this line to include additional attributes, somewhat like
JsonWebToken.encode(user_id: user.id, name: user.name) if user.
You will then need to include a JWT library in your android app and decode the server response (obtained from Retrofit) and you'll be able to access the attributes you encoded.
There is one way to retrieve the token value, first add those lines to your ServerResponse.java file:
#SerializedName("token")
private String token;
String token
this.token = token;
public String getToken() { return token; }
public void setToken(String token) {
this.token = token;
}
According to what is already there and then go to then main activity and store the token in a variable such as this:
String auth_token = serverEvent.getServerResponse().getToken()
next you can make it private final and use it all over your project.
Hope it helps!
I want to share an image on a Facebook page of mine. But I couldn't figure out how to do this step by step. And couldn't find step by step guide for this.
This is my code to share an image on a page
Bundle params = new Bundle();
String nameText = name.getText().toString();
String tags = engine.implodeTags(tagsList);
String textText = text.getText().toString();
params.putString("caption", nameText + "\n\n" + textText + "\n\n" + tags);
params.putString("url", imagesList.get(mainImageSelected).getImageUrl());
params.putString("access_token", "{access token here}");
new GraphRequest(
AccessToken.getCurrentAccessToken(),
"/{page_id here}/photos",
params,
HttpMethod.POST,
new GraphRequest.Callback() {
public void onCompleted(GraphResponse response) {
if(response.getJSONObject()!=null) {
Log.d("qwe", response.getJSONObject().toString());
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(activity, "Shared on facebook", Toast.LENGTH_LONG).show();
progressBar.setVisibility(View.GONE);
}
});
}
else{
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(activity, "Error", Toast.LENGTH_LONG).show();
progressBar.setVisibility(View.GONE);
}
});
}
}
}
).executeAsync();
It works if I put access token by hand. I am getting access token here
https://developers.facebook.com/tools/explorer .
But after some time this access token is not working any more. So I need to get new access token.
How to get PAGE access token from android itself? Via user login button of facebook sdk?
https://developers.facebook.com/docs/facebook-login/access-tokens
Please help.
Easily if you use facebook SDK to do that. read at here https://developers.facebook.com/docs/sharing/android
You need to get Permanent access token to share images on your Page. To get that you need to follow steps written here.
facebook: permanent Page Access Token?
I create an App that a user can send messages(notifications) in other users. Fot these perpose i use parse SDK. So i send the message from device into the parse cloud with below code.
final ParseQuery<ParseUser> query = ParseUser.getQuery();
query.whereEqualTo("email", "user#email.com");
query.getFirstInBackground(new GetCallback<ParseUser>() {
public void done(ParseUser object, ParseException e) {
if (e == null) {
Toast.makeText(getActivity(), "User found", Toast.LENGTH_SHORT).show();
String search_username = object.getString("username");
String id = object.getObjectId();
Log.d("ObjectID:",id);
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("recipientId", id);
params.put("message", username);
ParseCloud.callFunctionInBackground("sendPushToUser", params, new FunctionCallback<String>() {
public void done(String success, ParseException e) {
if (e == null) {
// Push sent successfully
Toast.makeText(getActivity(), "Request send", Toast.LENGTH_SHORT).show();
}
}
});
Then i have the next cloud function for recieve and push the message to the specific user.
Parse.Cloud.define("sendPushToUser", function(request,response){
var senderUser = request.user;
var recipientUserId = request.params.recipientId;
var message = request.params.message;
var title ="Friend Request";
if(message.length > 140){
message = message.substring(0, 137) + "...";
}
var recipientUser = new Parse.User();
recipientUser.id = recipientUserId;
var pushQuery = new Parse.Query(Parse.Installation);
pushQuery.equalTo("user", recipientUser);
Parse.Push.send({
where: pushQuery,
data: {
"alert":{"data":{"message":"message",
"title":"title"}}
}
}).then(function(){
response.success("true")
}, function(error) {
response.error("Push failed to send with error: "+error.message);
});
});
But the message never been received. If i sent a push notification from parse dashboard everything works fine. Anyone knows how to solve it? The device expect a JSON to received so may my cloud function didnt send data in json format? Thanks in advance
I had problems while sending notifications because of 2 things
enabling client push "not in your case"
didn't save the user in the installation "try the following"
After the user logs into your app add his id to the installation by
ParseInstallation installation = ParseInstallation.getCurrentInstallation();
installation.addUnique("userId", currentUser.getObjectId());
installation.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
// check the returned exception
if (e == null) {
// everything worked fine
} else {
// error occurred
}
}
});
hope it helps :)
Update
In your code you're sending the recipient userId although you saved the username also in your cloud function you have the same problem, the username is saved but you query the installation based on the id. I've updated the installation above also change the "user" in your cloud function to the "userId"
pushQuery.equalTo("userId", recipientUser);
I'm working on a Oauth2 Token system to access my REST API for my Android app. I'm having some problems with the token refreshment part on the client side.
Here is the flow : My app makes a request (with an access Token in parameter) to the server thanks some asynctask ( PostCommentAsyncTask(), AddFriendAsyncTask() etc...), so if the accessToken is valid it's ok, but if it has expired I call another AsyncTask (GetRefreshTokenAsyncTask()) from the onPostExecute() method of the precedent AsyncTask to get new accessToken. Here is the tricky part for me. When I get the new access Token I want to re-execute the initial AsyncTask request to the server. I can't figure out how to do it properly.
example1 :
request PostCommentAsyncTask() --> (acessToken expired) -->GetRefreshTokenAsyncTask()-->request PostCommentAsyncTask()--> (good token)--> Ok
EDIT:
I finally chose to use the Volley library ( no need to use Asynctask anymore ).
As I use JSON Web Token I can check the expire date wich is encoded in the payload of the token.
Here is the isAccessTokenExpired() method to check if the Access Token is not expired before making a request to the server :
public Boolean isAccessTokenExpired(String accessToken){
String[] accessTokenPart = accessToken.split("\\.");
String header =accessTokenPart[0];
String payload =accessTokenPart[1];
String signature =accessTokenPart[2];
try {
byte[] decodedPayload = Base64.decode(payload, Base64.DEFAULT);
payload = new String(decodedPayload,"UTF-8");
} catch(UnsupportedEncodingException e) {
e.printStackTrace();
}
try {
JSONObject obj = new JSONObject(payload);
int expireDate = obj.getInt("exp");
Timestamp timestampExpireDate= new Timestamp( expireDate);
long time = System.currentTimeMillis();
Timestamp timestamp = new Timestamp(time);
return timestamp.after(timestampExpireDate);
} catch (JSONException e) {
e.printStackTrace();
return true;
}
}
And here is the refreshJsonWebToken() method to get a new pair of Access token/Refresh token from my OAUTH2 server:
public void refreshJsonWebToken(){
SharedPreferences settings = getActivity().getSharedPreferences(PREFS_NAME, 0);
String refreshToken = settings.getString("refreshToken", null);
final HashMap<String, String> params = new HashMap<String, String>();
params.put("grant_type","refresh_token");
params.put("client_id","client");
params.put("refresh_token",refreshToken);
JsonObjectRequest req = new JsonObjectRequest(URL_OAUTH2, new JSONObject(params), new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
String newRefreshToken = response.getString("refresh_token");
SharedPreferences settings = getActivity().getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putString("accessToken", newAccessToken);
editor.putString("refreshToken", newRefreshToken);
editor.apply();
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("grid", "Error: " + error.getMessage());
}
}
});
AppController.getInstance().addToRequestQueue(req);
}
And finnally the getPost() method where I use the precedent methods :
private void getPost(String latitude, String longitude) {
SharedPreferences settings = getActivity().getSharedPreferences(PREFS_NAME, 0);
String accessToken = settings.getString("accessToken", null);
final HashMap<String, String> params = new HashMap<String, String>();
params.put("action", "getLocalPosts");
params.put("latitude", latitude);
params.put("longitude", longitude);
if (isAccessTokenExpired(accessToken)){
refreshJsonWebToken();
}
settings = getActivity().getSharedPreferences(PREFS_NAME, 0);
accessToken = settings.getString("accessToken", null);
JsonObjectRequest req = new JsonObjectRequest(URL_APP+accessToken, new JSONObject(params), new Response.Listener<JSONObject>() {
//Some code ....
});
AppController.getInstance().addToRequestQueue(req);
}
I think Handler is better in this case because Looper has synchronous message queue which is convenient here. You create a HandlerThread and associate your Handler with it. Then you can call postRunnable on it depending on your needs, e.g. you add PostCommentRunnable, if token has expired you add GetRefreshTokenRunnable and PostCommentRunnable, - they will be executed sequentially.
If you still want to go with AsyncTasks, can you check whether token has expired before launching PostCommentAsyncTask? I think that will a be better design. If you can't, then you can execute them one after another because they work on the same background thread by default, e.g.:
new PostCommentAsyncTask().execute();
class PostCommentAsyncTask extends AsyncTask {
//...
onPostExecute() {
if (tokenExpired) {
new GetRefreshTokenAsyncTask().execute();
new PostCommentAsyncTask().execute(); // this guy will wait till refresh token is executed.
}
}
}