1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.bremersee.security.authentication;
18
19 import java.util.Objects;
20 import org.springframework.core.convert.converter.Converter;
21 import org.springframework.http.HttpStatus;
22 import org.springframework.lang.Nullable;
23 import org.springframework.security.authentication.AbstractAuthenticationToken;
24 import org.springframework.security.authentication.AuthenticationManager;
25 import org.springframework.security.authentication.AuthenticationProvider;
26 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
27 import org.springframework.security.core.Authentication;
28 import org.springframework.security.core.AuthenticationException;
29 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
30 import org.springframework.security.oauth2.core.OAuth2Error;
31 import org.springframework.security.oauth2.jwt.Jwt;
32 import org.springframework.security.oauth2.jwt.JwtDecoder;
33 import org.springframework.security.oauth2.jwt.JwtException;
34 import org.springframework.security.oauth2.server.resource.BearerTokenError;
35 import org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;
36 import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
37
38
39
40
41
42
43 public class PasswordFlowAuthenticationManager
44 implements AuthenticationManager, AuthenticationProvider {
45
46 private static final OAuth2Error DEFAULT_INVALID_TOKEN =
47 invalidToken("An error occurred while attempting to decode the Jwt: Invalid token");
48
49 private final ClientCredentialsFlowProperties passwordFlowProperties;
50
51 private final AccessTokenRetriever<String> accessTokenRetriever;
52
53 private final JwtDecoder jwtDecoder;
54
55 private final Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter;
56
57
58
59
60
61
62
63
64
65 public PasswordFlowAuthenticationManager(
66 ClientCredentialsFlowProperties passwordFlowProperties,
67 JwtDecoder jwtDecoder,
68 @Nullable Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter,
69 AccessTokenRetriever<String> accessTokenRetriever) {
70
71 this.passwordFlowProperties = passwordFlowProperties;
72 this.jwtDecoder = jwtDecoder;
73 this.jwtAuthenticationConverter = Objects.requireNonNullElseGet(
74 jwtAuthenticationConverter,
75 JwtAuthenticationConverter::new);
76 this.accessTokenRetriever = accessTokenRetriever;
77 }
78
79 @Override
80 public Authentication authenticate(Authentication authentication) throws AuthenticationException {
81 final PasswordFlowProperties properties = PasswordFlowProperties.builder()
82 .from(passwordFlowProperties)
83 .username(authentication.getName())
84 .password((String) authentication.getCredentials())
85 .build();
86 try {
87 return this.jwtAuthenticationConverter.convert(
88 jwtDecoder.decode(
89 accessTokenRetriever.retrieveAccessToken(properties)));
90
91 } catch (JwtException failed) {
92 final OAuth2Error invalidToken = invalidToken(failed.getMessage());
93 throw new OAuth2AuthenticationException(invalidToken, invalidToken.getDescription(), failed);
94 }
95 }
96
97 @Override
98 public boolean supports(Class<?> authentication) {
99 return UsernamePasswordAuthenticationToken.class
100 .isAssignableFrom(authentication);
101 }
102
103 private static OAuth2Error invalidToken(String message) {
104 try {
105 return new BearerTokenError(
106 BearerTokenErrorCodes.INVALID_TOKEN,
107 HttpStatus.UNAUTHORIZED,
108 message,
109 "https://tools.ietf.org/html/rfc6750#section-3.1");
110 } catch (IllegalArgumentException malformed) {
111
112
113 return DEFAULT_INVALID_TOKEN;
114 }
115 }
116
117 }