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 valid
public ApplicationConfig checkSecurityRoles() {
app.beforeMatched(securityController::authenticate); // check if there is a valid token in the header
app.beforeMatched(securityController::authorize); // check if the user has the required role
return appConfig;
}
- If it is valid the user is added to the context as an attribute and the request is passed on to the endpoint.
- If the JWT token is valid, 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 /register // returns a JWT token
- POST /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