I implemented security for my application using JWT tokens. I make a post request to the /auth endpoint (passing userName and password) and get back a token, which I then use for further requests to the other endpoints. Everything works perfectly fine, but I can't get the userName out of this token. Here' my controller method:
#GetMapping(produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public ResponseEntity<List<SongList>> getSongListsForUser(#RequestParam("userId") String ownerId) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String userId = auth.getName();
return ResponseEntity.ok(userId.equals(ownerId)
? songListDAO.findListsOf(userId)
: songListDAO.findPublicListsOf(ownerId));
}
Found this solution here on stackoverflow.
I put a breakpoint at the line of the return statement to inspect whats inside the userId and got this:
Why this is not working? What do I have to do to get the userName (=userId) from the jwt token?
Related
I'm using the appengine endpoints-framework-v2 java sample. In this sample there is an endpoint called getUserEmailFirebase which should authenticated firebase user but it does not work
#ApiMethod(
path = "firebase_user",
httpMethod = ApiMethod.HttpMethod.GET,
authenticators = {EspAuthenticator.class},
issuerAudiences = {#ApiIssuerAudience(name = "firebase",
audiences = {"my-project-id"})}
)
public Email getUserEmailFirebase(User user) throws UnauthorizedException {
if (user == null) {
throw new UnauthorizedException("Invalid credentials");
}
Email response = new Email();
response.setEmail(user.getEmail());
return response;
}
I tried to send the request with an Authorization header inculding the firebase user id token but I'm getting 401 response, Also tried to add 'Bearer ' at the begining of the token but I get the same result. Even tried to follow this appengine guide Authenticating Users (Frameworks) without success.
The token is retrieved by AngularFire SDK using firebaseUser.getIdToken(), I have used the Firebase server sdk to authenticated users successfuly but it doesn't work using the above method
Can you help?
I have an android app. It connects with a REST API developed with Jersey. My REST End points are secured with Tokens. Below is how I generate them.
Algorithm algorithm = Algorithm.HMAC256(secret);
String token = JWT.create()
.withClaim("userName","myusername)
.withExpiresAt(expirationDate)
.sign(algorithm);
Below is how I validate the token
public boolean validateTokenHMAC256(String token, String secret) throws UnsupportedEncodingException, JWTVerificationException
{
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm)
.build(); //Reusable verifier instance
DecodedJWT jwt = verifier.verify(token);
Claim usernameClaim = jwt.getClaim("username");
String username = usernameClaim.asString();
System.out.println(username);
return true;
}
In my REST API I have a filter and that filter checks every request to see whether the token is as it is. Below is the code.
#Secured
#Provider
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter{
//private static String authorizationSecret = "ZXW24xGr9Dqf9sq5Dp8ZAn5nSnuZwux2QxdvcH3wQGqYteJ5yMTw5T8DBUJPbySR";
public AuthenticationFilter()
{
System.out.println("test printing");
}
#Override
public void filter(ContainerRequestContext crc) throws IOException
{
String headerString = crc.getHeaderString("Bearer");
System.out.println("bluh: "+headerString);
System.out.println("test printing");
try
{
boolean validateToken = validateToken(headerString, AuthKey.authorizationSecret);
System.out.println("valid");
}
catch(Exception e)
{
System.out.println("invalid");
crc.abortWith(
Response.status(Response.Status.UNAUTHORIZED).build());
}
}
private boolean validateToken(String strToken, String secret) throws UnsupportedEncodingException, JWTVerificationException
{
Token token = new Token();
return token.validateTokenHMAC256(strToken,secret);
}
}
The above code will be called when the user login to the application. However the token will be expired in 60 minutes. I know that after the token is expired either I have to take the user back to sign in screen or refresh the token. I went through the advices in here and here
But I do not understand the following.
How can I figure out whether the token has to be renewed? I thought I should do that after it is expired, but seems that is not the case. If I ask it to refresh in now<exp it will refresh in every request.
How can I assign and send this token back to the user? Currently when the user login on, he will get the token and he will save it in a variable. For the refreshed token to work, do I have to call the login method again (So the token will be sent to the user) or JWT it self will handle the case?
How do I actually refersh using java-jwt ?
How can I figure out whether the token has to be renewed? I thought I should do that after it is expired, but seems that is not the case. If I ask it to refresh in now
You need to refresh the token before it is expired. Decide your policy:
issue a fresh token in every request
issue a fresh token when the current one is close to expire. e.g. 10 min
let client app request a new token when it needs it using a "refresh service" of your api. For example
#GET
#Path("/jwt/refresh")
#Produces(MediaType.TEXT_HTML)
public String refresh(){
//Build a returns a fresh JWT to client
}
How can I assign and send this token back to the user?
If you issue a fresh token during a request, you can return it in a special header that client will read during processing of the response. If you publish a "refresh" service as described above, then the client will call it independently when the current JWT is close to expire
Redirect to login method is not a good alternative because you will lose the current request
How do I actually refresh using java-jwt
Just issue a new token
I have some trouble figuring out how to create a login form in angularjs using springboot.
I can register a user and send the data to the database but the problems begin when i want to login.
in angularjs i have a function like this
function Login(username, password, callback) {
$http.post('/api/authenticate', { username: username, password: password })
.success(function (response) {
callback(response);
});
}
What i managed to do but probably is't right:
#RequestMapping(value = "/authenticate/{id}",method = RequestMethod.GET)
public User getUser(#PathVariable Integer id) {
return repo.findOne(id);
}
This gives me following json
{"id":2,"username":"jdoe","password":"$2a$10$5hgIyQr.K9wb8cXEyWGbROAU.rkYzd19vP7ajHpwp1KUYdShfcPn.","lastname":"doe","firstname":"john","customfield":"Hello there"}
But now i have following problems and questions :
How can i check if the username and password is equal to the username and password of json by going to api/authenticate ? (without {id})
can i hide this json from the users ?
Is this safe ?
how will angular now all the users propertys ? (i suggest i can retrieve this from the json)
any pro tips on how to solve this?
From AngularJS you are calling HTTP POST method and at Spring side you have declared as HTTP GET, which is wrong.
Correct request mapping is
#RequestMapping(value = "/api/authenticate",method = RequestMethod.POST, consumes = "application/json")
#ResponseBody
public User getUser(#RequestBody User user) {
//here request body contains User POJO object's payload (JSON object)
//You are getting username from JSON,
//so you need to update your call to findOne method
return repo.findOne(user.getUserName());
}
Please refer
#RequestBody and #ReponseBody spring
#RequestBody annotation spring docs
#RequestMapping#consumes
how to authenticate a user without typing a name and password, but a URL sent by email with a token ?
I tried with a controller intercepting the url containing the token but my spring setting returns the login page because the user is anonymous.
I would also like to connect with token in the URL such as :
http://myWebSite/guest/18f1ff9a-fd1e-49be-bb31 ...
I have a token for each user in database
I have allowed anonymous access to the controller with token. Then I get the username and password of a user by token Like this :
String queryFindUserByToken = "from UserEntity u where u.token = :token";
Now I want to automatically create a user session with the login and password that I got from the database.
I tried to redirect to redirect to j_spring_security_check with parameters but authentication method not supported: GET
#RequestMapping(value = "guest/{tokenUser}", method = RequestMethod.GET)
public String guest(Map<String, Object> map, #PathVariable String tokenUser) {
UserEntity user = userService.findUserByToken(tokenUser);
return "redirect:j_spring_security_check?j_username=" + user.getUsername() + "&j_password=" + user.getPassword() ;
}
do you have a better solution ?
I'm trying to write a simple smoke test for a web application.
The application normally uses form based authentication, but accepts basic auth as well, but since the default is form based authentication, it never sends an authentication required, but instead just sends the login form.
In the test I try to send the basic auth header using
WebClient webClient = new WebClient();
DefaultCredentialsProvider creds = new DefaultCredentialsProvider();
// Set some example credentials
creds.addCredentials("usr", "pwd");
// And now add the provider to the webClient instance
webClient.setCredentialsProvider(creds);
webClient.getPage("<some url>")
I also tried stuffing the credentials in a WebRequest object and passing that to the webClient.getPage method.
But on the server I don't get an authentication header. I suspect the WebClient only sends the authentication header if it get explicitly asked for it by the server, which never happens.
So the question is how can I make the WebClient send the Authentication header on every request, including the first one?
This might help:
WebClient.addRequestHeader(String name, String value)
More specific one can create an authentication header like this
private static void setCredentials(WebClient webClient)
{
String username = "user";
String password = "password";
String base64encodedUsernameAndPassword = base64Encode(username + ":" + password);
webClient.addRequestHeader("Authorization", "Basic " + base64encodedUsernameAndPassword);
}
private static String base64Encode(String stringToEncode)
{
return DatatypeConverter.printBase64Binary(stringToEncode.getBytes());
}