Skip to content

Conversation

@Fayeredd
Copy link
Contributor

Security compatibility using JWT.

Temporary token generator/validator is JwtTokenUtil.

Security classes (by package):
com.revature.config: WebSecurityConfig

com.revature.repositories: UserRepository

com.revature.model.security: Authority, AuthorityName, User

com.revature.security: JwtAuthenticationEntryPoint, JwtAuthenticationFilter, JwtAuthenticationRequest, JwtAuthenticationSuccessHandler, JwtTokenUtil (TEMP), JwtUser, JwtUserFactory

com.revature.security.controllers: AuthenticationRestController, UserRestController

com.revature.security.exceptions: JwtTokenMissingException

com.revature.security.service: JwtAuthenticationResponse, JwtUserDetailsServiceImpl


@EnableJpaRepositories(basePackageClasses=PersonRepository.class)
@SpringBootApplication
public class InterviewEvaluationsApplication {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR Add a private constructor to hide the implicit public one. rule

@cmatheny
Copy link
Contributor

@tjkemper please review this.

@tjkemper
Copy link
Contributor

This is a lot in one PR.

Initial reactions

  • The Swagger endpoint /swagger-ui.html should load without a JWT in the request
  • Each request must have a JWT signed with some secret: dog
    • The API doesn't handle passwords or generate tokens

@Fayeredd
Copy link
Contributor Author

Claims have a secret: mySecret (see application.yml and JwtTokenUtil)
This secret is validated through the JwtAuthenticationFilter.
The password/token generation is handled by JwtTokenUtil, a test class placed in the structure to test the rest of it. It can be replaced later with an external generator.

… extra to be discarded when external token generation/validation is implemented
private UserRepository userRepository;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MINOR Remove the declaration of thrown exception 'org.springframework.security.core.userdetails.UsernameNotFoundException' which is a runtime exception. rule

private final boolean enabled;
private final Date lastPasswordResetDate;

public JwtUser(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR Constructor has 9 parameters, which is greater than 7 authorized. rule

try {
final Claims claims = getClaimsFromToken(token);
audience = (String) claims.get(CLAIM_KEY_AUDIENCE);
} catch (Exception e) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR Either log or rethrow this exception. rule

Date expires;
try {
final Claims claims = getClaimsFromToken(token);
expires = claims.getExpiration();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR NullPointerException might be thrown as 'claims' is nullable here rule

try {
final Claims claims = getClaimsFromToken(token);
expires = claims.getExpiration();
} catch (Exception e) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR Either log or rethrow this exception. rule

Date created;
try {
final Claims claims = getClaimsFromToken(token);
created = new Date((Long) claims.get(CLAIM_KEY_CREATED));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR NullPointerException might be thrown as 'claims' is nullable here rule

try {
final Claims claims = getClaimsFromToken(token);
created = new Date((Long) claims.get(CLAIM_KEY_CREATED));
} catch (Exception e) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR Either log or rethrow this exception. rule

String refreshedToken;
try {
final Claims claims = getClaimsFromToken(token);
claims.put(CLAIM_KEY_CREATED, new Date());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR NullPointerException might be thrown as 'claims' is nullable here rule

.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (ExpiredJwtException | MalformedJwtException | SignatureException | UnsupportedJwtException | IllegalArgumentException e) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR Either log or rethrow this exception. rule

final Claims claims = getClaimsFromToken(token);
claims.put(CLAIM_KEY_CREATED, new Date());
refreshedToken = generateToken(claims);
} catch (Exception e) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR Either log or rethrow this exception. rule

String username;
try {
final Claims claims = getClaimsFromToken(token);
username = claims.getSubject();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR NullPointerException might be thrown as 'claims' is nullable here rule

try {
final Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR Either log or rethrow this exception. rule

String audience;
try {
final Claims claims = getClaimsFromToken(token);
audience = (String) claims.get(CLAIM_KEY_AUDIENCE);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR NullPointerException might be thrown as 'claims' is nullable here rule

Copy link
Contributor

@tjkemper tjkemper left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall good progress.

Focus on verifying the JWT has the correct signature.

The API does not need to worry about usernames/passwords or generating tokens.

authReq.getPassword())
);

SecurityContextHolder.getContext().setAuthentication(authentication);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's avoid using SecurityContext. We want our API to be stateless.

private String tokenHeader;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're on the right track here.

May want something like:

try {

    Jwts.parser().setSigningKey(key).parseClaimsJws(compactJws);

    //OK, we can trust this JWT

} catch (SignatureException e) {

    //don't trust the JWT!
}

Notice we do not know anything about the user. We only care that the JWT was signed with our secret and it wasn't tampered with.

@Fayeredd
Copy link
Contributor Author

Fayeredd commented Apr 12, 2017

Build fails for non-existent dependency (JwtAuthenticationFilter [8,37])

The dependency is a local second project. The dependency is in the POM and the directory is in the classpath. Any ideas as to why it can't find this dependency would be appreciated.

POM:

<dependency>
<groupId>com.revature</groupId>
<artifactId>RevatureSecurityLogin</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>

Classpath:
<classpathentry kind="var" path="M2_REPO/com/revature/RevatureSecurityLogin/1.0-SNAPSHOT/RevatureSecurityLogin-1.0-SNAPSHOT.jar"/>

…are to be removed and placed in a seperate Authorization Server service. All files in the 'static' folder likewise belong to the future Authorization Server service

String token = req.getHeader(tokenHeader);
String username = jwtTokenUtil.getUsernameFromToken(token);
JwtUser user = (JwtUser) userDetailsService.loadUserByUsername(username);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MINOR Immediately return this expression instead of assigning it to the temporary variable "user". rule


//This value can be found in the application.yml file
@RequestMapping(value = "${jwt.route.authentication.path}", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtAuthenticationRequest authReq, Device dev) throws AuthenticationException{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR Remove usage of generic wildcard type. rule
MINOR Remove the declaration of thrown exception 'org.springframework.security.core.AuthenticationException' which is a runtime exception. rule


//This value can be found in the application.yml file
@RequestMapping(value = "${jwt.route.authentication.refresh}", method = RequestMethod.GET)
public ResponseEntity<?> refreshAndGetAuthenticationToken(HttpServletRequest req){

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR Remove usage of generic wildcard type. rule

@octocat-jedi
Copy link

SonarQube analysis reported 18 issues

  • MAJOR 15 major
  • MINOR 3 minor

Watch the comments in this conversation to review them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants