API Security
- The user sends a register (POST) request to the server with a username and password.
- The server checks if the username is taken.
- If the username is taken, the server returns an error.
- If the username is not taken, the server creates a user and returns a JWT token.
- The user sends a login (POST) request to the server with a username and password.
- The server checks if the username and password are valid.
- If the username and password are valid, the server returns a JWT token.
- If the username and password are invalid, the server returns an error.
- The user sends a request to a protected endpoint with the JWT token in the header (‘Authorization’: ‘Bearer ’ + token).
- A
beforefilter calls theauthenticatemethod in the SecurityController and check if the token is validIn Javalin.create(config -> {...}):
config.routes.beforeMatched(securityController::authenticate);
config.routes.beforeMatched(securityController::authorize);
- If it is valid the user is added to the context as an attribute and the request is passed on to the next filter where authorize is called.
- The UserDTO that was attached to the request in the first (authenticate filter) is used in the second (authorize filter), the server checks if the user has the required roles to access the endpoint.
- If the JWT token is invalid or missing, the server returns an error.
- If the user doesn’t have the required roles, the server returns an error.
- Create an endpoint that can only be accessed by authenticated users with a role of ADMIN.
- The endpoint should return an error if the JWT token is invalid.
- The endpoint should return an error if the JWT token is valid but the user doesn’t have the ADMIN role.
- The endpoint should return the protected resource if the JWT token is valid and the user is authorized.
- Add more endpoints so that there are endpoints for only USER and only ADMIN roles and endpoints that are open to all users.
- Use Rest Assured, JUnit 5, and Hamcrest matchers to test all the finished endpoints.
- Test protected Endpoints by storing a valid JWT token in a variable and use it in the test. Below security exercise builds on the previous weeks Rest project. Either use the Hotel project from last week or create a new Rest project to your liking. Then apply Register, Login, Password hashing, and JWT security to the project as specified below.
- Create User entity: username, password
- Create constructor that hashes password
- Create method: verifyPassword(String password)
- Create Role @ManyToMany with User
- User::addRole, User::removeRole
- SecurityDAO implements ISecurityDAO (from toolbox->security->API Security)
- Create a
UserEntity and aRoleEntity withManyToManyrelationship between them - Let the
Userimplement an interface with like this one:
public interface ISecurityUser {
Set<String> getRolesAsStrings();
boolean verifyPassword(String pw);
void addRole(Role role);
void removeRole(String role);
}
- Create a UserDAO that implements the following interface:
public interface ISecurityDAO {
User getVerifiedUser(String username, String password) throws ValidationException; // used for login
User createUser(String username, String password); // used for register
Role createRole(String role);
User addUserRole(String username, String role);
}
Use bcrypt to hash the password before storing it in the database.
bcrypt link: https://www.mindrot.org/projects/jBCrypt/
add pom.xml property:
<jbcrypt.version>0.4</jbcrypt.version><dependency> <groupId>org.mindrot</groupId> <artifactId>jbcrypt</artifactId> <version>${jbcrypt.version}</version> </dependency>
Hint: Hash the password in the User constructor:
BCrypt.hashpw(userPass, BCrypt.gensalt());Hint: Verify the password in the User´s verifyPassword method:
BCrypt.checkpw(password, this.password);
- Create a SecurityRoutes class with the following routes:
- POST /auth/register // returns a JWT token
- POST /auth/login // returns a JWT token
- Create a SecurityController that implements the following interface methods:
void login(Context ctx); // to get a token
void register(Context ctx); // to get a user
void authenticate(Context ctx); // to verify roles inside token
void authorize(Context ctx);
You will need these dependency in pom.xml file with various JWT utility classes:
<dependency> <groupId>com.github.Hartmannsolution</groupId> <artifactId>TokenSecurity</artifactId> <version>${token.security.version}</version> </dependency>And this property:
<token.security.version>1.0.4</token.security.version>You also need to add this to the pom.xml file just after the properties:
<repositories> <repository> <id>jitpack.io</id> <url>https://jitpack.io</url> </repository> </repositories>The jitpack.io repository is where the TokenSecurity library is located. It is not in the Maven Central Repository.
- SecurityController implements ISecurityController (void login(Context ctx); void register(Context ctx); void authenticate(Context ctx); void authorize(Context ctx));
- login() uses securityDAO.getVerifiedUser (Created in class tuesday) and createToken
- register() creates a new user with the securityDAO and returns token
- authenticate
- authorize
- Security routes