I'm trying to create an authenticated websocket to wss://ws-feed-public.sandbox.exchange.coinbase.com and need to create a "signature" for my requests using the following:
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
public class Signature {
private final String secretKey;
public Signature(final String secretKey) {
this.secretKey = secretKey;
}
public String generate(String requestPath, String method, String body, String timestamp) {
String message = timestamp + method.toUpperCase() + requestPath + body;
return new HmacUtils(HmacAlgorithms.HMAC_SHA_256, secretKey).hmacHex(message);
}
}
For this particular signature, requestPath is always a blank string, and method is always GET
I continuously get the following return:
{
"type":"error",
"message":"Authentication Failed",
"reason":"{\"message\":\"invalid signature\"}"
}
I have also tried utilizing Signature.java from Gdax-java https://github.com/irufus/gdax-java/blob/master/security/src/main/java/com/coinbase/exchange/security/Signature.java#L34 but to no avail.
What am I doing incorrectly? Any help is appreciated.
Update: I also tried setting requestPath to /users/self/verify.
Related
I'm attempting to use Retrofit to call the GitHub API to update the contents of an existing file, but am getting 404s in my responses. For this question, I'm interested in updating this file. Here is the main code I wrote to try and achieve this:
GitHubUpdateFileRequest
public class GitHubUpdateFileRequest {
public String message = "Some commit message";
public String content = "Hello World!!";
public String sha = "shaRetrievedFromSuccessfulGETOperation";
public final Committer committer = new Committer();
private class Committer {
Author author = new Author();
private class Author {
final String name = "blakewilliams1";
final String email = "blake#blakewilliams.org";
}
}
}
**GitHubUpdateFileResponse **
public class GitHubUpdateFileResponse {
public GitHubUpdateFileResponse() {}
}
GitHubClient
public interface GitHubClient {
// Docs: https://docs.github.com/en/rest/reference/repos#get-repository-content
// WORKS FINE
#GET("/repos/blakewilliams1/blakewilliams1.github.io/contents/qr_config.json")
Call<GitHubFile> getConfigFile();
// https://docs.github.com/en/rest/reference/repos#create-or-update-file-contents
// DOES NOT WORK
#PUT("/repos/blakewilliams1/blakewilliams1.github.io/contents/qr_config.json")
Call<GitHubUpdateFileResponse> updateConfigFile(#Body GitHubUpdateFileRequest request);
}
Main Logic
// Set up the Retrofit client and add an authorization interceptor
UserAuthInterceptor interceptor =
new UserAuthInterceptor("blake#blakewilliams.org", "myActualGitHubPassword");
OkHttpClient.Builder httpClient =
new OkHttpClient.Builder().addInterceptor(interceptor);
Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.client(httpClient.build()).build();
client = retrofit.create(GitHubClient.class);
// Now make the request and process the response
GitHubUpdateFileRequest request = new GitHubUpdateFileRequest();
client.updateConfigFile(request).enqueue(new Callback<GitHubUpdateFileResponse>() {
#Override
public void onResponse(Call<GitHubUpdateFileResponse> call, Response<GitHubUpdateFileResponse> response) {
int responseCode = response.code();
// More code on successful update
}
#Override
public void onFailure(Call<GitHubUpdateFileResponse> call, Throwable t) {
Log.e("MainActivity", "Unable to update file" + t.getLocalizedMessage());
}
});
What currently happens:
Currently, the success callback is triggered, but with a response code of 404 like so:
Response{protocol=http/1.1, code=404, message=Not Found, url=https://api.github.com/repos/blakewilliams1/blakewilliams1.github.io/contents/qr_config.json}
Has anyone else encountered this? I first thought it was a problem with including '/content/' in the URL but I do the same thing for reading the file contents request and it works fine (also uses same URL just a GET instead of PUT).
For anyone interested in doing this in the future, I figured out the solution.
I needed to revise the request object structure
Rather than using an authentication interceptor, I instead added an access token to the header. Here is where you can create access tokens for Github, you only need to grant it permissions to the 'repos' options for this use case to work.
This is what my updated request object looks like:
public class GitHubUpdateFileRequest {
public String message;
public String content;
public String sha;
public final Committer committer = new Committer();
public GitHubUpdateFileRequest(String unencodedContent, String message, String sha) {
this.message = message;
this.content = Base64.getEncoder().encodeToString(unencodedContent.getBytes());
this.sha = sha;
}
private static class Committer {
final String name = "yourGithubUsername";
final String email = "email#yourEmailAddressForTheUsername.com";
}
}
Then from my code, I would just say:
GitHubUpdateFileRequest updateRequest = new GitHubUpdateFileRequest("Hello World File Contents", "This is the title of the commit", shaOfExistingFile);
For using this reqest, I updated the Retrofit client implementation like so:
// https://docs.github.com/en/rest/reference/repos#create-or-update-file-contents
#Headers({"Content-Type: application/vnd.github.v3+json"})
#PUT("/repos/yourUserName/yourRepository/subfolder/path/to/specific/file/theFile.txt")
Call<GitHubUpdateFileResponse> updateConfigFile(
#Header("Authorization") String authorization, #Body GitHubUpdateFileRequest request);
And I call that interface like this:
githubClient.updateConfigFile("token yourGeneratedGithubToken", request);
And yes, you do need the prefix "token ". You could hardcode that header into the interface, but I pass it in so that I can store it in locations outside of my version control's reach for security reasons.
I've implemented auth with Firestore and it works fine and now redoing it via Google API and get http status "404" and empty message:
D/RESPONSE FIREBASE: Response{protocol=h2, code=404, message=, url=https://identitytoolkit.googleapis.com/v1/accounts/signInWithPassword?key=000080511101}
Network service:
public class NetworkService {
private static NetworkService instance;
private static final String BASE_URL
= "https://identitytoolkit.googleapis.com/v1/";
private Retrofit retrofit;
private NetworkService() {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
public static NetworkService getInstance() {
if (instance == null) {
instance = new NetworkService();
}
return instance;
}
public PlaceHolderApi getJsonApi() {
return retrofit.create(PlaceHolderApi.class);
}
}
Api
public interface PlaceHolderApi {
#FormUrlEncoded
#POST("accounts/signInWithPassword")
Call<Transaction.Result> loginWithEmail(
#Query("key") String key,
#Field("email") String email,
#Field("password") String password,
#Field("returnSecureToken") boolean returnSecureToken
);
}
Usage:
NetworkService.getInstance()
.getJsonApi().loginWithEmail("000080511101", email, password, true)
.enqueue(new Callback<Transaction.Result>() {
#Override
public void onResponse(Call<Transaction.Result> call, Response<Transaction.Result> response) {
Log.d("RESPONSE FIREBASE", response.toString());
Log.d("RESPONSE MESSAGE", response.message());
}
#Override
public void onFailure(Call<Transaction.Result> call, Throwable t) {
t.printStackTrace();
}
});
Documentation says that I should use Content type application/JSON, but how to use it here or pass it via http using retrofit?
Any directions will be helpful.
Thanks!
UPD: Console query result
The real issue was because of colon symbol inside url ":", so url should start from dot and slash symbols "./":
#POST("./accounts:signInWithPassword")
Found this on github and it helps https://github.com/square/retrofit/issues/2730
UPD: A little explanation why I used url like "accounts/signInWithPassword" with slash symbol inside instead of colon symbol: I tried with colon first, but got an error "Malformed url" so I dug a bit deeper with that mistake :)
You can add a header like this. But I think if you miss the header response, the error code wouldn't be 404.
Anyway, try this.
#FormUrlEncoded
#Headers({"Content-Type: application/json"})
#POST("accounts/signInWithPassword")
Call<Transaction.Result> loginWithEmail(
#Query("key") String key,
#Field("email") String email,
#Field("password") String password,
#Field("returnSecureToken") boolean returnSecureToken
);
I have the following API-method:
#PatchMapping("/{id}")
public ResponseEntity<?> partialProjectUpdate(#PathVariable long id, #RequestBody EntryStatus status) throws DailyEntryNotFoundException {
return dailyEntryService.partialDailyEntryUpdate(id, status);
}
EntryStatus is an enum:
public enum EntryStatus {
OPEN,
PROGRESS,
CHECKED,
BOOKED,
UNAVAILABLE;
private static Map<String, EntryStatus> namesMap = new HashMap<String, EntryStatus>(3);
static {
namesMap.put("OPEN", OPEN);
namesMap.put("PROGRESS", PROGRESS);
namesMap.put("CHECKED", CHECKED);
namesMap.put("BOOKED", BOOKED);
namesMap.put("UNAVAILABLE", UNAVAILABLE);
}
#JsonCreator
public static EntryStatus forValue(String value) {
return namesMap.get(value);
}
#JsonValue
public String toValue() {
for (Map.Entry<String, EntryStatus> entry : namesMap.entrySet()) {
if (entry.getValue() == this)
return entry.getKey();
}
return null; // or fail
}
}
I call the method in typescript like this:
partialUpdateDailyEntry(dailyEntry: DailyEntry, status): Observable<any> {
const statusName: string = status.name;
return this.http.patch(BASE_URL + dailyEntry.id, statusName, this.authService.setHeaders('application/json'))
.pipe(
catchError(this.handleService.error)
);
}
statusName is a string, but the problem is that its getting sent without quotemarks via JSON. The RequestBody is for example OPEN instead of "OPEN" which gives me the following error:
JSON parse error: Unrecognized token 'OPEN': was expecting ('true', 'false' or 'null').
As stated thats caused by the fact that the string is sent without quotemarks.
I could fix that problem by adding the quotemarks manually to statusName like this:
const statusName: string = '"' + status.name + '"';
But that cant be the proper solution, is there a better way to do it?
Try with
#JsonFormat(shape = JsonFormat.Shape.STRING)
public enum EntryStatus{
OPEN,
PROGRESS,
CHECKED,
BOOKED,
UNAVAILABLE;
}
maybe you could put
namesMap.put("OPEN", OPEN);
as
namesMap.put("\"OPEN\"", OPEN);
You are adding the header that you are sending JSON, but "OPEN" is not a valid JSON value.
You should either change your headers:
this.authService.setHeaders('text/plain')
Or change how you send it:
this.http.patch(BASE_URL + dailyEntry.id, { status: statusName});
And change your java backend to handle to receive the object and read the status
Or stringify it before sending it:
const statusName: string = JSON.stringify(status.name);
I am facing a issue that whenever I am signing a token also I parse it and it is not throwing any signature exception.
You can see the key are different still it giving me the proper response.
public class JwtUtil {
public String parseToken(String token) {
try {
Jws<Claims> jwt = Jwts.parser()
.setSigningKey("Test#12")
.parseClaimsJws(token);
System.out.println(jwt.getBody().getSubject());
return "Valid";
} catch (SignatureException jwtException) {
jwtException.printStackTrace();
return null;
}
}
public String generateToken() {
Claims claim = Jwts.claims();
claim.put("GivenName", "Johnny");
claim.put("Surname", "Rocket");
claim.put("Email", "jrocket#example.com");
return Jwts.builder().setHeaderParam("typ", "JWT").setClaims(claim)
.setIssuer("Online JWT Builder")
.setAudience("www.example.com").setSubject("jrocket#example.com")
.signWith(SignatureAlgorithm.HS256, "Test#123").compact();
}
public static void main(String[] args) {
JwtUtil jwtUtil = new JwtUtil();
String token = jwtUtil.generateToken();
System.out.println(token);
JwtUtil jwtUtil1 = new JwtUtil();
jwtUtil1.parseToken(token);
}
}
Really Test#12 and Test#123 are the same key
It is due to JwtBuilder.signWith(SignatureAlgorithm alg, String base64EncodedSecretKey). assumes that you are providing a key in base64 and your keys are not base64. When the method decodes from base64 to byte[] the java converter used by jjwt provides a representation of the string. Test#12 and Test#123 are encoded with the byte array
See https://stackoverflow.com/a/38269014/6371459
You can test yourself with
System.out.println(
javax.xml.bind.DatatypeConverter.printBase64Binary(
javax.xml.bind.DatatypeConverter.parseBase64Binary("Test#12")));
System.out.println(
javax.xml.bind.DatatypeConverter.printBase64Binary(
javax.xml.bind.DatatypeConverter.parseBase64Binary("Test#123")));
Try a (more) different key and the SignatureException will be thrown
Hi everyone this is my first question here, hope you can help me with this issue I'm having right now.
I want to send a JSON Object using JQuery to a Spring Controller.
The format of the JSON Object is as follows:
{"ssoArray":["21212", "231341"], "groupArray":["g10002", "g10003"]}
But I got the error message: the request sent by the client was syntactically incorrect
My Environment:
Spring 3.0.6
jackson 1.9.13
JBoss 4.2
JQuery code:
Update: Added the full code of the javascript method that makes the ajax call
function addRemoveAdmin(action, ssoArray, groupArray) {
var uri = actionPath + "/";
var jsonParameter = {"ssoArray":ssoArray, "groupArray":groupArray};
if(action == "add") {
uri = uri + addAdminAction;
} else {
uri = uri + removeAdminAction;
}
console.log(typeof jsonParameter);
$.ajax({
url:uri,
type:"POST",
data:jsonParameter,
contentType:"application/json; charset=utf-8",
dataType:"json",
success: function(){
alert(data);
}
});
}
Controller code:
#RequestMapping(value = "/addAdmin", method = RequestMethod.POST)
public String addAdmin(#RequestBody final AdminAndGroup personsAndGroups) throws Exception {
LOGGER.info("[RequestController] - addAdmin start");
LOGGER.info(personsAndGroups.toString());
return "OK";
}
Mapping class:
public class AdminAndGroup implements Serializable {
private static final long serialVersionUID = 9024455757311860269L;
private List<String> ssoArray;
private List<String> groupArray;
public AdminAndGroup(){}
public List<String> getSsoArray() {
return ssoArray;
}
public void setSsoArray(List<String> ssoArray) {
this.ssoArray = ssoArray;
}
public List<String> getGroupArray() {
return groupArray;
}
public void setGroupArray(List<String> groupArray) {
this.groupArray = groupArray;
}
#Override
public String toString() {
return "AdminAndGroup [ssoArray=" + ssoArray + ", groupArray="
+ groupArray + "]";
}
}
I have used java.util.List to map the arrays that come inside the JSON Object but I'm not sure if this is the right way to tackle the issue.
Also I have searched in Stack Overflow and I haven't found an example on how to map arrays inside a JSON Object.
Thanks in advance.
If you want to send json, you must convert your object to json. Otherwise, jQuery will convert it to a param string
data: JSON.stringify(jsonParameter)