I want to register a device with my Wordpress plugin. For this the Plguin uses the following code.
Sample request for new registration:
POST /pnfw/register/ HTTP/1.1
Host: yoursite
Content-Length: 26
Content-Type: application/x-www-form-urlencoded
token=new_device_id&os=iOS
Returns:
200 OK: on success.
401 Unauthorized: if OAuth is enabled but the signature is invalid (see below).
404 Not Found: if prevToken is not found on updating registration.
500 Internal Server Error: on missing mandatory parameters, unknown operating system, or general failure.
On errors:
On errors, besides the HTTP status codes defined above, a JSON is returned with this format:
This is the class the device registers with Firebase.
public class MyFirebaseInstanceIdService extends FirebaseInstanceIdService {
private static final String REG_TOKEN = "REG_TOKEN";
#Override
public void onTokenRefresh() {
String recent_token = FirebaseInstanceId.getInstance().getToken();
Log.d(REG_TOKEN,recent_token);
sendRegistrationToServer(recent_token);
}
public void sendRegistrationToServer(String recent_token) {
}
}
I can not get the data from "recent_token" to my server.
I tried the whole day.
public void sendRegistrationToServer(String recent_token) throws IOException {
String result = null;
HttpURLConnection urlConnection = null;
try {
String postData = "token=" + recent_token + "&os=Android";
byte[] postDataBytes = postData.getBytes("UTF-8");
URL url = new URL("http://www.bluestarfish.de/pnfw/register/");
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setConnectTimeout(30000);
urlConnection.setReadTimeout(30000);
urlConnection.setRequestMethod("POST");
urlConnection.setDoOutput(true);
urlConnection.setRequestProperty("POST", "/pnfw/register/ HTTP/1.1");
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("charset", "UTF-8");
urlConnection.setRequestProperty("Host", "www.bluestarfish.de");
urlConnection.setRequestProperty("Content-Length", Integer.toString(postDataBytes.length));
OutputStream out = urlConnection.getOutputStream();
out.write(postDataBytes);
out.close();
//result = readStream(urlConnection.getInputStream());
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
//return result;
}
Where is the failure
Unfortunately, nothing happens. Can someone explain the mistake to me?
Related
I am having trouble developing a Android app that I want to authenticate with Django REST framework to securely access information. I am being successfully issued a REST token but IsAuthenticated remains false for all of my views.
In Django, I have a class based view that responds if both authentication.TokenAuthentication permissions.IsAuthenticated are valid:
class TestAuthView(APIView):
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
def get(self, request, format=None):
return GetRestData()
In Android, I acquire a token by POSTing my uname and passwd to the default url: /rest-auth/login/which responds with token: {"key":"c03c1238ab99d91301d34567bda9d417d2b48c0c"}
public static String getResponseFromHttpUrl(String... params) throws IOException {
ArrayList<AbstractMap.SimpleEntry<String,String>> paramssss = new ArrayList<>();
paramssss.add(new AbstractMap.SimpleEntry<>("username", "root"));
paramssss.add(new AbstractMap.SimpleEntry<>("password", "mypass"));
URL url = new URL(params[0]);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setReadTimeout(3000);
urlConnection.setConnectTimeout(3000);
urlConnection.setRequestMethod("POST");
urlConnection.setDoInput(true);
urlConnection.setDoOutput(true);
OutputStream os = urlConnection.getOutputStream();
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(os, "UTF-8"));
writer.write(getQuery(paramssss));
writer.flush();
writer.close();
os.close();
urlConnection.connect();
try {
InputStream in = urlConnection.getInputStream();
Scanner scanner = new Scanner(in);
scanner.useDelimiter("\\A");
boolean hasInput = scanner.hasNext();
if (hasInput) {
return scanner.next(); //eg. {"key":"c03c1238ab99d91301d34567bda9d417d2b48c0c"}
} else {
return null;
}
} finally {
urlConnection.disconnect();
}
}
I then store the token and later use it to request some data:
#Override
protected String doInBackground(String... sUrl) {
try {
URL url = new URL(sUrl[0]);
URLConnection urlConnection = url.openConnection();
String authToken = "c03c1238ab99d91301d34567bda9d417d2b48c0c"; //just use a constant string for now..
urlConnection.addRequestProperty("Authorization", "Token " + authToken);
urlConnection.addRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
urlConnection.connect();
...
process the response
...
Looking at the Django logs I see that login succeeds but the GET request fails with HTTP_401_UNAUTHORIZED:
[08/Oct/2019 22:18:53] "POST /rest-auth/login/ HTTP/1.1" 200 50
[08/Oct/2019 22:18:53] "GET /update/ HTTP/1.1" 401 58
When I change the permission_classes to AllowAny:
class TestAuthView(APIView):
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.AllowAny,) //Changed this!!!
def get(self, request, format=None):
return GetRestData()
The response contains the expected REST data and everything succeeds:
[08/Oct/2019 22:24:57] "POST /rest-auth/login/ HTTP/1.1" 200 50
[08/Oct/2019 22:25:02] "GET /update/ HTTP/1.1" 200 19451876
I don't understand how I should properly authenticate my Android app so that IsAuthenticated will not always be False?
Currently I submit a username and password to /rest-auth/login/ and am issued a rest token. But must I also login somewhere else to get a CSRF token and use that as well?
I am not familiar with the need for permissions.IsAuthenticated and if it is even valid for Android apps? I mean do I just leave the permission as AllowAny for non-browser Android apps? I feel it's a bad idea..?
I've been plugging at this for a few days and would kindly appreciate any help!
I'm trying to connect to a web API using a URL. However, I get a 301 error from the server (Moved Permanently), although the provided URL works very well with no errors when I try it in my browser.
Here is the code that builds the URL:
public Loader<List<Earthquake>> onCreateLoader(int i, Bundle bundle) {
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
String minMagnitude = sharedPrefs.getString(
getString(R.string.settings_min_magnitude_key),
getString(R.string.settings_min_magnitude_default));
String orderBy = sharedPrefs.getString(
getString(R.string.settings_order_by_key),
getString(R.string.settings_order_by_default)
);
Uri baseUri = Uri.parse(USGS_REQUEST_URL);
Uri.Builder uriBuilder = baseUri.buildUpon();
uriBuilder.appendQueryParameter("format", "geojson");
uriBuilder.appendQueryParameter("limit", "10");
uriBuilder.appendQueryParameter("minmag", minMagnitude);
uriBuilder.appendQueryParameter("orderby", orderBy);
Log.i ("the uri is ", uriBuilder.toString());
return new EarthquakeLoader(this, uriBuilder.toString());
}
Here is the code that tries to connect to the resource represented by the URL:
private static String makeHttpRequest(URL url) throws IOException {
String jsonResponse = "";
// If the URL is null, then return early.
if (url == null) {
return jsonResponse;
}
Log.i("The received url is " , url +"");
HttpURLConnection urlConnection = null;
InputStream inputStream = null;
try {
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setReadTimeout(10000 /* milliseconds */);
urlConnection.setConnectTimeout(15000 /* milliseconds */);
urlConnection.setRequestMethod("GET");
urlConnection.connect();
// If the request was successful (response code 200),
// then read the input stream and parse the response.
if (urlConnection.getResponseCode() == 200) {
inputStream = urlConnection.getInputStream();
jsonResponse = readFromStream(inputStream);
} else {
Log.e(LOG_TAG, "Error response code: " + urlConnection.getResponseCode()); //this log returns 301
}
} catch (IOException e) {
Log.e(LOG_TAG, "Problem retrieving the earthquake JSON results.", e);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (inputStream != null) {
// Closing the input stream could throw an IOException, which is why
// the makeHttpRequest(URL url) method signature specifies than an IOException
// could be thrown.
inputStream.close();
}
}
return jsonResponse;
}
I could know that the connection returns status code of 301 from the log provided in the case when the status code is not 200. I have also logged the generated URL, I copied it from the logcat and tried it in my browser and it worked well. Here is the built URL: http://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&limit=10&minmag=6&orderby=magnitude
I checked this question: Android HttpURLConnection receives HTTP 301 response code but it wasn't clear to me what is the solution for the problem.
Can you please help me identify and solve the problem?
UPDATE: As greenapps indicated in his comment, the connection is done through https. That comment identified the problem and helped me fix the code.
In my code, the string I used to build the basic URL, had the protocol value as http not https, it was:
private static final String USGS_REQUEST_URL =
"http://earthquake.usgs.gov/fdsnws/event/1/query";
After reading greenapps comment, I just changed the protocol part in the string to https, so it became:
private static final String USGS_REQUEST_URL =
"https://earthquake.usgs.gov/fdsnws/event/1/query";
That solved the problem.
Thanks.
If you click your http link here you will see that the browser shows a https page. You better use that url directly as there is redirection now.
This is because the address http to https transferred.
To avoid this, you need to convert the request address to https.
I'm trying to implement Firebase Topic Messaging in an Android application, and I'm attempting to build a HTTP post request, and I'm receiving a response code of 400. I have looked at various solutions but none of them have seemed to help.
Here is where I call the subclass of AsyncTask:
try{new FirebaseSendMessage().execute("Hello world");}
catch (Exception e) {
Log.d("Exception", e.toString());
}
Here is my Async Task class's subclass.
class FirebaseSendMessage extends AsyncTask<String, Integer, Double> {
private final static String USER_AGENT = "Mozilla/5.0";
private final static String AUTH_KEY = "<My firebase authorization key obtained from firebase>";
private Exception exception;
protected Double doInBackground(String... params) {
try {
sendRequest(params);
} catch (Exception e) {
this.exception = e;
}
return null;
}
protected void onPostExecute(Long l) {
// TODO: check this.exception
// TODO: do something with the feed
}
public void sendRequest(String... params) {
try {
String urlString = "https://fcm.googleapis.com/fcm/send";
URL url = new URL(urlString);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDoOutput(true);
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/json");
con.setRequestProperty("Authorization", "key=" + AUTH_KEY);
String postJsonData = "{\"to\": \"/topics/news\"\"data\": {\"message\": \"This is a Firebase Cloud Messaging Topic Message!\"}";
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.writeBytes(postJsonData);
wr.flush();
wr.close();
int responseCode = con.getResponseCode();
System.out.println("POST Response Code :: " + responseCode);
if (responseCode == HttpURLConnection.HTTP_OK){
System.out.println("succeeded");
}
/*InputStream is = con.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
//con.disconnect();*/
}
catch(IOException e){
Log.d("exception thrown: ", e.toString());
}
}
}
Error: I/System.out: POST Response Code :: 400
Please let me know if there are additional code snippets required to help me debug. Thanks in advance!
Error 400 means an Invalid JSON in your request:
Check that the JSON message is properly formatted and contains valid fields (for instance, making sure the right data type is passed in).
In your sendRequest, you missed a comma (,) between "news\" and \"data\" and a closing bracket (}):
String postJsonData = "{\"to\": \"/topics/news\"\"data\": {\"message\": \"This is a Firebase Cloud Messaging Topic Message!\"}";
which looks like this:
{"to": "/topics/news/""data":{"message":"...."}
Should be:
String postJsonData = "{\"to\": \"/topics/news\", \"data\": {\"message\": \"This is a Firebase Cloud Messaging Topic Message!\"}}";
So that the JSON structure would be correct:
{"to": "/topics/news/",
"data":{"message":"..."}
}
For those who are willing to use the authentication key in your application.
I suggest to encrypt the key manually by the SHA-1 of your application and the decrypt it at the time of runtime with the SHA-1 code.
I have a server using mongodb, mongoose and node.js.
I have implemented some GET and POST methods.
Inside a HTML website, I can post data to the server within an XMLHttpRequest as follows inside javascript:
function postPlantType(base64){
var httpPost = new XMLHttpRequest(),
path = "http://...", // real URL taken out here
header = ('Content-Type','application/json'),
data = JSON.stringify({image:base64});
httpPost.onreadystatechange = function(err) {
if (httpPost.readyState == 4 && httpPost.status == 201){
console.log(httpPost.responseText);
} else {
console.log(err);
}
};
path = "http://..." // real URL taken out here
httpPost.open("POST", path, true);
httpPost.send(data);
}
this works fine. Now I want to create an Android app, making use of such a POST request, but my Code is not working successfully. Here is my Code:
private class PostNewPlantTask extends AsyncTask<String, Integer, String> {
String responseString = "";
int response;
InputStream is = null;
protected String doInBackground(String... urls){
DataOutputStream wr=null;
try {
URL url = new URL(urls[0]); // urls[0] is the url of the http request "http://www..."
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
String json = "{\"image\":\"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYE...\"}";
Log.d("json", json.toString());
conn.setRequestProperty("Content-length", json.getBytes().length + "");
conn.setDoInput(true);
conn.setUseCaches(false);
conn.setAllowUserInteraction(false);
OutputStream os = conn.getOutputStream();
os.write( json.getBytes("UTF-8"));
os.close();
// Starts the query
conn.connect();
response = conn.getResponseCode();
if (response >= 200 && response <=399){
is = conn.getInputStream();
} else {
is = conn.getErrorStream();
}
// Convert the InputStream into a string
String contentAsString = readIt(is, 200);
responseString = contentAsString;
conn.disconnect();
} catch (Exception e) {
responseString = "error occured: "+e;
} finally {
if (is != null){
try { is.close();} catch (Exception e) {Log.d("HTTP POST planttypes","Exception occured at closing InputStream: "+e);}
}
}
Log.d("HTTP POST plants", "The response is: " + response + responseString);
return responseString;
}
protected void onPostExecute(String result){
// TODO: nothing(?)
// give user feedback(?)
}
}
NOTE: If I change the json String to invalid json content e.g. deleting the last "}", The response of the server is
400 "code":"InvalidContent","message":"Invalid JSON: Unexpected end of input"
So I assume the entire json string must be correct, if its unchanged.
I am hardcoding the base64encoded image String here instead of encode a real image, because of testing issues. You can see the image at this jsfiddle.
If I see it correctly, its the exact same request as done from my javascript, but I get 500 internal server error.
However, in order to get more information, here is the server function, that is called for that request url:
function postNewPlantType(req, res, next){
var json = JSON.parse(req.body);
newPlantTypeData = {
image:json.image
};
var imageBuffer = decodeBase64Image(json.image);
newPlantType = new Planttype(newPlantTypeData);
newPlantType.save(function(err){
if (err) return next(new restify.InvalidArgumentError(JSON.stringify(err.errors)));
var fileName = cfg.imageFolder + "" + newPlantType._id + '.jpeg';
fs.writeFile(fileName, imageBuffer.data, function(error){
if (error) log.debug(error);
log.debug("PlantType-ImageFile successfully created on server.");
});
res.send(201, newPlantType);
log.debug("PlantType successfully saved in database.");
});
}
What I am wondering about is, the javascript request is working, but the android request is not. So I assume there must be a mistake in my android code. Can you help me and explain, what the error is and what I have to change?
You probably need to encode it properly:
conn.connect();
DataOutputStream printout = new DataOutputStream(conn.getOutputStream ());
printout.write(URLEncoder.encode(json.toString(),"UTF-8"));
printout.flush ();
printout.close ();
response = conn.getResponseCode();
...
After lots of days of investigation I finally got a 201 response by changing the line
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
to
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
Well.. I was sending an encoded JSON and not a json itself...
I have this code so far:
private class DownloadWebPageTask extends AsyncTask<String, Void, String>
{
#Override
protected String doInBackground(String... theParams)
{
String myUrl = theParams[0];
String myEmail = theParams[1];
String myPassword = theParams[2];
HttpPost post = new HttpPost(myUrl);
post.addHeader("Authorization","Basic "+ Base64.encodeToString((myEmail+":"+myPassword).getBytes(), 0 ));
ResponseHandler<String> responseHandler = new BasicResponseHandler();
String response = null;
try
{
response = client.execute(post, responseHandler);
InputStream content = execute.getEntity().getContent();
BufferedReader buffer = new BufferedReader(
new InputStreamReader(content));
String s = "";
while ((s = buffer.readLine()) != null)
{
response += s;
}
}
catch (Exception e)
{
e.printStackTrace();
}
return response;
}
#Override
protected void onPostExecute(String result)
{
}
}
This code does not compile because I am running into confusion at the point of:
response = client.execute(post, responseHandler);
InputStream content = execute.getEntity().getContent();
I got that code from tinkering with various examples, and not sure what Object the client is supposed to be, and whether the first line will just get me the server response, or I have to go the route of getting the InputStream and reading the server response in?
Please help me understand how to do this correctly.
Thank you!
I have managed to use Digest authentication using OkHttp. In this code sample I also use Dagger and Robospice-retrofit. What I did was creating an OkHttp Authenticator and assign it to my custom OkHttp client.
The authenticator class implements an authenticate method that will be called whenever the server encounters a 401 error and expects an Authorization header back (if it expects Proxy-Authorization you should implement the authenticateProxy method.
What it basically does is wrapping calls to the HttpClient DigestScheme and make it usable for OkHttp. Currently it does not increase the nc counter. This could cause problems with your server as it could be interpreted as a replay attack.
public class DigestAuthenticator implements com.squareup.okhttp.Authenticator {
#Inject DigestScheme mDigestScheme;
#Inject org.apache.http.auth.Credentials mCredentials;
#Override
public Request authenticate(Proxy proxy, Response response) throws IOException {
String authHeader = buildAuthorizationHeader(response);
if (authHeader == null) {
return null;
}
return response.request().newBuilder().addHeader("Authorization", authHeader).build();
}
#Override
public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
return null;
}
private String buildAuthorizationHeader(Response response) throws IOException {
processChallenge("WWW-Authenticate", response.header("WWW-Authenticate"));
return generateDigestHeader(response);
}
private void processChallenge(String headerName, String headerValue) {
try {
mDigestScheme.processChallenge(new BasicHeader(headerName, headerValue));
} catch (MalformedChallengeException e) {
Timber.e(e, "Error processing header " + headerName + " for DIGEST authentication.");
}
}
private String generateDigestHeader(Response response) throws IOException {
org.apache.http.HttpRequest request = new BasicHttpRequest(
response.request().method(),
response.request().uri().toString()
);
try {
return mDigestScheme.authenticate(mCredentials, request).getValue();
} catch (AuthenticationException e) {
Timber.e(e, "Error generating DIGEST auth header.");
return null;
}
}
}
The authenticator will then be used in an OkHttpClient built with a provider:
public class CustomClientProvider implements Client.Provider {
#Inject DigestAuthenticator mDigestAuthenticator;
#Override
public Client get() {
OkHttpClient client = new OkHttpClient();
client.setAuthenticator(mDigestAuthenticator);
return new OkClient(client);
}
}
Finally the client is set to the RetrofitRobospice server in the function createRestAdapterBuilder:
public class ApiRetrofitSpiceService extends RetrofitJackson2SpiceService {
#Inject Client.Provider mClientProvider;
#Override
public void onCreate() {
App.get(this).inject(this);
super.onCreate();
addRetrofitInterface(NotificationRestInterface.class);
}
#Override
protected String getServerUrl() {
return Constants.Url.BASE;
}
#Override
protected RestAdapter.Builder createRestAdapterBuilder() {
return super.createRestAdapterBuilder()
.setClient(mClientProvider.get());
}
}
You might want to switch to HttpURLConnection. According to this article its API is simpler than HttpClient's and it's better supported on Android. If you do choose to go with HttpURLConnection, authenticating is pretty simple:
Authenticator.setDefault(new Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("username", "password".toCharArray());
}
});
After that, continue using HttpURLConnection as usual. A simple example:
final URL url = new URL("http://example.com/");
final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
final InputStream is = conn.getInputStream();
final byte[] buffer = new byte[8196];
int readCount;
final StringBuilder builder = new StringBuilder();
while ((readCount = is.read(buffer)) > -1) {
builder.append(new String(buffer, 0, readCount));
}
final String response = builder.toString();
The version of Apache's HttpClient shipped with Android is based on an old, pre-BETA version of HttpClient. Google has long recommended against using it and removed it in Android 6.0. Google's replacement HttpURLConnection does not support HTTP digest authentication, only basic.
This leaves you with a few options, including:
Migrate to HttpURLConnection (as Google recommends) and use a library, bare-bones-digest, for digest authentication. Example below.
Use the OkHttp library instead of HttpURLConnection or HttpClient. OkHttp does not support digest out of the box, but there's a library okhttp-digest that implements a digest authenticator. Example below.
Continue using the (deprecated) HttpClient by explicitly adding the 'org.apache.http.legacy' library to your build, as mentioned in the changelist for Android 6.0.
There is an Apache project for porting newer versions of HttpClient to Android, but the project has been discontinued. Read more on Apache's page on HttpClient for Android.
Implement HTTP digest yourself.
Here is a verbose example of how to authenticate a request using bare-bones-digest and HttpURLConnection (copied from the project's github page):
// Step 1. Create the connection
URL url = new URL("http://httpbin.org/digest-auth/auth/user/passwd");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// Step 2. Make the request and check to see if the response contains
// an authorization challenge
if (connection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) {
// Step 3. Create a authentication object from the challenge...
DigestAuthentication auth = DigestAuthentication.fromResponse(connection);
// ...with correct credentials
auth.username("user").password("passwd");
// Step 4 (Optional). Check if the challenge was a digest
// challenge of a supported type
if (!auth.canRespond()) {
// No digest challenge or a challenge of an unsupported
// type - do something else or fail
return;
}
// Step 5. Create a new connection, identical to the original
// one..
connection = (HttpURLConnection) url.openConnection();
// ...and set the Authorization header on the request, with the
// challenge response
connection.setRequestProperty(
DigestChallengeResponse.HTTP_HEADER_AUTHORIZATION,
auth.getAuthorizationForRequest("GET", connection.getURL().getPath()));
}
Here is an example using OkHttp and okhttp-digest (copied from the okhttp-digest page):
client = new OkHttpClient();
final DigestAuthenticator authenticator = new DigestAuthenticator(new Credentials("username", "pass"));
final Map<String, CachingAuthenticator> authCache = new ConcurrentHashMap<>();
client.interceptors().add(new AuthenticationCacheInterceptor(authCache));
client.setAuthenticator(new CachingAuthenticatorDecorator(authenticator, authCache));
Request request = new Request.Builder()
.url(url);
.get()
.build();
Response response = client.newCall(request).execute();