-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTokenService.java
More file actions
105 lines (88 loc) · 3.77 KB
/
TokenService.java
File metadata and controls
105 lines (88 loc) · 3.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package org.openpodcastapi.opa.security;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import org.openpodcastapi.opa.user.UserEntity;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Date;
import java.util.UUID;
@Service
@RequiredArgsConstructor
public class TokenService {
private final RefreshTokenRepository repository;
private final BCryptPasswordEncoder passwordEncoder;
// The secret string used to generate secret keys
@Value("${jwt.secret}")
private String secret;
// The TTL for each JWT, in minutes
@Value("${jwt.expiration-minutes:15}")
private long accessTokenMinutes;
// The TTL for each refresh token, in days
@Value("${jwt.refresh-days:7}")
private long refreshTokenDays;
@Value("${jwt.ttl}")
private String jwtExpiration;
// The calculated secret key
private SecretKey key() {
return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
}
/// Returns the expiration time for JWTs
public long getExpirationTime() {
return Long.parseLong(jwtExpiration);
}
/// Generates an access token for a given user
///
/// @param userEntity the [UserEntity] to generate a token for
/// @return the generated token
public String generateAccessToken(UserEntity userEntity) {
Instant now = Instant.now();
return Jwts.builder()
.subject(userEntity.getUuid().toString())
.claim("username", userEntity.getUsername())
.issuedAt(Date.from(now))
.expiration(Date.from(now.plusSeconds(accessTokenMinutes * 60)))
.signWith(key())
.compact();
}
/// Generates a refresh token for a given user
///
/// @param userEntity the [UserEntity] to generate a refresh token for
/// @return the generated refresh token
public String generateRefreshToken(UserEntity userEntity) {
String raw = UUID.randomUUID().toString() + UUID.randomUUID();
String hash = passwordEncoder.encode(raw);
RefreshTokenEntity token = RefreshTokenEntity.builder()
.tokenHash(hash)
.user(userEntity)
.createdAt(Instant.now())
.expiresAt(Instant.now().plusSeconds(refreshTokenDays * 24 * 3600))
.build();
repository.save(token);
return raw;
}
/// Validates the refresh token for a user and updates its expiry time
///
/// @param rawToken the raw token to validate
/// @param userEntity the [UserEntity] to validate the token for
/// @return the validated [UserEntity]
public UserEntity validateRefreshToken(String rawToken, UserEntity userEntity) {
// Only fetch refresh tokens for the requesting user
for (RefreshTokenEntity token : repository.findAllByUser(userEntity)) {
// Check that the raw token and the token hash match and the token is not expired
if (passwordEncoder.matches(rawToken, token.getTokenHash()) &&
token.getExpiresAt().isAfter(Instant.now())) {
// Update the expiry date on the refresh token
token.setExpiresAt(Instant.now().plusSeconds(refreshTokenDays * 24 * 3600));
final RefreshTokenEntity updatedToken = repository.save(token);
// Return the user to confirm the token is valid
return updatedToken.getUser();
}
}
throw new IllegalArgumentException("Invalid refresh token");
}
}