is there a way to parse claims from an expired JWT token? - java

If we try to parse an expired JWT, results in expired exception.
Is there a way to read claims even the JWT was expired.
Below is used to parse JWT in java:
Jwts.parser().setSigningKey(secret.getBytes()).parseClaimsJws(token).getBody();

There is a better approach to do this.
if you see JWT Exception handler object e.g. ExpiredJwtException, expection object itself contains the following:-
header, claims and message
so claims can easily extracted through this object i.e. e.getClaims().getId() where e is ExpiredJwtException object.
ExpiredJwtException consturct is as follow:-
public ExpiredJwtException(Header header, Claims claims, String message) {
super(header, claims, message);
}
Example:-
try{
// executable code
}catch(ExpiredJwtException e){
System.out.println("token expired for id : " + e.getClaims().getId());
}

JWT objects are Base64URL encoded. This means that you can always read headers and payload by manually Base64URL-decoding it. In this case you will simply ignore exp attribute.
For instance you can do like this (I'm using Java8 built-in Base64 class, but you can use any external library, such as Apache Commons Codec):
Base64.Decoder decoder = Base64.getUrlDecoder();
String src = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImV4cCI6IjEzMDA4MTkzODAifQ.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.2GpoV9q_uguSg0Ku6peI5aZ2qBxO5qOA42zaS25gq_c";
String[] parts = src.split("\\."); // Splitting header, payload and signature
System.out.println("Headers: "+new String(decoder.decode(parts[0]))); // Header
System.out.println("Payload: "+new String(decoder.decode(parts[1]))); // Payload
and the output is:
Headers: {"alg":"HS256","typ":"JWT","exp":"1300819380"}
Payload: {"sub":"1234567890","name":"John Doe","admin":true}
Please note also that the exp attribute is set to 1300819380, which corresponds to 16 january 2016.

this might be old but for anyone whose facing this issue, the java's io.jsonwebtoken
ExpiredJwtException already got the claims in it, you can get it by calling e.getClaims().

If you use io.jsonwebtoken you try my function:
public Claims getClaimsFromToken(String token) {
try {
// Get Claims from valid token
return Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
} catch (ExpiredJwtException e) {
// Get Claims from expired token
return e.getClaims();
}
}

If Someone comes in looking for jose4j library then below works:
invalidJwtException.getJwtContext().getJwtClaims()

Just set the ValidateLifetime property of the TokenValidationParameters to false before calling ValidateToken.
TokenValidationParameters tokenValidationParameters = new TokenValidationParameters();
tokenValidationParameters.ValidateLifetime = false;
JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
ClaimsPrincipal principal = jwtSecurityTokenHandler.ValidateToken(token, tokenValidationParameters, out SecurityToken validatedToken);
Then you can read the claims like this:
string name = principal.Claims.FirstOrDefault(e => e.Type.Equals(ClaimTypes.Name)).Value;
string email = principal.Claims.FirstOrDefault(e => e.Type.Equals(ClaimTypes.Email)).Value;

Related

Saving an object to a Json file as a key [duplicate]

I have this method:
public static Object parseStringToObject(String json) {
String Object = json;
Gson gson = new Gson();
Object objects = gson.fromJson(object, Object.class);
parseConfigFromObjectToString(object);
return objects;
}
And I want to parse a JSON with:
public static void addObject(String IP, Object addObject) {
try {
String json = sendPostRequest("http://" + IP + ":3000/config/add_Object", ConfigJSONParser.parseConfigFromObjectToString(addObject));
addObject = ConfigJSONParser.parseStringToObject(json);
} catch (Exception ex) {
ex.printStackTrace();
}
}
But I get an error message:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:
Expected BEGIN_OBJECT but was STRING at line 1 column 1
Even without seeing your JSON string you can tell from the error message that it is not the correct structure to be parsed into an instance of your class.
Gson is expecting your JSON string to begin with an object opening brace. e.g.
{
But the string you have passed to it starts with an open quotes
"
Invalid JSON from the server should always be an expected use case. A million things can go wrong during transmission. Gson is a bit tricky, because its error output will give you one problem, and the actual exception you catch will be of a different type.
With all that in mind, the proper fix on the client side is
try
{
gson.fromJSON(ad, Ad.class);
//...
}
catch (IllegalStateException | JsonSyntaxException exception)
{
//...
If you want to know why the JSON you received from the server is wrong, you can look inside your catch block at the exception. But even if it is your problem, it's not the client's responsibility to fix JSON it is receiving from the internet.
Either way, it is the client's responsibility to decide what to do when it gets bad JSON. Two possibilities are rejecting the JSON and doing nothing, and trying again.
If you are going to try again, I highly recommend setting a flag inside the try / catch block and then responding to that flag outside the try / catch block. Nested try / catch is likely how Gson got us into this mess with our stack trace and exceptions not matching up.
In other words, even though I'll admit it doesn't look very elegant, I would recommend
boolean failed = false;
try
{
gson.fromJSON(ad, Ad.class);
//...
}
catch (IllegalStateException | JsonSyntaxException exception)
{
failed = true;
//...
}
if (failed)
{
//...
I had a similar problem recently and found an interesting solution. Basically I needed to deserialize following nested JSON String into my POJO:
"{\"restaurant\":{\"id\":\"abc-012\",\"name\":\"good restaurant\",\"foodType\":\"American\",\"phoneNumber\":\"123-456-7890\",\"currency\":\"USD\",\"website\":\"website.com\",\"location\":{\"address\":{\"street\":\" Good Street\",\"city\":\"Good City\",\"state\":\"CA\",\"country\":\"USA\",\"postalCode\":\"12345\"},\"coordinates\":{\"latitude\":\"00.7904692\",\"longitude\":\"-000.4047208\"}},\"restaurantUser\":{\"firstName\":\"test\",\"lastName\":\"test\",\"email\":\"test#test.com\",\"title\":\"server\",\"phone\":\"0000000000\"}}}"
I ended up using regex to remove the open quotes from beginning and the end of JSON and then used apache.commons unescapeJava() method to unescape it. Basically passed the unclean JSON into following method to get back a cleansed one:
private String removeQuotesAndUnescape(String uncleanJson) {
String noQuotes = uncleanJson.replaceAll("^\"|\"$", "");
return StringEscapeUtils.unescapeJava(noQuotes);
}
then used Google GSON to parse it into my own Object:
MyObject myObject = new.Gson().fromJson(this.removeQuotesAndUnescape(uncleanJson));
In Retrofit2, When you want to send your parameters in raw you must use Scalars.
first add this in your gradle:
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:converter-scalars:2.3.0'
public interface ApiInterface {
String URL_BASE = "http://10.157.102.22/rest/";
#Headers("Content-Type: application/json")
#POST("login")
Call<User> getUser(#Body String body);
}
my SampleActivity :
public class SampleActivity extends AppCompatActivity implements Callback<User> {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ApiInterface.URL_BASE)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiInterface apiInterface = retrofit.create(ApiInterface.class);
// prepare call in Retrofit 2.0
try {
JSONObject paramObject = new JSONObject();
paramObject.put("email", "sample#gmail.com");
paramObject.put("pass", "4384984938943");
Call<User> userCall = apiInterface.getUser(paramObject.toString());
userCall.enqueue(this);
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onResponse(Call<User> call, Response<User> response) {
}
#Override
public void onFailure(Call<User> call, Throwable t) {
}
}
Reference: [How to POST raw whole JSON in the body of a Retrofit request?
I have come to share an solution. The error happened to me after forcing the notbook to hang up. possible solution clean preject.
Maybe your JSON Object is right,but the response that you received is not your valid data.Just like when you connect the invalid WiFi,you may received a strange response < html>.....< /html> that GSON can not parse.
you may need to do some try..catch.. for this strange response to avoid crash.
Make sure you have DESERIALIZED objects like DATE/DATETIME etc. If you are directly sending JSON without deserializing it then it can cause this problem.
In my situation, I have a "model", consist of several String parameters, with the exception of one: it is byte array byte[].
Some code snippet:
String response = args[0].toString();
Gson gson = new Gson();
BaseModel responseModel = gson.fromJson(response, BaseModel.class);
The last line above is when the
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column
is triggered. Searching through the SO, I realised I need to have some form of Adapter to convert my BaseModel to and fro a JsonObject. Having mixed of String and byte[] in a model does complicate thing. Apparently, Gson don't really like the situation.
I end up making an Adapter to ensure byte[] is converted to Base64 format. Here is my Adapter class:
public class ByteArrayToBase64Adapter implements JsonSerializer<byte[]>, JsonDeserializer<byte[]> {
#Override
public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return Base64.decode(json.getAsString(), Base64.NO_WRAP);
}
#Override
public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(Base64.encodeToString(src, Base64.NO_WRAP));
}
}
To convert JSONObject to model, I used the following:
Gson customGson = new GsonBuilder().registerTypeHierarchyAdapter(byte[].class, new ByteArrayToBase64Adapter()).create();
BaseModel responseModel = customGson.fromJson(response, BaseModel.class);
Similarly, to convert the model to JSONObject, I used the following:
Gson customGson = new GsonBuilder().registerTypeHierarchyAdapter(byte[].class, new ByteArrayToBase64Adapter()).create();
String responseJSon = customGson.toJson(response);
What the code is doing is basically to push the intended class/object (in this case, byte[] class) through the Adapter whenever it is encountered during the convertion to/fro JSONObject.
Don't use jsonObject.toString on a JSON object.
In my case, I am Returning JSON Object as
{"data":"","message":"Attendance Saved
Successfully..!!!","status":"success"}
Resolved by changing it as
{"data":{},"message":"Attendance Saved
Successfully..!!!","status":"success"}
Here data is a sub JsonObject and it should starts from { not ""
Don't forget to convert your object into Json first using Gson()
val fromUserJson = Gson().toJson(notificationRequest.fromUser)
Then you can easily convert it back into an object using this awesome library
val fromUser = Gson().fromJson(fromUserJson, User::class.java)
if your json format and variables are okay then check your database queries...even if data is saved in db correctly the actual problem might be in there...recheck your queries and try again.. Hope it helps
I had a case where I read from a handwritten json file. The json is perfect. However, this error occurred. So I write from a java object to json file, then read from that json file. things are fine. I could not see any difference between the handwritten json and the one from java object. Tried beyondCompare it sees no difference.
I finally noticed the two file sizes are slightly different, and I used winHex tool and detected extra stuff.
So the solution for my situation is, make copy of the good json file, paste content into it and use.
In my case, my custom http-client didn't support the gzip encoding. I was sending the "Accept-Encoding: gzip" header, and so the response was sent back as a gzip string and couldn't be decoded.
The solution was to not send that header.
I was making a POST request with some parameters using Retrofit in Android
WHAT I FACED:
The error I was getting in Android Studio logcat:
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
at line 2 column 1 path $
[but it was working fine with VOLLY library]
when I googled it...
you know[ Obviously json is expecting a OBJECT but...]
BUT when I changed my service to return a simple string [ like print_r("don't lose hope") ] or
Noting at all
It was getting printed fine in Postman
but in Android studio logcat, it was still SAME ERROR [
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
at line 2 column 1 path $
]
Hold up now, I am sending a simple message or not sending anything in response and still studio is
telling me "...Expected BEGIN_OBJECT but was STRING..."
SOMETHING IS WRONG
On 4th day:
I finally stopped for looking "QUICK SOLUTIONS" and REALLY READ some stack overflow questions
and articles carefully.
WHAT I GOT:
Logging interceptor
It will show you whatever data comes from your server[even eco messages] which are not shown in
Andorid studios logcat,
that way you can FIND THE PROBLEM.
What I found is I was sending data with #Body like-
#Headers("Content-Type: application/json")
#POST("CreateNewPost")
Call<Resp> createNewPost(#Body ParaModel paraModel);
but no parameter was reaching to server, everything was null [I found using Logging interceptor]
then I simply searched an article "how to make POST request using Retrofit"
here's one
SOLUTION:
from here I changed my method to:
#POST("CreateNewPost")
#FormUrlEncoded
Call<Resp> createNewPost(
#Field("user_id") Integer user_id,
#Field("user_name") String user_name,
#Field("description") String description,
#Field("tags") String tags);
and everything was fine.
CONCLUSION:
I don't understand why Retrofit gave this error
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
at line 2 column 1 path $
it doesn't make any sense at all.
So ALWAYS DEBUG in detail then find WHERE THINGS ARE LEAKING and then FIX.
This error solved for by replacing .toString method to .string on the response
toString => string (add in try{...code..}catche(IOException e))
below code is working for me
try {
MainModelResponse model;
Gson gson = new GsonBuilder().create();
if (response.code() == ConstantValues.SUCCESS_OK) {
model = gson.fromJson(response.body().string(), MainModelResponse.class);
} else {
model = gson.fromJson(response.errorBody().string(), MainModelResponse.class);
}
moduleData.postValue(model);
}catch (IllegalStateException | JsonSyntaxException | IOException exception){
exception.printStackTrace();
}
}
use a string begin & end with {}.
such as
final String jsStr = "{\"metric\":\"opentsdb_metric\",\"tags\":{\"testtag\":\"sunbotest\"},\"aggregateTags\":[],\"dps\":{\"1483399261\":18}}";
DataPoint dataPoint = new Gson().fromJson(jsStr, DataPoint.class);
this works for me.
In my case the object was all fine even the Json Validator was giving it a valid resposne but I was using Interface like this
#POST(NetworkConstants.REGISTER_USER)
Call<UserResponse> registerUser(
#Query("name") String name,
#Query("email") String email,
#Query("password") String password,
#Query("created_date") Long creationDate
);
Then I changed the code to
#FormUrlEncoded
#POST(NetworkConstants.REGISTER_USER)
Call<UserResponse> registerUser(
#Field("name") String name,
#Field("email") String email,
#Field("password") String password,
#Field("created_date") Long creationDate
);
And everything was resolved.
my problem not related to my codes
after copy some files from an other project got this issue
in the stack pointed to Gson library
in android studio 4.2.1 this problem not solved when I try file-> invalidate and restart
and
after restart in first time build got same error but in second build this problem solved
I don't understand why this happened
I was using an old version of retrofit library. So what I had to do was to change my code from this after upgrading it to com.squareup.retrofit2:retrofit:2.9.0:
#POST(AppConstants.UPLOAD_TRANSACTION_DETAIL)
fun postPremiumAppTransactionDetail(
#Query("name") planName:String,
#Query("amount") amount:String,
#Query("user_id") userId: String,
#Query("sub_id") planId: String,
#Query("folder") description:String,
#Query("payment_type") paymentType:String):
Call<TransactionResponseModel>
To this:
#FormUrlEncoded
#POST(AppConstants.UPLOAD_TRANSACTION_DETAIL)
fun postPremiumAppTransactionDetail(
#Field("name") planName:String,
#Field("amount") amount:String,
#Field("user_id") userId: String,
#Field("sub_id") planId: String,
#Field("folder") description:String,
#Field("payment_type") paymentType:String):
Call<TransactionResponseModel>
For me it turned out that I was trying to deserialize to an object that used java.time.ZonedDateTime for one of the properties. It worked as soon as I changed it to a java.util.Date instead.

Having issue in Volley [Android] [duplicate]

I have this method:
public static Object parseStringToObject(String json) {
String Object = json;
Gson gson = new Gson();
Object objects = gson.fromJson(object, Object.class);
parseConfigFromObjectToString(object);
return objects;
}
And I want to parse a JSON with:
public static void addObject(String IP, Object addObject) {
try {
String json = sendPostRequest("http://" + IP + ":3000/config/add_Object", ConfigJSONParser.parseConfigFromObjectToString(addObject));
addObject = ConfigJSONParser.parseStringToObject(json);
} catch (Exception ex) {
ex.printStackTrace();
}
}
But I get an error message:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:
Expected BEGIN_OBJECT but was STRING at line 1 column 1
Even without seeing your JSON string you can tell from the error message that it is not the correct structure to be parsed into an instance of your class.
Gson is expecting your JSON string to begin with an object opening brace. e.g.
{
But the string you have passed to it starts with an open quotes
"
Invalid JSON from the server should always be an expected use case. A million things can go wrong during transmission. Gson is a bit tricky, because its error output will give you one problem, and the actual exception you catch will be of a different type.
With all that in mind, the proper fix on the client side is
try
{
gson.fromJSON(ad, Ad.class);
//...
}
catch (IllegalStateException | JsonSyntaxException exception)
{
//...
If you want to know why the JSON you received from the server is wrong, you can look inside your catch block at the exception. But even if it is your problem, it's not the client's responsibility to fix JSON it is receiving from the internet.
Either way, it is the client's responsibility to decide what to do when it gets bad JSON. Two possibilities are rejecting the JSON and doing nothing, and trying again.
If you are going to try again, I highly recommend setting a flag inside the try / catch block and then responding to that flag outside the try / catch block. Nested try / catch is likely how Gson got us into this mess with our stack trace and exceptions not matching up.
In other words, even though I'll admit it doesn't look very elegant, I would recommend
boolean failed = false;
try
{
gson.fromJSON(ad, Ad.class);
//...
}
catch (IllegalStateException | JsonSyntaxException exception)
{
failed = true;
//...
}
if (failed)
{
//...
I had a similar problem recently and found an interesting solution. Basically I needed to deserialize following nested JSON String into my POJO:
"{\"restaurant\":{\"id\":\"abc-012\",\"name\":\"good restaurant\",\"foodType\":\"American\",\"phoneNumber\":\"123-456-7890\",\"currency\":\"USD\",\"website\":\"website.com\",\"location\":{\"address\":{\"street\":\" Good Street\",\"city\":\"Good City\",\"state\":\"CA\",\"country\":\"USA\",\"postalCode\":\"12345\"},\"coordinates\":{\"latitude\":\"00.7904692\",\"longitude\":\"-000.4047208\"}},\"restaurantUser\":{\"firstName\":\"test\",\"lastName\":\"test\",\"email\":\"test#test.com\",\"title\":\"server\",\"phone\":\"0000000000\"}}}"
I ended up using regex to remove the open quotes from beginning and the end of JSON and then used apache.commons unescapeJava() method to unescape it. Basically passed the unclean JSON into following method to get back a cleansed one:
private String removeQuotesAndUnescape(String uncleanJson) {
String noQuotes = uncleanJson.replaceAll("^\"|\"$", "");
return StringEscapeUtils.unescapeJava(noQuotes);
}
then used Google GSON to parse it into my own Object:
MyObject myObject = new.Gson().fromJson(this.removeQuotesAndUnescape(uncleanJson));
In Retrofit2, When you want to send your parameters in raw you must use Scalars.
first add this in your gradle:
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:converter-scalars:2.3.0'
public interface ApiInterface {
String URL_BASE = "http://10.157.102.22/rest/";
#Headers("Content-Type: application/json")
#POST("login")
Call<User> getUser(#Body String body);
}
my SampleActivity :
public class SampleActivity extends AppCompatActivity implements Callback<User> {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ApiInterface.URL_BASE)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiInterface apiInterface = retrofit.create(ApiInterface.class);
// prepare call in Retrofit 2.0
try {
JSONObject paramObject = new JSONObject();
paramObject.put("email", "sample#gmail.com");
paramObject.put("pass", "4384984938943");
Call<User> userCall = apiInterface.getUser(paramObject.toString());
userCall.enqueue(this);
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onResponse(Call<User> call, Response<User> response) {
}
#Override
public void onFailure(Call<User> call, Throwable t) {
}
}
Reference: [How to POST raw whole JSON in the body of a Retrofit request?
I have come to share an solution. The error happened to me after forcing the notbook to hang up. possible solution clean preject.
Maybe your JSON Object is right,but the response that you received is not your valid data.Just like when you connect the invalid WiFi,you may received a strange response < html>.....< /html> that GSON can not parse.
you may need to do some try..catch.. for this strange response to avoid crash.
Make sure you have DESERIALIZED objects like DATE/DATETIME etc. If you are directly sending JSON without deserializing it then it can cause this problem.
In my situation, I have a "model", consist of several String parameters, with the exception of one: it is byte array byte[].
Some code snippet:
String response = args[0].toString();
Gson gson = new Gson();
BaseModel responseModel = gson.fromJson(response, BaseModel.class);
The last line above is when the
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column
is triggered. Searching through the SO, I realised I need to have some form of Adapter to convert my BaseModel to and fro a JsonObject. Having mixed of String and byte[] in a model does complicate thing. Apparently, Gson don't really like the situation.
I end up making an Adapter to ensure byte[] is converted to Base64 format. Here is my Adapter class:
public class ByteArrayToBase64Adapter implements JsonSerializer<byte[]>, JsonDeserializer<byte[]> {
#Override
public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return Base64.decode(json.getAsString(), Base64.NO_WRAP);
}
#Override
public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(Base64.encodeToString(src, Base64.NO_WRAP));
}
}
To convert JSONObject to model, I used the following:
Gson customGson = new GsonBuilder().registerTypeHierarchyAdapter(byte[].class, new ByteArrayToBase64Adapter()).create();
BaseModel responseModel = customGson.fromJson(response, BaseModel.class);
Similarly, to convert the model to JSONObject, I used the following:
Gson customGson = new GsonBuilder().registerTypeHierarchyAdapter(byte[].class, new ByteArrayToBase64Adapter()).create();
String responseJSon = customGson.toJson(response);
What the code is doing is basically to push the intended class/object (in this case, byte[] class) through the Adapter whenever it is encountered during the convertion to/fro JSONObject.
Don't use jsonObject.toString on a JSON object.
In my case, I am Returning JSON Object as
{"data":"","message":"Attendance Saved
Successfully..!!!","status":"success"}
Resolved by changing it as
{"data":{},"message":"Attendance Saved
Successfully..!!!","status":"success"}
Here data is a sub JsonObject and it should starts from { not ""
Don't forget to convert your object into Json first using Gson()
val fromUserJson = Gson().toJson(notificationRequest.fromUser)
Then you can easily convert it back into an object using this awesome library
val fromUser = Gson().fromJson(fromUserJson, User::class.java)
if your json format and variables are okay then check your database queries...even if data is saved in db correctly the actual problem might be in there...recheck your queries and try again.. Hope it helps
I had a case where I read from a handwritten json file. The json is perfect. However, this error occurred. So I write from a java object to json file, then read from that json file. things are fine. I could not see any difference between the handwritten json and the one from java object. Tried beyondCompare it sees no difference.
I finally noticed the two file sizes are slightly different, and I used winHex tool and detected extra stuff.
So the solution for my situation is, make copy of the good json file, paste content into it and use.
In my case, my custom http-client didn't support the gzip encoding. I was sending the "Accept-Encoding: gzip" header, and so the response was sent back as a gzip string and couldn't be decoded.
The solution was to not send that header.
I was making a POST request with some parameters using Retrofit in Android
WHAT I FACED:
The error I was getting in Android Studio logcat:
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
at line 2 column 1 path $
[but it was working fine with VOLLY library]
when I googled it...
you know[ Obviously json is expecting a OBJECT but...]
BUT when I changed my service to return a simple string [ like print_r("don't lose hope") ] or
Noting at all
It was getting printed fine in Postman
but in Android studio logcat, it was still SAME ERROR [
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
at line 2 column 1 path $
]
Hold up now, I am sending a simple message or not sending anything in response and still studio is
telling me "...Expected BEGIN_OBJECT but was STRING..."
SOMETHING IS WRONG
On 4th day:
I finally stopped for looking "QUICK SOLUTIONS" and REALLY READ some stack overflow questions
and articles carefully.
WHAT I GOT:
Logging interceptor
It will show you whatever data comes from your server[even eco messages] which are not shown in
Andorid studios logcat,
that way you can FIND THE PROBLEM.
What I found is I was sending data with #Body like-
#Headers("Content-Type: application/json")
#POST("CreateNewPost")
Call<Resp> createNewPost(#Body ParaModel paraModel);
but no parameter was reaching to server, everything was null [I found using Logging interceptor]
then I simply searched an article "how to make POST request using Retrofit"
here's one
SOLUTION:
from here I changed my method to:
#POST("CreateNewPost")
#FormUrlEncoded
Call<Resp> createNewPost(
#Field("user_id") Integer user_id,
#Field("user_name") String user_name,
#Field("description") String description,
#Field("tags") String tags);
and everything was fine.
CONCLUSION:
I don't understand why Retrofit gave this error
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
at line 2 column 1 path $
it doesn't make any sense at all.
So ALWAYS DEBUG in detail then find WHERE THINGS ARE LEAKING and then FIX.
This error solved for by replacing .toString method to .string on the response
toString => string (add in try{...code..}catche(IOException e))
below code is working for me
try {
MainModelResponse model;
Gson gson = new GsonBuilder().create();
if (response.code() == ConstantValues.SUCCESS_OK) {
model = gson.fromJson(response.body().string(), MainModelResponse.class);
} else {
model = gson.fromJson(response.errorBody().string(), MainModelResponse.class);
}
moduleData.postValue(model);
}catch (IllegalStateException | JsonSyntaxException | IOException exception){
exception.printStackTrace();
}
}
use a string begin & end with {}.
such as
final String jsStr = "{\"metric\":\"opentsdb_metric\",\"tags\":{\"testtag\":\"sunbotest\"},\"aggregateTags\":[],\"dps\":{\"1483399261\":18}}";
DataPoint dataPoint = new Gson().fromJson(jsStr, DataPoint.class);
this works for me.
In my case the object was all fine even the Json Validator was giving it a valid resposne but I was using Interface like this
#POST(NetworkConstants.REGISTER_USER)
Call<UserResponse> registerUser(
#Query("name") String name,
#Query("email") String email,
#Query("password") String password,
#Query("created_date") Long creationDate
);
Then I changed the code to
#FormUrlEncoded
#POST(NetworkConstants.REGISTER_USER)
Call<UserResponse> registerUser(
#Field("name") String name,
#Field("email") String email,
#Field("password") String password,
#Field("created_date") Long creationDate
);
And everything was resolved.
my problem not related to my codes
after copy some files from an other project got this issue
in the stack pointed to Gson library
in android studio 4.2.1 this problem not solved when I try file-> invalidate and restart
and
after restart in first time build got same error but in second build this problem solved
I don't understand why this happened
I was using an old version of retrofit library. So what I had to do was to change my code from this after upgrading it to com.squareup.retrofit2:retrofit:2.9.0:
#POST(AppConstants.UPLOAD_TRANSACTION_DETAIL)
fun postPremiumAppTransactionDetail(
#Query("name") planName:String,
#Query("amount") amount:String,
#Query("user_id") userId: String,
#Query("sub_id") planId: String,
#Query("folder") description:String,
#Query("payment_type") paymentType:String):
Call<TransactionResponseModel>
To this:
#FormUrlEncoded
#POST(AppConstants.UPLOAD_TRANSACTION_DETAIL)
fun postPremiumAppTransactionDetail(
#Field("name") planName:String,
#Field("amount") amount:String,
#Field("user_id") userId: String,
#Field("sub_id") planId: String,
#Field("folder") description:String,
#Field("payment_type") paymentType:String):
Call<TransactionResponseModel>
For me it turned out that I was trying to deserialize to an object that used java.time.ZonedDateTime for one of the properties. It worked as soon as I changed it to a java.util.Date instead.

Plaid Springboot webhook listener

I am wondering if anyone knows best practices for handling Plaid webhooks with Java Springboot?
Does the Plaid SDK offer any easy way to convert the webhook request object to a model object for the given event type? I only see they have Node Express examples which seems to only deconstruct the JSON request object by key.
Also wondering if their is anyway to verify the incoming webhook request is actually from Plaid
#PostMapping(value = "/webhook/plaid", produces =
MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity plaidWebhook(#RequestBody String payload) {
JSONParser parser = new JSONParser(payload);
JSONObject plaidWebhookRequest = null;
try {
plaidWebhookRequest = (JSONObject) parser.parse();
String webhookType = plaidWebhookRequest.has("webhook_type") ? (String) plaidWebhookRequest.get("webhook_type") : null;
String webhookCode = plaidWebhookRequest.has("webhook_code") ? (String) plaidWebhookRequest.get("webhook_code") : null;
String error = plaidWebhookRequest.has("error") ? (String) plaidWebhookRequest.get("error") : null;
String itemID = plaidWebhookRequest.has("item_id") ? (String) plaidWebhookRequest.get("item_id") : null;
if (webhookType != null && webhookCode != null && webhookType.equals(WebhookType.ITEM.name())) {
switch (webhookCode) {
case ERROR_WEBCODE:
log.info("Plaid webhook received: " + ERROR_WEBCODE);
break;
case PENDING_EXPIRATION:
log.info("Plaid webhook received: " + PENDING_EXPIRATION);
break;
case USER_PERMISSION_REVOKED:
log.info("Plaid webhook received: " + USER_PERMISSION_REVOKED);
break;
}
}
} catch (ParseException e) {
log.debug("Plaid webhook object failed to convert to JSONObject");
}
return ResponseEntity.status(HttpStatus.OK).body("");
}
I am not a Java expert but I can speak to some of the other parts of your question:
You can use the webhook verification endpoint to verify that the webhook is from Plaid: https://plaid.com/docs/api/webhooks/webhook-verification/ although I will admit the process is not as easy as most of the other things you can do with the Plaid API.
As an alternative option -- for a situation like this, you can always check the Item status by calling /item/get to confirm that the Item needs to be updated before sending the user through update mode. As a rule, Plaid doesn't ever send sensitive information in webhooks, and information in webhooks can be verified by calling endpoints that are free to call, so you should never need to "trust" a Plaid webhook without verifying it if you don't want to. This is generally smart to do anyway, for example: even if you got a webhook indicating that the Item is in an error state, the user may have resolved it or it may have self-healed in the interim.

How do I use MSAL4J to acquire a token for a daemon?

I have a daemon written in Java and running on AWS.
It calls multiple Microsoft APIs using tokens based on client Id, client secret and tenant id for each of 100s of user accounts that I am supporting. All worked fine with MS Azure Active Directory Library for Java (ADAL4J). But that is going bye bye and so I am being forced over to MS Authentication Library for Java (MSAL4J).
Basically, I need to use a client id, secret and tenant to get an accessToken that is required for a MS API.
After much meandering through the examples (many of which compile), it seems that this is the closest code I can get to:
public static String getToken( String apiUrl,
String clientId,
String clientSecret,
String tenantId,
String authUrl ) {
String token = null ;
if ( !authUrl.endsWith("/")){
authUrl = authUrl + "/" ;
}
/*
NOTE: This is derived from the following:
https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-desktop-acquire-token?tabs=java
I simplified the code by taking out the SilentParameters support.
*/
// BAD: authUrl = authUrl + "organizations/";
// BAD: authUrl = "https://login.microsoftonline.com/" + tenantId + "/";
// BAD: authUrl = "https://login.microsoftonline.com/organizations/";
authUrl = "https://login.microsoftonline.com/organizations/" + tenantId + "/" ;
// BAD: Set<String> SCOPE = Collections.singleton("https://graph.microsoft.com/.default");
// BAD: Set<String> scope = Collections.singleton(clientId);
Set<String> scope = Collections.singleton("");
// Load token cache from file and initialize token cache aspect. The token cache will have
// dummy data, so the acquireTokenSilently call will fail.
ITokenCacheAccessAspect tokenCacheAspect = new TokenPersistence("");
PublicClientApplication pca;
try {
pca = PublicClientApplication
.builder(clientId)
.authority(authUrl)
.setTokenCacheAccessAspect(tokenCacheAspect)
.build();
} catch (MalformedURLException e) {
return null ;
}
IAuthenticationResult result;
/*
BAD: ClientCredentialParameters parameters =
BAD: ClientCredentialParameters
BAD: .builder(SCOPE)
BAD: .build();
*/
UserNamePasswordParameters parameters =
UserNamePasswordParameters
.builder(scope, clientId, clientSecret.toCharArray())
.build();
result = pca.acquireToken(parameters).join();
token = result.accessToken() ;
return token ;
}
So, it compiles (even the BAD commented out code compiles), and it runs but it generates:
com.microsoft.aad.msal4j.MsalClientException: com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class com.microsoft.aad.msal4j.InstanceDiscoveryMetadataEntry]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
The above is generated on the acquireToken call (near the bottom).
I can't figure out what code needs a default constructor (to make the JSON happy).
OTOH, I don't know if these are the calls that I should even be making; there seem to be about 47 different ways through and around this MSAL stuff and I am not at ALL sure if I have found "the right path".
Help Me, Obi-Wan Kenobi. You're My Only Hope!
Checkout the ms-identity-java-daemon sample: https://github.com/Azure-Samples/ms-identity-java-daemon.
Try not using TokenCacheAccessAspect at all and see if that works? I.e. something like:
IClientCredential credential = ClientCredentialFactory.createFromSecret(clientSecret);
ConfidentialClientApplication cca = ConfidentialClientApplication.builder(clientId, credential)
.authority(authUrl)
.build();
Set<String> scope = ImmutableSet.of();
ClientCredentialParameters parameters =
ClientCredentialParameters.builder(scope)
.build();
result = cca.acquireToken(parameters).join();
Where authUrl should look like https://login.microsoftonline.com/<tenantId>
See: https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-daemon-acquire-token?tabs=java

How do I get a custom field out of the payload using JJWT

OK, I am adding a couple of custom claims to the payload when I generate the JWT, and I can pull those out just fine in my front-end (javascript). I then have my javascript send an ajax call to a micro-service and it passes the JWT along with it. I want to get my custom claims out of the JWT in the micro-service. I'm doing the following:
Claims claims = Jwts.parser().setSigningKey(Vars.SECRET_KEY).parseClaimsJws(token).getBody();
User user = claims.get("customuser", User.class);
and it throws an exception.
io.jsonwebtoken.RequiredTypeException: Expected value to be of type: class net.netdatacorp.netdauth.model.User, but was class java.util.LinkedHashMap
at io.jsonwebtoken.impl.DefaultClaims.get(DefaultClaims.java:128)
Here is how the data looks in the JWT inspector on the front-end for my custom claim.
{
jti: "83bffbad-7d36-4370-9332-21a84f2a3dce",
iat: 1498241526,
sub: "test",
iss: "www.test.net",
customuser: {
userId: 1,
userCd: "TMM",
firstNm: "Testy",
lastNm: "McTesty",
userNm: "test",
emailAddress: "jacob#test.net",
active: true,
createdDt: 1491355712000,
createdByUserId: 0,
lastUpdateDt: 1498199278000,
lastUpdateByUserId: 0,
lastLoginDt: 1484928016000
}
}
What am I missing to be able to pull my custom claim out?
We can use Jackson's object mapper to convert Claims (which is a Map<String, Object>) to our custom claim java object.
final ObjectMapper mapper = new ObjectMapper();
Claims jwsMap = Jwts.parser()
.setSigningKey("SECRET")
.parseClaimsJws("jwt")
.getBody();
return mapper.convertValue(jwsMap, MyCustomClaim.class);
Also add that code to try catch to make sure we handle the case of missing/tampered signature.
Add Custom Claims to JWT.
Note: I used this in Spring Security
Reserved Claims
iss – Issuer
sub – Subject
aud – Audience
exp – Expiration
nbf – Not Before
iat – Issued At
jti – JWT ID
Adding Custom Claims
String token = Jwts.builder()
.setSubject(subject)
.setExpiration(expDate)
.claim("userId", "3232")
.claim("UserRole", "Admin")
.signWith(SignatureAlgorithm.HS512, secret )
.compact();
Retrieving Custom Claims
Claims claims = Jwts.parser()
.setSigningKey(tokenSecret)
.parseClaimsJws(jwt).getBody();
// Reading Reserved Claims
System.out.println("Subject: " + claims.getSubject());
System.out.println("Expiration: " + claims.getExpiration());
// Reading Custom Claims
System.out.println("userId: " + claims.get("userId"));
System.out.println("userRole: " + claims.get("userRole"));
JJWT has had this functionality since its 0.11.0 release.
The idea is that a JWT library should not itself undertake marshaling behavior because 1) it's really complex work to be able to handle any ad-hoc data structure (see the JAXB and Jackson codebases as examples) and 2) the JSON marshaller can do it already - there's no point in JJWT re-inventing that wheel.
So, to leverage the marshaller's built-in support, we need to tell it what fields it should look to unmarshall into custom objects so it can do this at parse time. (By the time the JSON is fully parsed, it's already 'too late' when JJWT starts looking at the JWT Map, so we need to ensure the marshaller can do it at parse time).
You do this by telling the marshaller which fields should be converted into custom types, for example, with Jackson:
Jwts.parserBuilder()
.deserializeJsonWith(new JacksonDeserializer(Maps.of("user", User.class).build())) // <-----
.build()
.parseClaimsJwt(aJwtString)
.getBody()
.get("user", User.class) // <-----
For more information, see JJWT's documentation at https://github.com/jwtk/jjwt#parsing-of-custom-claim-types
OK, so I switched to using Jose4J instead of JJWT and after working to get every thing working I realized I probably could have done something similar with JJWT. So what I ended up doing was to use Gson to perform a JSON encoding of the Object and the attaching the resulting JSON string as a claim. And so when I wanted to get a custom claim back out, I would extract the claim as a string and the use the Gson library to convert it back to a POJO.
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
JwtConsumer jwtConsumer = getConsumer();
JwtClaims jwtClaims = jwtConsumer.processToClaims(token);
String userStr = jwtClaims.getClaimValue("user", String.class);
User user = gson.fromJson(userStr, User.class);
I know that your main target is Customer object. other data already exist in the object of the claim. you can easily manage your own object like this.
#Data //insted of this annotation, you can generate the getters and setters
#JsonIgnoreProperties(ignoreUnknown = true)
public class Customer {
private Integer userId;
private String userCd;
private String firstNm;
........
}
JsonIgnoreProperties annotation is very important when converts to the object from the token body. it ignores the other Properties the object hasn't. (Jti,Lat,Iss)
now you have the object that you want. let's generate the token.
Map<String, Object> claims = new HashMap<>(); //create a hashmap
Customer customer= new Customer(); //create your object
//assign the initial customer data
customer.setUserId(1);
customer.setUserCd("TMM");
customer.setFirstNm("Testy");
ObjectMapper oMapper = new ObjectMapper(); //create a objectmapper object
Map<String, Object> customerData = oMapper.convertValue(customer, Map.class); //convert the customer object into map of (String, Object)
claims.putAll(customerData ); //put all the customer data into claims map
//create the token using another required data
String token = Jwts.builder()
.setClaims(claims) //this our object
.setSubject("test")
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000))
.signWith(SignatureAlgorithm.HS512, "secret")
.compact();
go to the https://jwt.io/ and put the generated token and see how is it. it will be like this.
{
"sub": "test",
"firstNm": "Testy", //customer data from the object
"exp": 1622862855,
"userId": 1, //customer data from the object
"iat": 1622844855,
"userCd": "TMM" //customer data from the object,
........
}
it contains all the data with your custom customer data too.
now let's decode the token
Jws<Claims> claimsJws = Jwts.parser().setSigningKey("secret").parseClaimsJws(token);
ObjectMapper mapper = new ObjectMapper();
Customer customer = mapper.convertValue(claimsJws.getBody(), Customer.class); //convert the claims body by mentioning the customer object class
System.out.println("customerData = " + customer);
now you can use the customer data object as you want.
**special thing is the #JsonIgnoreProperties(ignoreUnknown = true) annotation.

Categories

Resources