2FA support for platform authentication

This commit is contained in:
Viacheslav Klimov 2022-03-10 18:15:42 +02:00
parent e2c9a5ffdf
commit 1ad769048c
10 changed files with 157 additions and 114 deletions

View File

@ -15,12 +15,14 @@
*/ */
package org.thingsboard.server.config; package org.thingsboard.server.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.security.model.JwtToken; import org.thingsboard.server.common.data.security.model.JwtToken;
@Configuration @Component
@ConfigurationProperties(prefix = "security.jwt") @ConfigurationProperties(prefix = "security.jwt")
@Data
public class JwtSettings { public class JwtSettings {
/** /**
* {@link JwtToken} will expire after this time. * {@link JwtToken} will expire after this time.
@ -42,34 +44,10 @@ public class JwtSettings {
*/ */
private Integer refreshTokenExpTime; private Integer refreshTokenExpTime;
public Integer getRefreshTokenExpTime() { /**
return refreshTokenExpTime; * Issued when 2FA is being used.
} * Valid only for 2FA verification code checking.
* */
private Integer preVerificationTokenExpirationTime;
public void setRefreshTokenExpTime(Integer refreshTokenExpTime) {
this.refreshTokenExpTime = refreshTokenExpTime;
}
public Integer getTokenExpirationTime() {
return tokenExpirationTime;
}
public void setTokenExpirationTime(Integer tokenExpirationTime) {
this.tokenExpirationTime = tokenExpirationTime;
}
public String getTokenIssuer() {
return tokenIssuer;
}
public void setTokenIssuer(String tokenIssuer) {
this.tokenIssuer = tokenIssuer;
}
public String getTokenSigningKey() {
return tokenSigningKey;
}
public void setTokenSigningKey(String tokenSigningKey) {
this.tokenSigningKey = tokenSigningKey;
}
} }

View File

@ -35,6 +35,7 @@ import org.thingsboard.server.service.security.auth.mfa.config.TwoFactorAuthSett
import org.thingsboard.server.service.security.auth.mfa.config.account.TotpTwoFactorAuthAccountConfig; import org.thingsboard.server.service.security.auth.mfa.config.account.TotpTwoFactorAuthAccountConfig;
import org.thingsboard.server.service.security.auth.mfa.config.account.TwoFactorAuthAccountConfig; import org.thingsboard.server.service.security.auth.mfa.config.account.TwoFactorAuthAccountConfig;
import org.thingsboard.server.service.security.auth.mfa.provider.TwoFactorAuthProviderType; import org.thingsboard.server.service.security.auth.mfa.provider.TwoFactorAuthProviderType;
import org.thingsboard.server.service.security.model.JwtTokenPair;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.token.JwtTokenFactory; import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
@ -125,4 +126,22 @@ public class TwoFactorAuthController extends BaseController {
twoFactorAuthService.saveTwoFaSettings(getTenantId(), twoFactorAuthSettings); twoFactorAuthService.saveTwoFaSettings(getTenantId(), twoFactorAuthSettings);
} }
@PostMapping("/auth/2fa/verification/check")
@PreAuthorize("hasAuthority('PRE_VERIFICATION_TOKEN')")
public JwtTokenPair checkTwoFaVerificationCode(@RequestParam String verificationCode) throws ThingsboardException {
SecurityUser user = getCurrentUser();
boolean verificationSuccess = twoFactorAuthService.processByTwoFaProvider(user.getTenantId(), user.getId(),
(provider, providerConfig, accountConfig) -> {
return provider.checkVerificationCode(user, verificationCode, accountConfig);
});
if (verificationSuccess) {
return tokenFactory.createTokenPair(user);
} else {
throw new ThingsboardException("Verification code is incorrect", ThingsboardErrorCode.AUTHENTICATION);
}
}
} }

View File

