Skip to content

Commit

Permalink
[SERVER] update database using UUID as JWT's jti
Browse files Browse the repository at this point in the history
  • Loading branch information
Huynh Thanh Binh committed Aug 23, 2020
1 parent 27db117 commit 871e23a
Show file tree
Hide file tree
Showing 12 changed files with 50 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.bht.saigonparking.common.auth;

import java.util.UUID;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

Expand All @@ -17,14 +19,14 @@ public interface SaigonParkingAuthentication {
SaigonParkingTokenBody parseJwtToken(@NotEmpty String jsonWebToken);

/* 1st: tokenId, 2nd: token */
Pair<String, String> generateAccessToken(@NotNull Long userId, @NotEmpty String userRole);
Pair<UUID, String> generateAccessToken(@NotNull Long userId, @NotEmpty String userRole);

/* 1st: tokenId, 2nd: token */
Pair<String, String> generateRefreshToken(@NotNull Long userId, @NotEmpty String userRole);
Pair<UUID, String> generateRefreshToken(@NotNull Long userId, @NotEmpty String userRole);

/* 1st: tokenId, 2nd: token */
Pair<String, String> generateActivateAccountToken(@NotNull Long userId, @NotEmpty String userRole);
Pair<UUID, String> generateActivateAccountToken(@NotNull Long userId, @NotEmpty String userRole);

/* 1st: tokenId, 2nd: token */
Pair<String, String> generateResetPasswordToken(@NotNull Long userId, @NotEmpty String userRole);
Pair<UUID, String> generateResetPasswordToken(@NotNull Long userId, @NotEmpty String userRole);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.util.Objects;
import java.util.Properties;
import java.util.Random;
import java.util.UUID;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
Expand Down Expand Up @@ -84,17 +85,17 @@ private byte[] getSecretKeyByteArray(@NotEmpty String keyPath) throws IOExceptio
.getBytes(StandardCharsets.UTF_8));
}

