Right now I am sending a confirmation link containing JWT token created for user to recipients mail address. This token is saved in a database column.
The activation link looks like this:
http://localhost:7070/RTH_Sample14/eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvcnRoLmNvbSIsInN1YiI6IlJUSCIsInJvbGUiOiJVU0VSIiwiZXhwIjoxNDU2MjQ1MzM2LCJlbWFpbCI6Imtpcml0aS5rOTk5QGdtYWlsLmNvbSJ9.RJ54PhKcj9GGMq_VefQEMhY0x38wX1t5GgMldHCRmBZsPKoXAYg5vr39aXjHtKmIDsoqmDdzzjsrEIweWEATg3-jGe_PGfxwKZg1zsKiWlpavvKJn92VgffJi1yO54t-H31n2NKjVhAcay34pf3eUNqpPcDCEz9uf_GwSZl1ZTM
When the user clicks on the link, I want to be able to call a restful resource (#Path("/public/emailActivation")) which checks for token in database and change the account-status column from "pending" to "active"
The problem is how would I point the activation-link to rest resource method when clicked?
Is this the right approach to activate/verify user?
Rest Registration Service:
#Path("/public/registrationService")
public class RegistrationService {
public RegistrationService() {
}
#POST
#Path("/register")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response register(UserProfile user) {
String email = user.getEmail();
String token = "";
try {
RegistrationDAO registrationDao = new RegistrationDAO();
int count = registrationDao.insert(user);
if (count > 0) {
System.out.println("Registration successful");
TokenProvider jwtProvider = new TokenProvider();
token = jwtProvider.getToken(email);
Map<String, String> response = new HashMap<>();
response.put("token", token);
SendEmailConfirmation mailActivation = new SendEmailConfirmation();
mailActivation.sendMail(email, "http://localhost:7070/RTH_Sample14/"+token);
return Response.ok(response).build();
}
else {
return Response.status(Response.Status.UNAUTHORIZED).type("text/plain").entity("Registration failed!").build();
}
} catch (Exception e) {
e.printStackTrace();
return Response.status(Response.Status.NOT_FOUND).type("text/plain").entity("Error in Login Service web service class").build();
}
}
}
This method should be called for mail activation when user clicks on link:
#Path("/public/emailActivation")
public class EmailActivation {
#Path("/activate")
public void activateAccount(){
//Check Database for token and account status
}
}
Related
Im making a basic reset password functionality in my API but when i try to fetch the PasswordResetToken object from the repository hibernate doesnt fetch it and instead sends back null. Im 99% sure the PasswordResetToken that im trying to fetch is inside of the repository because i did some system.out.println's and it shows the PasswordResetToken is in there with the matching token it just doesnt send anything back.
heres the email varification and token creation
#PostMapping("/resetPassword")
public String resetPassword(#RequestBody PasswordModel passwordModel, HttpServletRequest request) {
User user = userService.findUserByEmail(passwordModel.getEmail());
String url = "";
if(user!=null) {
String token = UUID.randomUUID().toString();
userService.createPasswordResetTokenForUser(user, token);
url = passwordResetTokenMail(user, applicationUrl(request), token);
}
return url;
}
service methods that go along with it
#Override
public User findUserByEmail(String email) {
return userRepository.findByEmail(email);
}
#Override
public void createPasswordResetTokenForUser(User user, String token) {
PasswordResetToken passwordResetToken
= new PasswordResetToken(user, token);
passwordResetTokenRepo.save(passwordResetToken);
}
save new password functionality where the issue happens
#PostMapping("/savePassword")
public String savePassword(#RequestParam("token") String token,
#RequestBody PasswordModel passwordModel) {
String result = userService.validatePasswordResetToken(token);
if(!result.equalsIgnoreCase("valid")) {
return "Invalid token";
}
Optional<User> userOptional = userService.getUserByPasswordResetToken(token);
if(userOptional.isPresent()) {
userService.changePassword(userOptional.get(), passwordModel.getNewPassword());
return "Password has been updated";
} else {
return "Invalid token";
}
}
service methods
#Override
public String validatePasswordResetToken(String token) {
PasswordResetToken passwordResetToken
= passwordResetTokenRepo.findByToken(token);
if(passwordResetToken == null) {
return "invalid";
}
User user = passwordResetToken.getUser();
Calendar cal = Calendar.getInstance();
if((passwordResetToken.getExpirationTime().getTime())
- cal.getTime().getTime() <=0) {
passwordResetTokenRepo.delete(passwordResetToken);
return "expired";
}
return "valid";
}
#Override
public Optional<User> getUserByPasswordResetToken(String token) {
return Optional.ofNullable(passwordResetTokenRepo.findByToken(token).getUser());
}
#Override
public void changePassword(User user, String newPassword) {
user.setPassword(passwordEncoder.encode(newPassword));
userRepository.save(user);
}
and finally the repository
#Repository
public interface PasswordResetTokenRepo extends JpaRepository<PasswordResetToken, Long> {
PasswordResetToken findByToken(String token);
}
Figured it out incase anyone runs into the same issue. I was using postman on the client side and for some reason when I was copying the URL and pasting it into postman it was adding a space after the url so the token ended up being token=xyz with a space after instead of just token xyz. That’s why it looked like the token was there in the repo because you couldn’t tell there was a space after it.
I am trying to make a log in system using spring. Problem is if username is not in the database I want to send a different status code and if username is in the database but password is wrong I want to send different status code. Because in my front end i am going to inform user using different alerts according to status code.
I cannot use HttpStatus.NOT_ACCEPTABLE or something like that because my controller is returning a User(my custom class). It will either return User or null.
#GetMapping("/users")
public User userLogin(#RequestParam String username,#RequestParam String password) {
User user = userService.findByUsername(username);
if(user==null) {
return null;
}
if(user.getPassword().equals(password)) {
return user;
} else {
return null;
}
}
Here I am trying to change status while returning nulls.
you can return ResponseEntity to meet your requirement
#GetMapping("/users")
public ResponseEntity<User> userLogin(#RequestParam String username,#RequestParam String password) {
User user = userService.findByUsername(username);
if(user==null) {
return new ResponseEntity<>(null,HttpStatus.NOT_FOUND);
}
if(user.getPassword().equals(password)) {
return new ResponseEntity<>(user,HttpStatus.OK);
} else {
return new ResponseEntity<>(null,HttpStatus.FORBIDDEN);
}
}
Spring 5 introduced the ResponseStatusException class. We can create an instance of it providing an HttpStatus and optionally a reason and a cause:
#GetMapping(value = "/{id}") public Foo findById(#PathVariable("id") Long id, HttpServletResponse response) {
try {
Foo resourceById = RestPreconditions.checkFound(service.findOne(id));
eventPublisher.publishEvent(new SingleResourceRetrievedEvent(this, response));
return resourceById;
}
catch (MyResourceNotFoundException exc) {
throw new ResponseStatusException(
HttpStatus.NOT_FOUND, "Foo Not Found", exc);
} }
Maybe this is which you looking for?
Detail in https://www.baeldung.com/exception-handling-for-rest-with-spring#controlleradvice
New users are created with the POST method, and after this the database will attach it an unique ID, which is necessary to create a token.
The only way to create a token, is after the process of user creation. I query it from the database and now it should have ID on it, but I can't figure how to do that.
Is there any way to retrieve the entity from database right after creating it?
If looked at this similar question, but couldn't find an answer: Is it ok by REST to return content after POST?
#POST
#Consumes({MediaType.APPLICATION_JSON})
public Response create(#QueryParam("email") String email, #QueryParam("username") String userName, #QueryParam("password") String password) {
if (TextUtil.isEmpty(userName) || TextUtil.isEmpty(password) || TextUtil.isEmpty(email)) {
return Response.status(Response.Status.BAD_REQUEST).build();
} else {
User newUser = new User();
newUser.setEmail(email);
newUser.setUsername(userName);
newUser.setPass(password);
super.create(newUser); //ths line create user in database
String id = newUser.getUid() + ""; //the newUser has no id yet so it is null but I want this id from database
return Response.status(Response.Status.OK).entity("id: " + id).build();
}
}
Create super.method
public void create(T entity) {
getEntityManager().persist(entity);
}
I generate REST api using this tutorial
https://netbeans.org/kb/docs/websvc/rest.html
getEntityManager().persist(entity);
Set the id of the entity by calling entity.setId(...).
I my comments I show how to sync EntityManager. Maybe you should write a better create method:
public <T> T create(T t) throws Exception {
try {
entityManager.persist(t);
entityManager.flush();
} catch (Exception e) {
throw e;
} finally {
entityManager.clear();
}
return t;
}
And then
newUser = super.create(newUser);
So I have an app which uses Google App Engine and Google Cloud Endpoints as it's backend in Java. I'm currently working on User authentication and here is what I'm trying to do:
When user first opens the app, they'll have option to either "Login through Facebook" or signup using their email address. Then this data would be stored in a user object and after registration would direct them to the app homepage. It will be saved in their preferences so that they don't need to login every time they open the app (if ever).
Now I heard you can use a custom authenticator for Facebook, but there's not much documentation regarding this. How can I get the email registration and Facebook Login options to be implemented with Google Cloud Endpoint's Authenticator? Or should I make a different approach?
Thanks.
My approach is using the Facebook login method (Facebook SDK for Android). The Facebook authentication process returns (on success) an object from which I can get the user's email then I save it in my Endpoints class using Datastore API. To check if user already logged in I chose the SharedPreferences approach with GSON library to parse objects into JSON String and save them in the prefs.
Links and my sample codes below :
Regarding the Authenticator I found this SO answer
More info about Facebook login method
Saving custom objects in SharedPreferences
Getting user's email through Facebook auth
private void onSessionStateChange(Session session, SessionState state, Exception exception) {
if (state.isOpened()) {
if (isSessionCalled == false) {
Log.i(TAG, "Logged in...");
System.out.println("Token=" + session.getAccessToken());
new Request(
session,
"/me",
null,
HttpMethod.GET,
new Request.Callback() {
public void onCompleted(Response response) {
if (response != null) {
GraphObject object = response.getGraphObject();
String email = (String) object.getProperty("email");
Log.i(TAG, "user email : " + email);
String firstName = (String) object.getProperty("first_name");
String lastName = (String) object.getProperty("last_name");
mUserTask = new UserAsyncTask();
mUserTask.execute(email);
}
}
}
).executeAsync();
isSessionCalled = true;
}
else {
Log.w(TAG, "session called twice");
}
}
else if (state.isClosed()) {
Log.i(TAG, "Logged out...");
}
}
Storing the user in my backend :
#ApiMethod(name = "storeUserModel")
public UserModel storeUserModel(UserModel userModel) throws UserAlreadyExistsException, UserNotFoundException {
logger.info("inside storeUser");
String email = userModel.getEmail();
UserModel checkUser = getUserModel(email);
logger.info("after getUserModel with email " + email);
if (checkUser == null) {
logger.info("inside checkUser is NULL");
DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService();
Transaction txn = datastoreService.beginTransaction();
try {
Entity userEntity = new Entity(UserModel.class.getSimpleName(), email);
userEntity.setProperty("nickname", userModel.getNickname());
// TODO save the pheromones with the key of userEntity
datastoreService.put(userEntity);
txn.commit();
storePheromoneList(userModel.getPheromoneList(), userEntity.getKey(), datastoreService);
} finally {
if (txn.isActive()) {
logger.severe("rolled back with email : " + email);
txn.rollback();
}
}
}
else {
throw new UserAlreadyExistsException();
}
return userModel;
}
A class that triggers calls to my backend
public class EndpointsServer implements Server {
private static final String TAG = "EndpointsServer";
final UserModelApi userEndpointsApi;
public EndpointsServer() {
UserModelApi.Builder builder = new UserModelApi.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null)
.setRootUrl("http://10.0.2.2:8080/_ah/api/")
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
#Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
});
userEndpointsApi = builder.build();
}
#Override
public User getUser(String email) {
User user = null;
try {
Log.d(TAG, "in getUser with email " +email);
// get user from db
UserModel userModel = userEndpointsApi.getUserModel(email).execute();
if (userModel != null) {
Log.d(TAG, "user != null with email " + email);
user = new User(userModel);
}
} catch (IOException e) {
e.printStackTrace();
}
return user;
}
}
Storing user on successful login :
String userString = gson.toJson(user, User.class);
SharedPreferences.Editor editor = preferences.edit();
editor.putString(USER_KEY, userString);
editor.commit();
There's more to it like another client side class to build the api call to the backend and lots of other details. I can post it if you want.
I can't speak on Java but I started with Python by looking at this repo on Github:
https://github.com/loudnate/appengine-endpoints-auth-example
This shows you an example on how to write a custom authenticator with Facebook Login. Writing your own authentication I think you should be able to find some examples. The only thing you need to do after is to use the same User entity.
And I suggest you do some reading on how OAUTH 2.0 works so you don't get too confused on the task you need to do.
Basically:
On your client side, whether web or android, get a facebook access token, sends it to your endpoint service. Exchange for a access token of your own. At the same time, create your User object in datastore and associate the access token.
Then all your subsequent request should use this access token to get access to your endpoint backend. (Do a user check on your endpoint API method.)
I am trying to write a server side Facebook Notification service in my GWT app. The idea is that I will run this as a timertask or cron job sort of.
With the code below, I get a login URL, I want to be able to Login programmatically as this is intended to be automated (Headless sort of way). I was gonna try do a submit with HTMLunit but I thought the FB API should cater for this.
Please advice.
public class NotificationServiceImpl extends RemoteServiceServlet implements NotificationService {
/**serialVersionUID*/
private static final long serialVersionUID = 6893572879522128833L;
private static final String FACEBOOK_USER_CLIENT = "facebook.user.client";
long facebookUserID;
public String sendMessage(Notification notification) throws IOException {
String api_key = notification.getApi_key();
String secret = notification.getSecret_key();
try {
// MDC.put(ipAddress, req.getRemoteAddr());
HttpServletRequest request = getThreadLocalRequest();
HttpServletResponse response = getThreadLocalResponse();
HttpSession session = getThreadLocalRequest().getSession(true);
// session.setAttribute("api_key", api_key);
IFacebookRestClient<Document> userClient = getUserClient(session);
if(userClient == null) {
System.out.println("User session doesn't have a Facebook API client setup yet. Creating one and storing it in the user's session.");
userClient = new FacebookXmlRestClient(api_key, secret);
session.setAttribute(FACEBOOK_USER_CLIENT, userClient);
}
System.out.println("Creating a FacebookWebappHelper, which copies fb_ request param data into the userClient");
FacebookWebappHelper<Document> facebook = new FacebookWebappHelper<Document>(request, response, api_key, secret, userClient);
String nextPage = request.getRequestURI();
nextPage = nextPage.substring(nextPage.indexOf("/", 1) + 1); //cut out the first /, the context path and the 2nd /
System.out.println(nextPage);
boolean redirectOccurred = facebook.requireLogin(nextPage);
if(redirectOccurred) {
return null;
}
redirectOccurred = facebook.requireFrame(nextPage);
if(redirectOccurred) {
return null;
}
try {
facebookUserID = userClient.users_getLoggedInUser();
if (userClient.users_hasAppPermission(Permission.STATUS_UPDATE)) {
userClient.users_setStatus("Im testing Facebook With Java! This status is written using my Java code! Can you see it? Cool :D", false);
}
} catch(FacebookException ex) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error while fetching user's facebook ID");
System.out.println("Error while getting cached (supplied by request params) value " +
"of the user's facebook ID or while fetching it from the Facebook service " +
"if the cached value was not present for some reason. Cached value = {}" + userClient.getCacheUserId());
return null;
}
// MDC.put(facebookUserId, String.valueOf(facebookUserID));
// chain.doFilter(request, response);
} finally {
// MDC.remove(ipAddress);
// MDC.remove(facebookUserId);
}
return String.valueOf(facebookUserID);
}
public static FacebookXmlRestClient getUserClient(HttpSession session) {
return (FacebookXmlRestClient)session.getAttribute(FACEBOOK_USER_CLIENT);
}
}
Error message:
[ERROR] com.google.gwt.user.client.rpc.InvocationException: <script type="text/javascript">
[ERROR] top.location.href = "http://www.facebook.com/login.php?v=1.0&api_key=MY_KEY&next=notification";
[ERROR] </script>