I have written a basic app using Java, Google Web Toolkit and Google Cloud Datastore. For authentication I am using Firebase. When the front end makes a RPC call it passes the user token so that the backend can validate it.
I would like to create a VerifyToken class in the backend, which will receive a user token, call the Firebase verifyIdToken to verify it, then return the user uid or 0 if the user token has not been successfully verified. The class which receives the RPC call will then use the uid to get data and return it in the RPC response.
Here's the current code:
public class VerifyToken
{
public String verify(String token)
{
String uid = "0";
try
{
//Connect to Firebase
FirebaseOptions options = new FirebaseOptions.Builder()
.setServiceAccount(new FileInputStream("firebaseJsonHere"))
.setDatabaseUrl("dbUrlHere")
.build();
FirebaseApp.initializeApp(options);
//Verify the token
FirebaseAuth.getInstance().verifyIdToken(token)
.addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
#Override
public void onSuccess(FirebaseToken decodedToken) {
String uid = decodedToken.getUid();
System.out.println("uid decoded = " + uid);
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(Exception e)
{
System.out.println(e.getMessage());
}
});
}catch(Exception e){
System.out.println("Exception: " + e.getMessage());
}
//Return the uid or 0 if not validated
return uid;
}
}
Please could someone let me know how to get the result from the SuccessListener. I can see that the uid is successfully decoded, just not sure how to get the result back.
Many thanks,
Ed
I guess you can do:
public String verify(String token)
{
String uid = "0";
try
{
//Connect to Firebase
FirebaseOptions options = new FirebaseOptions.Builder()
.setServiceAccount(new FileInputStream("firebaseJsonHere"))
.setDatabaseUrl("dbUrlHere")
.build();
FirebaseApp.initializeApp(options);
//Verify the token
FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(token).getResult();
uid = decodedToken.getUid();
}catch(Exception e){
System.out.println("Exception: " + e.getMessage());
}
//Return the uid or 0 if not validated
return uid;
}
I am not a huge fan of String uid = "0"; though, setting it to null instead sounds better to me.
Related
The FCM Token ID has been generated and I want it to send to the PHP server and then store it in a variable. What should be the approach?
#Override
public void onNewToken(String token) {
Log.d(TAG, "Refreshed token: " + token);
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// Instance ID token to your app server.
sendRegistrationToServer(token);
}
private void sendRegistrationToServer(String token) {
}
PHP Code
<?php
$token = $_POST["tokenid"];
echo ($token);
?>
You can store you FCM-Id in Preference and then pass this FCM-Id to backend pass it as a parametr using API calling. here below i'm get FCM-Id and pas to PHP using API.
MyFirebaseInstanceIDService.java
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
private static final String TAG = "MyFirebaseIIDService";
Context context;
/**
* Called if InstanceID token is updated. This may occur if the security of
* the previous token had been compromised. Note that this is called when the InstanceID token
* is initially generated so this is where you would retrieve the token.
*/
// [START refresh_token]
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
context = getApplicationContext();
AppPreference.setStringPref(context, AppPreference.PREF_SIGNUP_FCM_ID, AppPreference.PREF_KEY.PREF_KEY_FCM_ID,
refreshedToken);
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// Instance ID token to your app server.
sendRegistrationToServer(refreshedToken);
}
// [END refresh_token]
/**
* Persist token to third-party servers.
* <p>
* Modify this method to associate the user's FCM InstanceID token with any server-side account
* maintained by your application.
*
* #param token The new token.
*/
private void sendRegistrationToServer(String token) {
// TODO: Implement this method to send token to your app server.
Map<String, String> params = new HashMap<String, String>();
String device_id = Common.getDeviceId(this);
params.put(FCM_TOKEN, token);
params.put(DEVICEID, device_id);
params.put(DEVICE_TYPE, device_type);
JsonObjectRequest request = new JsonObjectRequest(FCM_TOKEN_URL, new JSONObject(params),
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
parseJsonPersonalDetail(response);
} catch (Exception e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
if (error.networkResponse != null) {
int statusCode = error.networkResponse.statusCode;
NetworkResponse response = error.networkResponse;
Log.d("testerror", "" + statusCode + " " + response.data);
}
}
}) {
#Override
public Map<String, String> getHeaders() {
Map<String, String> headers = new HashMap<String, String>();
headers.put("User-agent", "Mozilla/5.0 (TV; rv:44.0) Gecko/44.0 Firefox/44.0");
return headers;
}
};
Common.setVolleyConnectionTimeout(request);
ApplicationClass.getInstance().getRequestQueue().add(request);
}
/**
* <b>Description</b> - Get back response for calling callUserDetailSave API
*
* #param jsonObject - Pass API response
*/
private void parseJsonPersonalDetail(JSONObject jsonObject) {
try {
Log.i("get response", "get response" + jsonObject);
if (jsonObject.toString().contains(Constant.JSON_KEY.MSG)) {
String message = jsonObject.getString(Constant.JSON_KEY.MSG);
String status = jsonObject.getString(Constant.JSON_KEY.CODE);
}
} catch (Exception e) {
e.getStackTrace();
}
}
}
Here first i'm get FCM id then call API method sendRegistrationToServer and pass token in API as a paramter so back-end developer get this token from API parameter.
Here i'm pass three parameters
params.put(FCM_TOKEN, token);
params.put(DEVICEID, device_id);
params.put(DEVICE_TYPE, device_type);
device_id and device_type pass because it's my requirment.
Add dependency in app level gradle file for calling Volley API call :
implementation 'com.android.volley:volley:1.1.0'
Checkout i'm created Demo for you: Demo
Volley Libraries Example :
Tutorial 1
Tutorial 2
Tutorial 3
I am using google firebase auth method to authenticate users I am fetching users profile info using firebase method and saving it on server.
When I am trying to save users image uri on server it is showing null value.
Here is my code below:
FirebaseAuth fAuth = FirebaseAuth.getInstance();
FirebaseUser user = fAuth.getCurrentUser();
if(user != null){
saveData();
}
private void saveData(){
final String uid = user.getUid();
final String email = user.getEmail();
final Uri profileImage = user.getPhotoUrl();
final String name = user.getDisplayName();
RequestQueue requestQueue = Volley.newRequestQueue(BookForm.this);
StringRequest stringRequest = new StringRequest(Request.Method.POST, URL, new Response.Listener<String>(){
#Override
public void onResponse(String response) {
if(response.equals("Successfully inserted")){
Toast.makeText(getApplicationContext(),"Successful",Toast.LENGTH_SHORT.show();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
NetworkResponse status = error.networkResponse;
Toast.makeText(getApplicationContext(),"Error:"+status,Toast.LENGTH_SHORT).show();
}
}){
#Override
protected Map<String, String> getParams() throws AuthFailureError {
HashMap<String,String> map = new HashMap<>();
map.put("userId",uid);
map.put("email",email);
map.put("name",name);
map.put("profileImage", String.valueOf(profileImage));
return map;
}
};
requestQueue.add(stringRequest);
}
This is how I am sending data to the server.
router.post('/addData', (req,res) => {
var data = {
User_id: req.body.userId,
Email:req.body.email,
Profile_image:req.body.profileImage,
Name: req.body.name
};
MongoClient.connect(dburl, {useNewUrlParser:true} ,(err,client) => {
if(err){
console.log("Error".red, +err);
}
else{
var collect = client.db('my_db').collection('Example');
collect.insertOne(data, (err,resp) =>{
if(err){
console.log("Error".red, +err);
}
else{
res.send("Successfully inserted");
}
client.close();
});
}
});
});
Someone please let me know why image uri is showing null after saved to server. Any help would be appreciated.
THANKS
There is no requirement that the authentication provider must give you an image URL for the user. If the provider doesn't have one (maybe because the user never provided an image), then you will get null. Your code should be able to handle this case.
Note that the API documentation for getPhotoUrl() specifically says:
This field will be automatically populated on account creation if the
AuthCredential used on signInWithCredential(AuthCredential) contained
such information.
Right now I am sending a confirmation link containing JWT token created for user to recipients mail address. This token is saved in a database column.
The activation link looks like this:
http://localhost:7070/RTH_Sample14/eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvcnRoLmNvbSIsInN1YiI6IlJUSCIsInJvbGUiOiJVU0VSIiwiZXhwIjoxNDU2MjQ1MzM2LCJlbWFpbCI6Imtpcml0aS5rOTk5QGdtYWlsLmNvbSJ9.RJ54PhKcj9GGMq_VefQEMhY0x38wX1t5GgMldHCRmBZsPKoXAYg5vr39aXjHtKmIDsoqmDdzzjsrEIweWEATg3-jGe_PGfxwKZg1zsKiWlpavvKJn92VgffJi1yO54t-H31n2NKjVhAcay34pf3eUNqpPcDCEz9uf_GwSZl1ZTM
When the user clicks on the link, I want to be able to call a restful resource (#Path("/public/emailActivation")) which checks for token in database and change the account-status column from "pending" to "active"
The problem is how would I point the activation-link to rest resource method when clicked?
Is this the right approach to activate/verify user?
Rest Registration Service:
#Path("/public/registrationService")
public class RegistrationService {
public RegistrationService() {
}
#POST
#Path("/register")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response register(UserProfile user) {
String email = user.getEmail();
String token = "";
try {
RegistrationDAO registrationDao = new RegistrationDAO();
int count = registrationDao.insert(user);
if (count > 0) {
System.out.println("Registration successful");
TokenProvider jwtProvider = new TokenProvider();
token = jwtProvider.getToken(email);
Map<String, String> response = new HashMap<>();
response.put("token", token);
SendEmailConfirmation mailActivation = new SendEmailConfirmation();
mailActivation.sendMail(email, "http://localhost:7070/RTH_Sample14/"+token);
return Response.ok(response).build();
}
else {
return Response.status(Response.Status.UNAUTHORIZED).type("text/plain").entity("Registration failed!").build();
}
} catch (Exception e) {
e.printStackTrace();
return Response.status(Response.Status.NOT_FOUND).type("text/plain").entity("Error in Login Service web service class").build();
}
}
}
This method should be called for mail activation when user clicks on link:
#Path("/public/emailActivation")
public class EmailActivation {
#Path("/activate")
public void activateAccount(){
//Check Database for token and account status
}
}
I'm writing an android application which uses rest services for user regitration and more but running into trouble with my login service. for some reason the requestparams i put into my service call on android side are not being found within my rest service.
could anny 1 tell me what i'm doing wrong or link to a guide which explains how to solve this problem?
Relevant android functions:
public void loginUser(View view) {
// Get username and password values
String username = usernameEdit.getText().toString();
String password = passwordEdit.getText().toString();
// Instantiate Http Request Param Object
RequestParams params = new RequestParams();
// Check if username & password is not null
if(Utility.isNotNull(username) && Utility.isNotNull(password)) {
// Http parameters
params.put("username", username);
params.put("password", password);
invokeWS(params);
} else {
Toast.makeText(getApplicationContext(), "Vul een gebruikersnaam en of " +
"wachtwoord in", Toast.LENGTH_LONG).show();
}
}
// Method that performs RESTful webservice invocations
public void invokeWS(RequestParams params) {
// Make RESTful webservice call using AsyncHttpClient object
AsyncHttpClient client = new AsyncHttpClient();
client.post("http://10.0.2.2:8080/NTR_application/rest/session", params, new AsyncHttpResponseHandler() {
// When the response returned by REST has Http response code '200'
#Override
public void onSuccess(String response) {
Toast.makeText(getApplicationContext(), "You are successfully logged in!" + response, Toast.LENGTH_LONG).show();
// Gets an JSON object with user Data
// Write user Data to SQLite
User user = new Gson().fromJson(response, User.class);
db.addUser(user);
// Navigate to Home screen
navigatetoHomeActivity();
}
// When the response returned by REST has Http response code other than '200'
#Override
public void onFailure(int statusCode, Throwable error,
String content) {
Toast.makeText(getApplicationContext(), "ERROR!" + content + error + statusCode, Toast.LENGTH_LONG).show();
}
});
}
and the rest services which is called :
#Path("/session")
public class UserService {
private Controller controller = new Controller();
#POST //Post so you can't see the information in the browser history easily
#Produces(MediaType.APPLICATION_JSON)
public Response authenticate(#QueryParam("username") String username, #QueryParam("password") String password){
User user = null;
try {
user = controller.authenticate(username, password);
} catch (NoSuchAlgorithmException | SQLException e) {
System.out.println("Authentication caught an exception; failed for: " + username);
e.printStackTrace();
}
if (user != null){
String json = new Gson().toJson(user);
return Response.status(200).entity(json).build();
} else {
return Response.status(401).entity("Username and/or password is incorrect").build();
}
}
}
Mistake was obvious once i saw it, since i use a #POST i need to use #FormParam instead of #QueryParam.
tutorial i used to write these methods used #GET to login which is insecure.
So I have an app which uses Google App Engine and Google Cloud Endpoints as it's backend in Java. I'm currently working on User authentication and here is what I'm trying to do:
When user first opens the app, they'll have option to either "Login through Facebook" or signup using their email address. Then this data would be stored in a user object and after registration would direct them to the app homepage. It will be saved in their preferences so that they don't need to login every time they open the app (if ever).
Now I heard you can use a custom authenticator for Facebook, but there's not much documentation regarding this. How can I get the email registration and Facebook Login options to be implemented with Google Cloud Endpoint's Authenticator? Or should I make a different approach?
Thanks.
My approach is using the Facebook login method (Facebook SDK for Android). The Facebook authentication process returns (on success) an object from which I can get the user's email then I save it in my Endpoints class using Datastore API. To check if user already logged in I chose the SharedPreferences approach with GSON library to parse objects into JSON String and save them in the prefs.
Links and my sample codes below :
Regarding the Authenticator I found this SO answer
More info about Facebook login method
Saving custom objects in SharedPreferences
Getting user's email through Facebook auth
private void onSessionStateChange(Session session, SessionState state, Exception exception) {
if (state.isOpened()) {
if (isSessionCalled == false) {
Log.i(TAG, "Logged in...");
System.out.println("Token=" + session.getAccessToken());
new Request(
session,
"/me",
null,
HttpMethod.GET,
new Request.Callback() {
public void onCompleted(Response response) {
if (response != null) {
GraphObject object = response.getGraphObject();
String email = (String) object.getProperty("email");
Log.i(TAG, "user email : " + email);
String firstName = (String) object.getProperty("first_name");
String lastName = (String) object.getProperty("last_name");
mUserTask = new UserAsyncTask();
mUserTask.execute(email);
}
}
}
).executeAsync();
isSessionCalled = true;
}
else {
Log.w(TAG, "session called twice");
}
}
else if (state.isClosed()) {
Log.i(TAG, "Logged out...");
}
}
Storing the user in my backend :
#ApiMethod(name = "storeUserModel")
public UserModel storeUserModel(UserModel userModel) throws UserAlreadyExistsException, UserNotFoundException {
logger.info("inside storeUser");
String email = userModel.getEmail();
UserModel checkUser = getUserModel(email);
logger.info("after getUserModel with email " + email);
if (checkUser == null) {
logger.info("inside checkUser is NULL");
DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService();
Transaction txn = datastoreService.beginTransaction();
try {
Entity userEntity = new Entity(UserModel.class.getSimpleName(), email);
userEntity.setProperty("nickname", userModel.getNickname());
// TODO save the pheromones with the key of userEntity
datastoreService.put(userEntity);
txn.commit();
storePheromoneList(userModel.getPheromoneList(), userEntity.getKey(), datastoreService);
} finally {
if (txn.isActive()) {
logger.severe("rolled back with email : " + email);
txn.rollback();
}
}
}
else {
throw new UserAlreadyExistsException();
}
return userModel;
}
A class that triggers calls to my backend
public class EndpointsServer implements Server {
private static final String TAG = "EndpointsServer";
final UserModelApi userEndpointsApi;
public EndpointsServer() {
UserModelApi.Builder builder = new UserModelApi.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null)
.setRootUrl("http://10.0.2.2:8080/_ah/api/")
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
#Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
});
userEndpointsApi = builder.build();
}
#Override
public User getUser(String email) {
User user = null;
try {
Log.d(TAG, "in getUser with email " +email);
// get user from db
UserModel userModel = userEndpointsApi.getUserModel(email).execute();
if (userModel != null) {
Log.d(TAG, "user != null with email " + email);
user = new User(userModel);
}
} catch (IOException e) {
e.printStackTrace();
}
return user;
}
}
Storing user on successful login :
String userString = gson.toJson(user, User.class);
SharedPreferences.Editor editor = preferences.edit();
editor.putString(USER_KEY, userString);
editor.commit();
There's more to it like another client side class to build the api call to the backend and lots of other details. I can post it if you want.
I can't speak on Java but I started with Python by looking at this repo on Github:
https://github.com/loudnate/appengine-endpoints-auth-example
This shows you an example on how to write a custom authenticator with Facebook Login. Writing your own authentication I think you should be able to find some examples. The only thing you need to do after is to use the same User entity.
And I suggest you do some reading on how OAUTH 2.0 works so you don't get too confused on the task you need to do.
Basically:
On your client side, whether web or android, get a facebook access token, sends it to your endpoint service. Exchange for a access token of your own. At the same time, create your User object in datastore and associate the access token.
Then all your subsequent request should use this access token to get access to your endpoint backend. (Do a user check on your endpoint API method.)