@ -66,6 +66,7 @@ public class RefreshTokenAuthenticationProvider implements AuthenticationProvide
} else { } else {
securityUser = authenticateByPublicId(principal.getValue()); securityUser = authenticateByPublicId(principal.getValue());
} }
securityUser.setSessionId(unsafeUser.getSessionId());
if (tokenOutdatingService.isOutdated(rawAccessToken, securityUser.getId())) { if (tokenOutdatingService.isOutdated(rawAccessToken, securityUser.getId())) {
throw new CredentialsExpiredException("Token is outdated"); throw new CredentialsExpiredException("Token is outdated");

View File

@ -16,15 +16,18 @@
package org.thingsboard.server.service.security.auth.rest; package org.thingsboard.server.service.security.auth.rest;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.web.WebAttributes; import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.security.model.JwtToken;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository;
import org.thingsboard.server.service.security.auth.mfa.TwoFactorAuthService;
import org.thingsboard.server.service.security.auth.mfa.config.account.TwoFactorAuthAccountConfig;
import org.thingsboard.server.service.security.model.JwtTokenPair;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.token.JwtTokenFactory; import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
@ -33,37 +36,44 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.Optional;
import java.util.Map;
@Component(value = "defaultAuthenticationSuccessHandler") @Component(value = "defaultAuthenticationSuccessHandler")
@RequiredArgsConstructor
@Slf4j
public class RestAwareAuthenticationSuccessHandler implements AuthenticationSuccessHandler { public class RestAwareAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private final ObjectMapper mapper; private final ObjectMapper mapper;
private final JwtTokenFactory tokenFactory; private final JwtTokenFactory tokenFactory;
private final RefreshTokenRepository refreshTokenRepository; private final TwoFactorAuthService twoFactorAuthService;
@Autowired
public RestAwareAuthenticationSuccessHandler(final ObjectMapper mapper, final JwtTokenFactory tokenFactory, final RefreshTokenRepository refreshTokenRepository) {
this.mapper = mapper;
this.tokenFactory = tokenFactory;
this.refreshTokenRepository = refreshTokenRepository;
}
@Override @Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException { Authentication authentication) throws IOException, ServletException {
SecurityUser securityUser = (SecurityUser) authentication.getPrincipal(); SecurityUser securityUser = (SecurityUser) authentication.getPrincipal();
JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); JwtTokenPair tokenPair;
JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);
Map<String, String> tokenMap = new HashMap<String, String>(); Optional<TwoFactorAuthAccountConfig> twoFaAccountConfig = twoFactorAuthService.getTwoFaAccountConfig(securityUser.getTenantId(), securityUser.getId());
tokenMap.put("token", accessToken.getToken()); if (twoFaAccountConfig.isPresent()) {
tokenMap.put("refreshToken", refreshToken.getToken()); try {
twoFactorAuthService.processByTwoFaProvider(securityUser.getTenantId(), twoFaAccountConfig.get().getProviderType(),
(provider, providerConfig) -> {
provider.prepareVerificationCode(securityUser, providerConfig, twoFaAccountConfig.get());
});
tokenPair = new JwtTokenPair();
tokenPair.setToken(tokenFactory.createPreVerificationToken(securityUser).getToken());
} catch (Exception e) {
log.error("Failed to process 2FA for user {}. Falling back to plain auth", securityUser.getId(), e);
tokenPair = tokenFactory.createTokenPair(securityUser);
}
} else {
tokenPair = tokenFactory.createTokenPair(securityUser);
}
response.setStatus(HttpStatus.OK.value()); response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setContentType(MediaType.APPLICATION_JSON_VALUE);
mapper.writeValue(response.getWriter(), tokenMap);
mapper.writeValue(response.getWriter(), tokenPair);
clearAuthenticationAttributes(request); clearAuthenticationAttributes(request);
} }

View File

