In this blog, I discuss the complete implementation of SUSI server when a user signs up with SUSI. Since public signup is enabled, minimal user role to access the servlet is ANONYMOUS. Given below is the API endpoint at which a signup request has to be made.
http://api.susi.ai/aaa/signup.json
Here we do not have much options unlike Login service. . Requests can be made in following ways with different parameters:
http://api.susi.ai/aaa/signup.json?signup=test@fossasia.com&password=Rest@123
http://api.susi.ai/aaa/signup.json?getParameters=true
http://api.susi.ai/aaa/signup.json?access_token={30 characters long access token}&validateEmail=test@fossasia.com&request_session=true
First one is the request which client makes, asking the server to register the user with email id test@fossasia.com and password as Reset@123. On receiving a request which has parameters signup and password, server looks back in the database and verifies that a user with same email id exists or not. If email id already exists, server throws an error stating “email already taken”. Given below is a small code snippet for better understanding.
ClientCredential credential = new ClientCredential(ClientCredential.Type.passwd_login, signup);
Authentication authentication = DAO.getAuthentication(credential);
if (authentication.getIdentity() != null) {
throw new APIException(422, "email already taken");
}
If client is not found in the database, a new identity is generated for the user and is set in authentication.json file. Next, a random salt string is generated which acts as salt to hash the user’s password. Since the same salt will be required at the time of matching the password for validation, we store both the salt and password hash in authentication.json file.
Now, if the database is going to encounter it’s first user, Automatically, BUREAUCRAT (Role with maximum power) will be assigned as his/her user role. To do this, we first get a list of all the users with the help of DAO class and count number of users present in it. Take a look at the code given below,
Authorization authorization = DAO.getAuthorization(identity);
Collection<ClientIdentity> authorized = DAO.getAuthorizedClients();
List<String> keysList = new ArrayList<String>();
authorized.forEach(client -> keysList.add(client.toString()));
String[] keysArray = keysList.toArray(new String[keysList.size()]);
if(keysArray.length == 1) {
authorization.setUserRole(UserRole.BUREAUCRAT);
} else {
authorization.setUserRole(UserRole.USER);
}
In further steps, a random token is generated and marked as the verification token for the user who just signed-up. Other attributes for the token are also set like expiry time. Finally, account verification mail’s template is picked from “conf/templates/verification-mail” file and used as the body for the user verification email.
The second type of request is quite simple which is just to request a regular expression from the server to give proper information to the user. It’s code is quite easy and self understandable.
Third type of request is sent to the server when user clicks on the link received in the account verification email. Taking a closer look at the account verification link, one could easily decode that the request would be redirected to /aaa/signup.json with parameters
- Access_token : 30 characters long access token
- validateEmail : user’s email id
- request_session : true
Access_token makes up the unique id for the user. ValidateEmail is to make it easier for server to generate a client identity. Let us now understand how server picks up each parameter and activates the user’s account.
if (post.get("validateEmail", null) != null) {
if((auth.getIdentity().getName().equals(post.get("validateEmail", null)) && auth.getIdentity().isEmail()) // the user is logged in via an access token from the email {
ClientCredential credential = new ClientCredential(ClientCredential.Type.passwd_login,
auth.getIdentity().getName());
Authentication authentication = DAO.getAuthentication(credential);
if (authentication.getIdentity() == null) {
authentication.delete();
throw new APIException(400, "Bad request");
}
authentication.put("activated", true);
result.put("accepted", true);
result.put("message", "You successfully verified your account!");
return new ServiceResponse(result);
}
throw new APIException(400, "Bad request"); // do not leak if user exists or not
}
In the above code snippet, an object of ClientCredential class is generated and used to get authentication object for the user. If user is not found (name in authentication object is null), server throws error with error message “Bad Request”. Finally, If no error was encountered, server activates the user.
In getDefaultPermissions() function, permission for each user role is specified. All the users registered are allowed to register other users, but no user with anonymous user role can register anyone else except him/her.
Resources