11package com .andriawan .andresource .service ;
22
3+ import com .andriawan .andresource .controller .AuthController .RefreshTokenRequest ;
4+ import com .andriawan .andresource .entity .RefreshToken ;
5+ import com .andriawan .andresource .entity .User ;
6+ import com .andriawan .andresource .repository .RefreshTokenRepository ;
7+ import jakarta .persistence .EntityNotFoundException ;
8+ import jakarta .transaction .Transactional ;
39import java .time .Instant ;
410import java .time .temporal .ChronoUnit ;
511import java .util .Map ;
6- import java . util . stream . Collectors ;
12+ import org . springframework . beans . factory . annotation . Autowired ;
713import org .springframework .beans .factory .annotation .Value ;
14+ import org .springframework .security .authentication .BadCredentialsException ;
15+ import org .springframework .security .authentication .InsufficientAuthenticationException ;
816import org .springframework .security .core .Authentication ;
9- import org .springframework .security .core .GrantedAuthority ;
17+ import org .springframework .security .core .AuthenticationException ;
18+ import org .springframework .security .oauth2 .jwt .BadJwtException ;
19+ import org .springframework .security .oauth2 .jwt .Jwt ;
1020import org .springframework .security .oauth2 .jwt .JwtClaimsSet ;
21+ import org .springframework .security .oauth2 .jwt .JwtDecoder ;
1122import org .springframework .security .oauth2 .jwt .JwtEncoder ;
1223import org .springframework .security .oauth2 .jwt .JwtEncoderParameters ;
1324import org .springframework .stereotype .Service ;
@@ -23,43 +34,107 @@ public class TokenService {
2334
2435 private final JwtEncoder jwtEncoder ;
2536
26- public record JwtClaimsSetParams (
27- long time , Instant now , String scope , Authentication authentication ) {}
37+ private final JwtDecoder jwtDecoder ;
2838
29- public TokenService (JwtEncoder jwtEncoder ) {
39+ @ Autowired JpaUserDetailsService jpaUserDetailsService ;
40+ @ Autowired RefreshTokenRepository refreshTokenRepository ;
41+
42+ public record JwtClaimsSetParams (long time , Instant now , String scope , String username ) {}
43+
44+ public TokenService (JwtEncoder jwtEncoder , JwtDecoder jwtDecoder ) {
3045 this .jwtEncoder = jwtEncoder ;
46+ this .jwtDecoder = jwtDecoder ;
3147 }
3248
3349 public Map <String , String > generateToken (Authentication authentication ) {
50+ return generateToken (authentication .getName ());
51+ }
52+
53+ public Map <String , String > generateToken (User user ) {
54+ return generateToken (user .getEmail ());
55+ }
56+
57+ public Map <String , String > generateToken (String username ) {
3458 Instant now = Instant .now ();
35- String scope =
36- authentication .getAuthorities ().stream ()
37- .map (GrantedAuthority ::getAuthority )
38- .collect (Collectors .joining (" " ));
59+ String scope = "" ;
3960 JwtClaimsSet claimsSetAccessToken =
40- buildJwtClaimsSet (new JwtClaimsSetParams (expiredTokenSeconds , now , scope , authentication ));
61+ buildJwtClaimsSet (new JwtClaimsSetParams (expiredTokenSeconds , now , scope , username ));
4162 JwtClaimsSet claimsSetRefreshToken =
42- buildJwtClaimsSet (
43- new JwtClaimsSetParams (expiredRefreshSeconds , now , scope , authentication ));
63+ buildJwtClaimsSet (new JwtClaimsSetParams (expiredRefreshSeconds , now , scope , username ));
4464 String accessToken = encodeToken (claimsSetAccessToken );
45- String refreshToken = encodeToken (claimsSetRefreshToken );
65+ String refreshToken = setupRefreshToken (claimsSetRefreshToken );
4666 return Map .of ("access_token" , accessToken , "refresh_token" , refreshToken );
4767 }
4868
69+ public String setupRefreshToken (JwtClaimsSet claimsSetRefreshToken ) {
70+ String token = encodeToken (claimsSetRefreshToken );
71+ refreshTokenRepository .save (
72+ RefreshToken .builder ()
73+ .token (token )
74+ .user (jpaUserDetailsService .getAuthenticatedUser ().getUser ())
75+ .expiresAt (claimsSetRefreshToken .getExpiresAt ())
76+ .build ());
77+ return token ;
78+ }
79+
80+ public void backlistToken (String token ) {
81+ RefreshToken refreshToken =
82+ refreshTokenRepository
83+ .findByToken (token )
84+ .orElseThrow (() -> new EntityNotFoundException ("token not found" ));
85+ refreshToken .setBlacklistedAt (Instant .now ());
86+ refreshTokenRepository .save (refreshToken );
87+ }
88+
89+ public boolean isBlacklistedRefreshToken (String token ) {
90+ boolean state =
91+ refreshTokenRepository
92+ .findByToken (token )
93+ .map (mapToken -> mapToken .getBlacklistedAt () != null )
94+ .orElse (true );
95+ return state ;
96+ }
97+
4998 private JwtClaimsSet buildJwtClaimsSet (JwtClaimsSetParams params ) {
99+
100+ Long userId = jpaUserDetailsService .getAuthenticatedUser ().getUser ().getId ();
101+
50102 JwtClaimsSet claimsSet =
51103 JwtClaimsSet .builder ()
52104 .issuer ("apps" )
53105 .issuedAt (params .now ())
54106 .expiresAt (params .now ().plus (params .time (), ChronoUnit .SECONDS ))
55- .subject (params .authentication (). getName ())
107+ .subject (params .username ())
56108 .claim ("scope" , params .scope ())
57- .claim ("user_id" , "1" )
109+ .claim ("user_id" , userId )
58110 .build ();
59111 return claimsSet ;
60112 }
61113
62114 public String encodeToken (JwtClaimsSet jwtClaimsSet ) {
63115 return this .jwtEncoder .encode (JwtEncoderParameters .from (jwtClaimsSet )).getTokenValue ();
64116 }
117+
118+ public void revokeToken (RefreshTokenRequest request ) {
119+ refreshTokenRepository .deleteByToken (request .token ());
120+ }
121+
122+ @ Transactional (dontRollbackOn = BadCredentialsException .class )
123+ public Map <String , String > doRefreshToken (RefreshTokenRequest request )
124+ throws AuthenticationException {
125+ try {
126+ Jwt jwt = jwtDecoder .decode (request .token ());
127+ if (isBlacklistedRefreshToken (request .token ())) {
128+ refreshTokenRepository .deleteByToken (request .token ());
129+ throw new BadCredentialsException ("refresh token is blacklisted" );
130+ }
131+ Map <String , String > response = generateToken (jwt .getSubject ());
132+ backlistToken (request .token ());
133+ return response ;
134+ } catch (InsufficientAuthenticationException | BadJwtException e ) {
135+ throw new BadCredentialsException (e .getMessage ());
136+ } catch (Exception e ) {
137+ throw e ;
138+ }
139+ }
65140}
0 commit comments