Hi would like to send an sms verification to users phone numbers.I have done it in ios by calling the rest Api and it works.
But can anyone help me with android I get an error :
code=401, message=UNAUTHORIZED.
Any help would be apreciated?
url = https://{AccountSid}:{AuthToken}#api.twilio.com/2010-04-01/AccountsAccountSid}/SMS/Messages
private OkHttpClient mClient2 = new OkHttpClient();
Call post(String url, Callback callback) throws IOException {
Random rand = new Random();
randomNum = 1000 + rand.nextInt((100000 - 1000) + 1);
RequestBody formBody = new FormBody.Builder()
.add("To", etNumber.getText().toString())
.add("From", "+mynum")
.add("Body", "Your confirmation code for United taxi is" + randomNum)
.build();
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
Call response = mClient2.newCall(request);
System.out.println(request.url());
response.enqueue(callback);
return response;
}
The WWW-Authenticate header is:
WWW-Authenticate: Basic realm="Twilio API"
Twilio Developer Evangelist here.
I actually happen to have written a blog post explaining why you shouldn't make requests like this directly from the device, but have a server in the middle. It will not only make your account more secure, but make it much easier to debug.
Best of all, even if you don't wanna build a backend yourself, my blog post shows you how to deploy one with the click of a button.
Check it out here or get started with your server-side language of choice and finish up with the post.
As for the error you're getting, UNAUTHORIZED implies you're not passing the correct AccountSid & AuthToken. If you still want to go down the route of making the request directly from the device, which again, we don't recommend, I would suggest trying to make the request from something like Postman for example, to make sure you've got your credentials right, and then moving that to the device using OkHttp.
Related
Since few days my app is not able anymore to create a device group from my android app. According to Firebase docs we should be able to manage device groups from server or client side. I didn't change my app from few years! So I guess something is changed without any reference in the docs. Currently my app does:
JSONObject data = new JSONObject();
data.put("operation", "add");
data.put("notification_key_name", notificationKeyName);
data.put("registration_ids", new
JSONArray(Collections.singletonList(registrationId)));
data.put("id_token", idToken);
RequestBody body = RequestBody.create(MEDIA_TYPE_JSON, data.toString());
Request oreq = new
Request.Builder().url("https://fcm.googleapis.com/fcm/googlenotification")
.addHeader("project_id", projectId)
.addHeader("Content-Type", "application/json")
.addHeader("Accept", "application/json")
.post(body)
.build();
where the notification key name is the user email of Google account the token is retrieved using
String idToken = GoogleAuthUtil.getToken(this, account, scope);
and the registration id is retrieved using
FirebaseMessaging.getInstance().getToken()
When the app now sends the request the result is "sender denied" but as I said I didn't change anything. In the Firebase docs however I can't find anymore any reference to the endpoint https://fcm.googleapis.com/fcm/googlenotification so is it changed anything?
I tracked the web page with firebase doc and I saw that the section relative to the client android app management of device groups has been removed one year ago, on 18th March 2020.
I reply to myself: Firebase support finally replied saying that the client support has been removed without any advice. No solution to this problem. Personal note: don't use anymore Firebase products.
I want to send a message to FCM topics from within my Android app. Sending the message through the Firebase console is working well, but once a user executes a particular action, I want a message to be sent to all other users who have subscribed to a particular topic.
In the documentation there is this code:
// The topic name can be optionally prefixed with "/topics/".
String topic = "highScores";
// See documentation on defining a message payload.
Message message = Message.builder()
.putData("score", "850")
.putData("time", "2:45")
.setTopic(topic)
.build();
// Send a message to the devices subscribed to the provided topic.
String response = FirebaseMessaging.getInstance().send(message);
// Response is a message ID string.
System.out.println("Successfully sent message: " + response);
I can't figure out from which class Message is. It is obviously not RemoteMessage.
You can do it by Volley and FCM API
here an example to send notification from user to "newOrder" Topic and have title and body
RequestQueue mRequestQue = Volley.newRequestQueue(this);
JSONObject json = new JSONObject();
try {
json.put("to", "/topics/" + "newOrder");
JSONObject notificationObj = new JSONObject();
notificationObj.put("title", "new Order");
notificationObj.put("body", "New order from : " + phoneNum.replace("+", " "));
//replace notification with data when went send data
json.put("notification", notificationObj);
String URL = "https://fcm.googleapis.com/fcm/send";
JsonObjectRequest request = new JsonObjectRequest(Request.Method.POST, URL,
json,
response -> Log.d("MUR", "onResponse: "),
error -> Log.d("MUR", "onError: " + error.networkResponse)
) {
#Override
public Map<String, String> getHeaders() {
Map<String, String> header = new HashMap<>();
header.put("content-type", "application/json");
header.put("authorization", "key=yourKey");
return header;
}
};
mRequestQue.add(request);
} catch (JSONException e) {
e.printStackTrace();
}
Replace yourKey with server key in your project in firebase
UPDATE :
as #frank say in correct answer You will always need a server (or otherwise trusted environment) to hold yourKey and make it not public
so this answer is already work and can send notifications from android to topic or token
but if anyone take your key can send also to your apps notifications in any time
so i suggest to use firebase functions or any service on your server just make sure your key in trusted environment and not reachable
also when get key there two type :
1 - Server key
2 - Legacy server key
like firebase say below also i suggested to use first because is more flexible to change or deleted
Firebase has upgraded our server keys to a new version. You may
continue to use your Legacy server key, but it is recommended that you
upgrade to the newest version
There is no way to securely send messages directly from one Android device to another device with Firebase Cloud Messaging. You will always need a server (or otherwise trusted environment) to do that. See this docs section showing how messages are sent and my answer. here: How to send one to one message using Firebase Messaging.
The code sample you shared is using the Admin SDK for Java to send a message, which is meant to be run in a trusted environment. It can't be used in your Android app.
Refer this link.. You can do what you need via this official google documentation.
But I can show you an example that I wrote via AndroidFastNetworking library:
JSONObject dataJsonObject = new JSONObject();
dataJsonObject.put("anyDataYouWant":"value");
try {
bodyJsonObject.put("to", "/topics/topic");
bodyJsonObject.put("data", dataJsonObject);
} catch (JSONException e) {
e.printStackTrace();
}
AndroidNetworking.post("https://fcm.googleapis.com/fcm/send")
.setContentType("application/json; charset=utf-8")
.addJSONObjectBody(bodyJsonObject)
.addHeaders("Authorization", "Your Firebase Authorization Key with 'key=' prefix: ("key=AAAAjHK....") ")
.setPriority(Priority.HIGH)
.build()
.getAsJSONObject(new JSONObjectRequestListener() {
#Override
public void onResponse(JSONObject response) {
Toast.makeText(context, "Success", Toast.LENGTH_SHORT).show();
}
#Override
public void onError(ANError anError) {
Toast.makeText(context, "Error: " + anError.toString(), Toast.LENGTH_SHORT).show();
}
});
Here's an implementation using OkHttp 4.x and Kotlin:
// create the payload
val payload = JSONObject()
.put("key", "value")
// create the request body (POST request)
val mediaType = "application/json; charset=utf-8".toMediaType()
val requestBody = JSONObject()
.put("to", "/topics/my_topic")
.put("data", payload)
.toString().toRequestBody(mediaType)
// create request
val request = Request.Builder()
.url("https://fcm.googleapis.com/fcm/send")
.post(requestBody)
.addHeader("Authorization", "key=${server_key_please_replace}")
.addHeader("Content-Type", "application/json")
.build()
// execute the call
val response = OkHttpClient().newCall(request).execute()
val responseBody = response.body?.charStream()?.readLines()
val httpCode = response.code
// error handling goes here, it's an error if the http response code is not 200
// or if the responseBody contains an error message like
// [{"multicast_id":2633602252647458018,"success":0,"failure":1,"canonical_ids":0,"results":[{"error":"InvalidRegistration"}]}]
Security Considerations
It's not recommended to use the server API key on client side since the key can be extracted by an attacker and then used to send messages on behalf of the key owner (that would be you). I would however argue that the risk is quite low under certain circumstances:
Notification messages: the ability to send notifications to all app users is only a risk if those messages can do harm. In my case it's not possible to surface notifications because the app only processes data messages.
Data messages: in my case the app accepts specific topics but only when they are sent from the same user id (I use FCM to sync data for the same user across multiple devices). Without knowing all user ids an attacker could not even send messages. The user ids are impossible to retrieve because they are stored on the device and the user's Google Drive only so unless the Google Drive service as a whole is compromised this is a theoretical risk. On top of that, the data messages are harmless in my case.
The only real risk I see is that an attacker can launch a DOS attack so that Google shuts down access to FCM for your app. If FCM is business critical for your app then that's a real threat. BUT preventing the same attack against your backend API isn't trivial. If you have an API to send messages, that API needs to be protected. Having an endpoint to send messages might protect the FCM key but won't protect against a DOS attack per se. If users are not authenticated (in many apps they aren't) then providing that protection is a non trivial task (bot manager solutions are expensive and implementing mTLS between client and server isn't simple).
To summarize: while it's not recommended to use the server API key on client side, depending on the use case, the security risk is very low. Many apps can't afford to run a backend service just for that one functionality so I would argue using the server key client side can be justified in some cases.
I'm using LoginActivity template and I'm trying to login to a website with email and password using a standard http request. The site doesn't provide an API so I'm thinking of somehow mirroring the site login to fill the email and password boxes on the page then sending the login request.
Think of logging in to stackoverflow for example by taking the input of an email and password TextView (s) and sending a standard http request to the authentication server with those credentials exactly how it would happen in the browser (same requests and addresses).
I haven't done anything like this before and I have no idea if it's even possible so please forgive any ignorance on my part.
This is done in Android in a similar fashion as in the web browser. Namely, you will send a POST request with proper parameters, let's say a JSON Object for the sake of explaining which contains something like:
{
username: 'myUsername'
password: 'mypass'
}
This will get processed and if your credentials are correct, you will get a response which may contain a variety of data, among which the accessToken (it may be called a slight variation of this).
You are supposed to remember this access token and use it to fetch any other data from the site, because that token is used from there on to authenticate you. I have an API I personally made, and I send the accessToken as a parameter in every request for a resource that is unavailable to the unregistered user.
As for the technical side, I'm using a nifty library called OkHttp for sending the Http requests, and it's quite rewarding and easy to use. Here's a code snippet to see what I'm talking about:
//JSON is a media type for parsing json
//json is a json string containing payload e.g. username and pass like in the example
OkHttpClient httpClient = new OkHttpClient();
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = httpClient.newCall(request).execute();
The only thing left to to for you is to properly parse the response. You can find various solutions on this topic, but I personally use 2 approaches, BufferedReader for huge responses using response.body().byteStream(), and plain old String for not-so-large responses using response.body().string().
This is not a short, but very thorough explanation, so feel free to ask for clarification if you do not get some part.
Assuming that you need to log in to sites like StackOverflow from your app with standard http request. That is simply impossible. Because no organizations will allow third party sites/apps handling their users' credentials. If they intend to share their resource with third party most organizations follow this procedure:
First they provide api for you to use.
With that api only you can make users to login i.e you can't handle those credentials
Then they give a token to you corresponding to the user.
With that token you can perform subsequent requests.
If the organization doesn't provide api then they most probably are in situation of not allowing third party sites/apps to access their users' resource.
To send a POST request with the Android library is used OKHTTP. How do I find the time server?
String post(String...url) throws IOException {
FormEncodingBuilder form = new FormEncodingBuilder();
RequestBody formBody =form.build();
Request request = new Request.Builder()
.header("Cookie", x)
.url(url[0])
.post(formBody)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
Time server and server time are different things and you probably want to know how to get server time.
First of all, you need a server that somehow provides time, so possible solutions are to find a webserver or create your own. Use its url to connect to it.
The server can offer time in its response in different ways:
- as a header which has "Date" name
- as a body which you have to know how to parse.
I am using Retrofit like this to get all the books and delete all the books.
#GET("/books")
BookListResponse getAllBooks();
#DELETE("/clean")
Response deleteAllBooks();
But an error status 500 (internal server error) was returned.
I tested these two restful calls using Chrome restful client app and they work properly.
However, if I just want to get one book or delete one book like this
#GET("books/1")
BookResponse getOneBook();
#DELETE("books/1")
Response deleteOneBook();
They can work properly.
So I am not sure if that's the server issue or I have missed something?
This is just a matter of semantics: when you say to your server "Please delete book", but you don't say which book you want to delete, the server don't know what to do (this happens when you send a DELETE to /books). Hence the error 500. But when you say "Please delete book with id 1", now the server knows what to do (this happens when you send a DELETE to /books/1).
It is ok to work that way if you setup your server like this, but I've never seen any REST service to delete ALL records of a particular model. Again, if you coded your server this way it is ok, just make sure the request is reaching the server the way you want.
After all, if you got a error 500, check your server. The problem isn't in the Android side, definitely.
Just add the header accept in the request as follow:
Request request = new Request.Builder()
.url(urlString)
.header("accept", "application/json")
.build();
Update In Retrofit 2.1 the request builder has changed so you have to specify header in the retrofit interface methods #Headers("Accept: application/json")
#GET("products")
Call<Products> getAllProducts();