private Pair<String, String> generateJwtToken(@NotNull SaigonParkingTokenType type,
@NotNull Long userId,
@NotEmpty String userRole,
@NotNull Integer timeAmount,
@NotNull ChronoUnit timeUnit) {
private Pair<UUID, String> generateJwtToken(@NotNull SaigonParkingTokenType type,
@NotNull Long userId,
@NotEmpty String userRole,
@NotNull Integer timeAmount,
@NotNull ChronoUnit timeUnit) {
Instant now = Instant.now();
Integer factor = new Random().nextInt(MAX_RANDOM_EXCLUSIVE);
String tokenId = UUID_GENERATOR.generate().toString();
UUID tokenUuid = UUID_GENERATOR.generate();

return Pair.of(tokenId, Jwts.builder()
.setId(tokenId)
return Pair.of(tokenUuid, Jwts.builder()
.setId(tokenUuid.toString())
.setIssuer(SAIGON_PARKING_ISSUER)
.claim(USER_ROLE_KEY_NAME, userRole)
.claim(FACTOR_KEY_NAME, factor)
Expand Down Expand Up @@ -123,22 +124,22 @@ public SaigonParkingTokenBody parseJwtToken(@NotEmpty String jsonWebToken) {
}

@Override
public Pair<String, String> generateAccessToken(@NotNull Long userId, @NotEmpty String userRole) {
public Pair<UUID, String> generateAccessToken(@NotNull Long userId, @NotEmpty String userRole) {
return generateJwtToken(ACCESS_TOKEN, userId, userRole, 30, ChronoUnit.MINUTES);
}

@Override
public Pair<String, String> generateRefreshToken(@NotNull Long userId, @NotEmpty String userRole) {
public Pair<UUID, String> generateRefreshToken(@NotNull Long userId, @NotEmpty String userRole) {
return generateJwtToken(REFRESH_TOKEN, userId, userRole, 30, ChronoUnit.DAYS);
}

@Override
public Pair<String, String> generateActivateAccountToken(@NotNull Long userId, @NotEmpty String userRole) {
public Pair<UUID, String> generateActivateAccountToken(@NotNull Long userId, @NotEmpty String userRole) {
return generateJwtToken(ACTIVATE_TOKEN, userId, userRole, 5, ChronoUnit.MINUTES);
}

@Override
public Pair<String, String> generateResetPasswordToken(@NotNull Long userId, @NotEmpty String userRole) {
public Pair<UUID, String> generateResetPasswordToken(@NotNull Long userId, @NotEmpty String userRole) {
return generateJwtToken(RESET_PW_TOKEN, userId, userRole, 5, ChronoUnit.MINUTES);
}
}
Binary file modified dev/database/backup/auth.bak
Binary file not shown.
Binary file modified dev/database/diagram/auth.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified dev/database/script/schema/auth_schema.sql
100644 → 100755
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.bht.saigonparking.service.auth.entity;

import java.util.UUID;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
Expand All @@ -9,7 +11,7 @@
import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.NaturalIdCache;
import org.hibernate.annotations.SelectBeforeUpdate;
import org.hibernate.validator.constraints.Length;
import org.hibernate.annotations.Type;

import lombok.AllArgsConstructor;
import lombok.Builder;
Expand Down Expand Up @@ -40,10 +42,10 @@ public final class UserTokenEntity {
@Column(name = "[USER_ID]")
private Long userId;

@Length(max = 40)
@Type(type = "uuid-char")
@NaturalId(mutable = true)
@Column(name = "[TOKEN_ID]", unique = true, nullable = false)
private String tokenId;
@Column(name = "[TOKEN_ID]", unique = true, nullable = false, columnDefinition = "UNIQUEIDENTIFIER")
private UUID tokenId;

@Version
@EqualsAndHashCode.Exclude
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import javax.persistence.EntityNotFoundException;

Expand Down Expand Up @@ -72,7 +73,7 @@ public final class AuthServiceInterceptor implements ServerInterceptor {
@Getter
private final Context.Key<String> userRoleContext = Context.key("userRole");
@Getter
private final Context.Key<String> tokenIdContext = Context.key("tokenId");
private final Context.Key<UUID> tokenIdContext = Context.key("tokenId");
@Getter
private final Context.Key<SaigonParkingTokenType> tokenTypeContext = Context.key("tokenType");
@Getter
Expand Down Expand Up @@ -137,12 +138,11 @@ public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, Re

userId = 0L;
userRole = "UNRECOGNIZED";
tokenId = "";
tokenId = null;
tokenType = null;
exp = new Date();

} else if (token == null && internalServiceCodeString == null) { /* spam requests */

throw new MissingTokenException();

} else if (token != null) { /* external requests */
Expand All @@ -159,7 +159,7 @@ public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, Re

userId = 1L;
userRole = "ADMIN";
tokenId = "";
tokenId = null;
tokenType = null;
exp = new Date();
}
Expand Down Expand Up @@ -200,7 +200,7 @@ public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, Re
return Contexts.interceptCall(Context.current()
.withValue(userIdContext, userId)
.withValue(userRoleContext, userRole)
.withValue(tokenIdContext, tokenId)
.withValue(tokenIdContext, (tokenId != null) ? UUID.fromString(tokenId) : null)
.withValue(tokenTypeContext, tokenType)
.withValue(expContext, exp),
wrappedServerCall,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.bht.saigonparking.service.auth.repository;

import java.util.Optional;
import java.util.UUID;

import javax.validation.constraints.NotEmpty;

Expand All @@ -18,13 +19,12 @@
public interface UserTokenRepository extends JpaRepository<UserTokenEntity, Long> {

/**
*
* self-implement findByTokenId method
* in order to prevent N+1 problem
* using optional to catch null return
*/
@Query("SELECT UT " +
"FROM UserTokenEntity UT " +
"WHERE UT.tokenId = ?1")
Optional<UserTokenEntity> findByTokenId(@NotEmpty String tokenId);
Optional<UserTokenEntity> findByTokenId(@NotEmpty UUID tokenId);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.bht.saigonparking.service.auth.service;

import java.util.Date;
import java.util.UUID;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
Expand Down Expand Up @@ -54,7 +55,7 @@ Pair<String, String> validateLogin(@NotEmpty String username,
*/
Triple<String, String, String> generateNewToken(@NotNull Long userId,
@NotNull Date currentExp,
@NotEmpty String currentTokenId,
@NotEmpty UUID currentTokenId,
boolean currentIsRefreshToken);

/**
Expand All @@ -66,6 +67,6 @@ Triple<String, String, String> generateNewToken(@NotNull Long userId,
*/
Triple<String, String, String> activateNewAccount(@NotNull Long userId,
@NotNull Date currentExp,
@NotEmpty String currentTokenId,
@NotEmpty UUID currentTokenId,
boolean currentIsRefreshToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public final class AuthServiceGrpcImpl extends AuthServiceGrpc.AuthServiceImplBa

private final AuthService authService;
private final AuthServiceInterceptor authServiceInterceptor;

private final UserServiceGrpc.UserServiceBlockingStub userServiceBlockingStub;

@Override
Expand Down Expand Up @@ -132,8 +133,7 @@ public void registerUser(RegisterRequest request, StreamObserver<StringValue> re
@Override
public void sendResetPasswordEmail(StringValue request, StreamObserver<StringValue> responseObserver) {
try {
StringValue email = StringValue.of(authService
.sendResetPasswordEmail(request.getValue()));
StringValue email = StringValue.of(authService.sendResetPasswordEmail(request.getValue()));

responseObserver.onNext(email);
responseObserver.onCompleted();
Expand All @@ -154,8 +154,7 @@ public void sendResetPasswordEmail(StringValue request, StreamObserver<StringVal
@Override
public void sendActivateAccountEmail(StringValue request, StreamObserver<StringValue> responseObserver) {
try {
StringValue email = StringValue.of(authService
.sendActivateAccountEmail(request.getValue()));
StringValue email = StringValue.of(authService.sendActivateAccountEmail(request.getValue()));

responseObserver.onNext(email);
responseObserver.onCompleted();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import static com.bht.saigonparking.common.constant.SaigonParkingMessageQueue.MAIL_TOPIC_ROUTING_KEY;
import static com.bht.saigonparking.common.constant.SaigonParkingMessageQueue.USER_TOPIC_ROUTING_KEY;

import java.util.UUID;

import javax.persistence.EntityNotFoundException;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
Expand Down Expand Up @@ -79,22 +81,20 @@ public void sendMail(@NotNull MailRequestType type,
.build());
}

public void saveUserRefreshToken(@NotNull Long userId, @NotEmpty String tokenId) {
public void saveUserRefreshToken(@NotNull Long userId, @NotEmpty UUID tokenId) {
try {
/* update refresh token id to database if already existed */
UserTokenEntity userTokenEntity = userTokenRepository.findById(userId).orElseThrow(EntityNotFoundException::new);
userTokenEntity.setTokenId(tokenId);
userTokenRepository.saveAndFlush(userTokenEntity);

} catch (EntityNotFoundException entityNotFoundException) {

/* save new refresh token id to database if not existed before */
UserTokenEntity userTokenEntity = UserTokenEntity.builder().userId(userId).tokenId(tokenId).build();
userTokenRepository.saveAndFlush(userTokenEntity);

} finally {
LoggingUtil.log(Level.INFO, "SERVICE", "Success",
String.format("saveUserRefreshToken(%d, %s)", userId, tokenId));
LoggingUtil.log(Level.INFO, "SERVICE", "Success", String.format("saveUserRefreshToken(%d, %s)", userId, tokenId));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static com.bht.saigonparking.api.grpc.mail.MailRequestType.RESET_PASSWORD;

import java.util.Date;
import java.util.UUID;

import javax.persistence.EntityNotFoundException;
import javax.validation.constraints.NotEmpty;
Expand Down Expand Up @@ -65,8 +66,8 @@ public Pair<String, String> validateLogin(@NotEmpty String username,
context.run(() -> authServiceImplHelper.updateUserLastSignIn(user.getId()));

/* Generate new access token, new refresh token for user with Id, Role */
Pair<String, String> generatedAccessToken = authentication.generateAccessToken(user.getId(), user.getRole().toString());
Pair<String, String> generatedRefreshToken = authentication.generateRefreshToken(user.getId(), user.getRole().toString());
Pair<UUID, String> generatedAccessToken = authentication.generateAccessToken(user.getId(), user.getRole().toString());
Pair<UUID, String> generatedRefreshToken = authentication.generateRefreshToken(user.getId(), user.getRole().toString());

/* Asynchronously save user token to the database */
authServiceImplHelper.saveUserRefreshToken(user.getId(), generatedRefreshToken.getFirst());
Expand Down Expand Up @@ -136,7 +137,7 @@ public String sendActivateAccountEmail(@NotEmpty String username) {
@Override
public Triple<String, String, String> generateNewToken(@NotNull Long userId,
@NotNull Date currentExp,
@NotEmpty String currentTokenId,
@NotEmpty UUID currentTokenId,
boolean currentIsRefreshToken) {

User user = userServiceBlockingStub.getUserById(Int64Value.of(userId));
Expand All @@ -147,7 +148,7 @@ public Triple<String, String, String> generateNewToken(@NotNull Long userId,
@Override
public Triple<String, String, String> activateNewAccount(@NotNull Long userId,
@NotNull Date currentExp,
@NotEmpty String currentTokenId,
@NotEmpty UUID currentTokenId,
boolean currentIsRefreshToken) {

User user = userServiceBlockingStub.getUserById(Int64Value.of(userId));
Expand All @@ -162,7 +163,7 @@ public Triple<String, String, String> activateNewAccount(@NotNull Long userId,
@SuppressWarnings("java:S2201")
private Triple<String, String, String> generateNewToken(@NotNull User user,
@NotNull Date currentExp,
@NotEmpty String currentTokenId,
@NotEmpty UUID currentTokenId,
boolean currentIsRefreshToken) {
try {
if (currentIsRefreshToken) {
Expand All @@ -173,15 +174,15 @@ private Triple<String, String, String> generateNewToken(@NotNull User user,
throw new InvalidRefreshTokenException();
}

Pair<String, String> generatedAccessToken = authentication.generateAccessToken(user.getId(), user.getRole().toString());
Pair<UUID, String> generatedAccessToken = authentication.generateAccessToken(user.getId(), user.getRole().toString());

if (((currentExp.getTime() - new Date().getTime()) / 86400000) > 7) { /* Token not nearly expire */
return Triple.of(user.getUsername(), generatedAccessToken.getSecond(), "");

} else { /* Token nearly expire */

/* Generate new refresh token for user with Id, Role */
Pair<String, String> generatedRefreshToken = authentication.generateRefreshToken(user.getId(), user.getRole().toString());
Pair<UUID, String> generatedRefreshToken = authentication.generateRefreshToken(user.getId(), user.getRole().toString());
authServiceImplHelper.saveUserRefreshToken(user.getId(), generatedRefreshToken.getFirst());

return Triple.of(user.getUsername(), generatedAccessToken.getSecond(), generatedRefreshToken.getSecond());
Expand Down

0 comments on commit 871e23a

Please sign in to comment.