@ -19,10 +19,12 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
@ApiModel(value = "JWT Token Pair") @ApiModel(value = "JWT Token Pair")
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor
public class JwtTokenPair { public class JwtTokenPair {
@ApiModelProperty(position = 1, value = "The JWT Access Token. Used to perform API calls.", example = "AAB254FF67D..") @ApiModelProperty(position = 1, value = "The JWT Access Token. Used to perform API calls.", example = "AAB254FF67D..")

View File

@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import java.util.Collection; import java.util.Collection;
import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -31,6 +32,7 @@ public class SecurityUser extends User {
private Collection<GrantedAuthority> authorities; private Collection<GrantedAuthority> authorities;
private boolean enabled; private boolean enabled;
private UserPrincipal userPrincipal; private UserPrincipal userPrincipal;
private String sessionId;
public SecurityUser() { public SecurityUser() {
super(); super();
@ -44,6 +46,7 @@ public class SecurityUser extends User {
super(user); super(user);
this.enabled = enabled; this.enabled = enabled;
this.userPrincipal = userPrincipal; this.userPrincipal = userPrincipal;
this.sessionId = UUID.randomUUID().toString();
} }
public Collection<GrantedAuthority> getAuthorities() { public Collection<GrantedAuthority> getAuthorities() {
@ -71,4 +74,12 @@ public class SecurityUser extends User {
this.userPrincipal = userPrincipal; this.userPrincipal = userPrincipal;
} }
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
} }

View File

@ -15,25 +15,17 @@
*/ */
package org.thingsboard.server.service.security.model.token; package org.thingsboard.server.service.security.model.token;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.jsonwebtoken.Claims;
import org.thingsboard.server.common.data.security.model.JwtToken; import org.thingsboard.server.common.data.security.model.JwtToken;
public final class AccessJwtToken implements JwtToken { public final class AccessJwtToken implements JwtToken {
private final String rawToken; private final String rawToken;
@JsonIgnore
private transient Claims claims;
protected AccessJwtToken(final String token, Claims claims) { public AccessJwtToken(String rawToken) {
this.rawToken = token; this.rawToken = rawToken;
this.claims = claims;
} }
public String getToken() { public String getToken() {
return this.rawToken; return this.rawToken;
} }
public Claims getClaims() {
return claims;
}
} }

View File

@ -18,6 +18,7 @@ package org.thingsboard.server.service.security.model.token;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException; import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.SignatureAlgorithm;
@ -36,6 +37,7 @@ import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.model.JwtToken; import org.thingsboard.server.common.data.security.model.JwtToken;
import org.thingsboard.server.config.JwtSettings; import org.thingsboard.server.config.JwtSettings;
import org.thingsboard.server.service.security.exception.JwtExpiredTokenException; import org.thingsboard.server.service.security.exception.JwtExpiredTokenException;
import org.thingsboard.server.service.security.model.JwtTokenPair;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.UserPrincipal; import org.thingsboard.server.service.security.model.UserPrincipal;
@ -58,6 +60,7 @@ public class JwtTokenFactory {
private static final String IS_PUBLIC = "isPublic"; private static final String IS_PUBLIC = "isPublic";
private static final String TENANT_ID = "tenantId"; private static final String TENANT_ID = "tenantId";
private static final String CUSTOMER_ID = "customerId"; private static final String CUSTOMER_ID = "customerId";
private static final String SESSION_ID = "sessionId";
private final JwtSettings settings; private final JwtSettings settings;
@ -70,39 +73,28 @@ public class JwtTokenFactory {
* Factory method for issuing new JWT Tokens. * Factory method for issuing new JWT Tokens.
*/ */
public AccessJwtToken createAccessJwtToken(SecurityUser securityUser) { public AccessJwtToken createAccessJwtToken(SecurityUser securityUser) {
if (StringUtils.isBlank(securityUser.getEmail())) if (securityUser.getAuthority() == null) {
throw new IllegalArgumentException("Cannot create JWT Token without username/email");
if (securityUser.getAuthority() == null)
throw new IllegalArgumentException("User doesn't have any privileges"); throw new IllegalArgumentException("User doesn't have any privileges");
}
UserPrincipal principal = securityUser.getUserPrincipal(); UserPrincipal principal = securityUser.getUserPrincipal();
String subject = principal.getValue();
Claims claims = Jwts.claims().setSubject(subject); JwtBuilder jwtBuilder = setUpToken(securityUser, securityUser.getAuthorities().stream()
claims.put(SCOPES, securityUser.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList())); .map(GrantedAuthority::getAuthority).collect(Collectors.toList()), settings.getTokenExpirationTime());
claims.put(USER_ID, securityUser.getId().getId().toString()); jwtBuilder.claim(FIRST_NAME, securityUser.getFirstName())
claims.put(FIRST_NAME, securityUser.getFirstName()); .claim(LAST_NAME, securityUser.getLastName())
claims.put(LAST_NAME, securityUser.getLastName()); .claim(ENABLED, securityUser.isEnabled())
claims.put(ENABLED, securityUser.isEnabled()); .claim(IS_PUBLIC, principal.getType() == UserPrincipal.Type.PUBLIC_ID);
claims.put(IS_PUBLIC, principal.getType() == UserPrincipal.Type.PUBLIC_ID);
if (securityUser.getTenantId() != null) { if (securityUser.getTenantId() != null) {
claims.put(TENANT_ID, securityUser.getTenantId().getId().toString()); jwtBuilder.claim(TENANT_ID, securityUser.getTenantId().getId().toString());
} }
if (securityUser.getCustomerId() != null) { if (securityUser.getCustomerId() != null) {
claims.put(CUSTOMER_ID, securityUser.getCustomerId().getId().toString()); jwtBuilder.claim(CUSTOMER_ID, securityUser.getCustomerId().getId().toString());
} }
ZonedDateTime currentTime = ZonedDateTime.now(); String token = jwtBuilder.compact();
String token = Jwts.builder() return new AccessJwtToken(token);
.setClaims(claims)
.setIssuer(settings.getTokenIssuer())
.setIssuedAt(Date.from(currentTime.toInstant()))
.setExpiration(Date.from(currentTime.plusSeconds(settings.getTokenExpirationTime()).toInstant()))
.signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey())
.compact();
return new AccessJwtToken(token, claims);
} }
public SecurityUser parseAccessJwtToken(RawAccessJwtToken rawAccessToken) { public SecurityUser parseAccessJwtToken(RawAccessJwtToken rawAccessToken) {
@ -118,47 +110,40 @@ public class JwtTokenFactory {
SecurityUser securityUser = new SecurityUser(new UserId(UUID.fromString(claims.get(USER_ID, String.class)))); SecurityUser securityUser = new SecurityUser(new UserId(UUID.fromString(claims.get(USER_ID, String.class))));
securityUser.setEmail(subject); securityUser.setEmail(subject);
securityUser.setAuthority(Authority.parse(scopes.get(0))); securityUser.setAuthority(Authority.parse(scopes.get(0)));
String tenantId = claims.get(TENANT_ID, String.class);
if (tenantId != null) {
securityUser.setTenantId(TenantId.fromUUID(UUID.fromString(tenantId)));
} else if (securityUser.getAuthority() == Authority.SYS_ADMIN) {
securityUser.setTenantId(TenantId.SYS_TENANT_ID);
}
securityUser.setSessionId(claims.get(SESSION_ID, String.class));
if (securityUser.getAuthority() != Authority.PRE_VERIFICATION_TOKEN) {
securityUser.setFirstName(claims.get(FIRST_NAME, String.class)); securityUser.setFirstName(claims.get(FIRST_NAME, String.class));
securityUser.setLastName(claims.get(LAST_NAME, String.class)); securityUser.setLastName(claims.get(LAST_NAME, String.class));
securityUser.setEnabled(claims.get(ENABLED, Boolean.class)); securityUser.setEnabled(claims.get(ENABLED, Boolean.class));
boolean isPublic = claims.get(IS_PUBLIC, Boolean.class); boolean isPublic = claims.get(IS_PUBLIC, Boolean.class);
UserPrincipal principal = new UserPrincipal(isPublic ? UserPrincipal.Type.PUBLIC_ID : UserPrincipal.Type.USER_NAME, subject); UserPrincipal principal = new UserPrincipal(isPublic ? UserPrincipal.Type.PUBLIC_ID : UserPrincipal.Type.USER_NAME, subject);
securityUser.setUserPrincipal(principal); securityUser.setUserPrincipal(principal);
String tenantId = claims.get(TENANT_ID, String.class);
if (tenantId != null) {
securityUser.setTenantId(TenantId.fromUUID(UUID.fromString(tenantId)));
}
String customerId = claims.get(CUSTOMER_ID, String.class); String customerId = claims.get(CUSTOMER_ID, String.class);
if (customerId != null) { if (customerId != null) {
securityUser.setCustomerId(new CustomerId(UUID.fromString(customerId))); securityUser.setCustomerId(new CustomerId(UUID.fromString(customerId)));
} }
} else {
securityUser.setUserPrincipal(new UserPrincipal(UserPrincipal.Type.USER_NAME, subject));
}
return securityUser; return securityUser;
} }
public JwtToken createRefreshToken(SecurityUser securityUser) { public JwtToken createRefreshToken(SecurityUser securityUser) {
if (StringUtils.isBlank(securityUser.getEmail())) {
throw new IllegalArgumentException("Cannot create JWT Token without username/email");
}
ZonedDateTime currentTime = ZonedDateTime.now();
UserPrincipal principal = securityUser.getUserPrincipal(); UserPrincipal principal = securityUser.getUserPrincipal();
Claims claims = Jwts.claims().setSubject(principal.getValue());
claims.put(SCOPES, Collections.singletonList(Authority.REFRESH_TOKEN.name()));
claims.put(USER_ID, securityUser.getId().getId().toString());
claims.put(IS_PUBLIC, principal.getType() == UserPrincipal.Type.PUBLIC_ID);
String token = Jwts.builder() String token = setUpToken(securityUser, Collections.singletonList(Authority.REFRESH_TOKEN.name()), settings.getRefreshTokenExpTime())
.setClaims(claims) .claim(IS_PUBLIC, principal.getType() == UserPrincipal.Type.PUBLIC_ID)
.setIssuer(settings.getTokenIssuer()) .setId(UUID.randomUUID().toString()).compact();
.setId(UUID.randomUUID().toString())
.setIssuedAt(Date.from(currentTime.toInstant()))
.setExpiration(Date.from(currentTime.plusSeconds(settings.getRefreshTokenExpTime()).toInstant()))
.signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey())
.compact();
return new AccessJwtToken(token, claims); return new AccessJwtToken(token);
} }
public SecurityUser parseRefreshToken(RawAccessJwtToken rawAccessToken) { public SecurityUser parseRefreshToken(RawAccessJwtToken rawAccessToken) {
@ -177,9 +162,41 @@ public class JwtTokenFactory {
UserPrincipal principal = new UserPrincipal(isPublic ? UserPrincipal.Type.PUBLIC_ID : UserPrincipal.Type.USER_NAME, subject); UserPrincipal principal = new UserPrincipal(isPublic ? UserPrincipal.Type.PUBLIC_ID : UserPrincipal.Type.USER_NAME, subject);
SecurityUser securityUser = new SecurityUser(new UserId(UUID.fromString(claims.get(USER_ID, String.class)))); SecurityUser securityUser = new SecurityUser(new UserId(UUID.fromString(claims.get(USER_ID, String.class))));
securityUser.setUserPrincipal(principal); securityUser.setUserPrincipal(principal);
securityUser.setSessionId(claims.get(SESSION_ID, String.class));
return securityUser; return securityUser;
} }
public JwtToken createPreVerificationToken(SecurityUser user) {
String token = setUpToken(user, Collections.singletonList(Authority.PRE_VERIFICATION_TOKEN.name()), settings.getPreVerificationTokenExpirationTime())
.claim(TENANT_ID, user.getTenantId().toString())
.compact();
return new AccessJwtToken(token);
}
private JwtBuilder setUpToken(SecurityUser securityUser, List<String> scopes, long expirationTime) {
if (StringUtils.isBlank(securityUser.getEmail())) {
throw new IllegalArgumentException("Cannot create JWT Token without username/email");
}
UserPrincipal principal = securityUser.getUserPrincipal();
Claims claims = Jwts.claims().setSubject(principal.getValue());
claims.put(USER_ID, securityUser.getId().getId().toString());
claims.put(SCOPES, scopes);
if (securityUser.getSessionId() != null) {
claims.put(SESSION_ID, securityUser.getSessionId());
}
ZonedDateTime currentTime = ZonedDateTime.now();
return Jwts.builder()
.setClaims(claims)
.setIssuer(settings.getTokenIssuer())
.setIssuedAt(Date.from(currentTime.toInstant()))
.setExpiration(Date.from(currentTime.plusSeconds(expirationTime).toInstant()))
.signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey());
}
public Jws<Claims> parseTokenClaims(JwtToken token) { public Jws<Claims> parseTokenClaims(JwtToken token) {
try { try {
return Jwts.parser() return Jwts.parser()
@ -193,4 +210,11 @@ public class JwtTokenFactory {
throw new JwtExpiredTokenException(token, "JWT Token expired", expiredEx); throw new JwtExpiredTokenException(token, "JWT Token expired", expiredEx);
} }
} }
public JwtTokenPair createTokenPair(SecurityUser securityUser) {
JwtToken accessToken = createAccessJwtToken(securityUser);
JwtToken refreshToken = createRefreshToken(securityUser);
return new JwtTokenPair(accessToken.getToken(), refreshToken.getToken());
}
} }

View File

@ -127,6 +127,8 @@ security:
jwt: jwt:
tokenExpirationTime: "${JWT_TOKEN_EXPIRATION_TIME:9000}" # Number of seconds (2.5 hours) tokenExpirationTime: "${JWT_TOKEN_EXPIRATION_TIME:9000}" # Number of seconds (2.5 hours)
refreshTokenExpTime: "${JWT_REFRESH_TOKEN_EXPIRATION_TIME:604800}" # Number of seconds (1 week) refreshTokenExpTime: "${JWT_REFRESH_TOKEN_EXPIRATION_TIME:604800}" # Number of seconds (1 week)
# Number of seconds. Issued when 2FA is being used; valid only for checking 2FA verification code after which usual token pair is issued
preVerificationTokenExpirationTime: "${JWT_PRE_VERIFICATION_TOKEN_EXPIRATION_TIME:30}"
tokenIssuer: "${JWT_TOKEN_ISSUER:thingsboard.io}" tokenIssuer: "${JWT_TOKEN_ISSUER:thingsboard.io}"
tokenSigningKey: "${JWT_TOKEN_SIGNING_KEY:thingsboardDefaultSigningKey}" tokenSigningKey: "${JWT_TOKEN_SIGNING_KEY:thingsboardDefaultSigningKey}"
# Enable/disable access to Tenant Administrators JWT token by System Administrator or Customer Users JWT token by Tenant Administrator # Enable/disable access to Tenant Administrators JWT token by System Administrator or Customer Users JWT token by Tenant Administrator
@ -425,6 +427,9 @@ caffeine:
edges: edges:
timeToLiveInMinutes: "${CACHE_SPECS_EDGES_TTL:1440}" timeToLiveInMinutes: "${CACHE_SPECS_EDGES_TTL:1440}"
maxSize: "${CACHE_SPECS_EDGES_MAX_SIZE:10000}" maxSize: "${CACHE_SPECS_EDGES_MAX_SIZE:10000}"
twoFaVerificationCodes:
timeToLiveInMinutes: "1"
maxSize: "100000"
redis: redis:
# standalone or cluster # standalone or cluster

View File

@ -20,7 +20,8 @@ public enum Authority {
SYS_ADMIN(0), SYS_ADMIN(0),
TENANT_ADMIN(1), TENANT_ADMIN(1),
CUSTOMER_USER(2), CUSTOMER_USER(2),
REFRESH_TOKEN(10); REFRESH_TOKEN(10),
PRE_VERIFICATION_TOKEN(11);
private int code; private int code;