Spring secure endpoint with only client credentials (Basic) - spring

I have oauth2 authorization server with one custom endpoint (log out specific user manually as admin)
I want this endpoint to be secured with rest client credentials (client id and secret as Basic encoded header value), similar to /oauth/check_token.
This endpoint can be called only from my resource server with specific scope.
I need to check if the client is authenticated.
I would like to be able to add #PreAuthorize("#oauth2.hasScope('TEST_SCOPE')")on the controller`s method.
I could not find any docs or way to use the Spring`s mechanism for client authentication check.
EDIT 1
I use java config not an xml one

So I ended up with the following solution
Authentication Manager
public class ClientAuthenticationManager implements AuthenticationManager {
private ClientDetailsService clientDetailsService;
private PasswordEncoder passwordEncoder;
public HGClientAuthenticationManager(ClientDetailsService clientDetailsService, PasswordEncoder passwordEncoder) {
Assert.notNull(clientDetailsService, "Given clientDetailsService must not be null!");
Assert.notNull(passwordEncoder, "Given passwordEncoder must not be null!");
this.clientDetailsService = clientDetailsService;
this.passwordEncoder = passwordEncoder;
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
ClientDetails clientDetails = null;
try {
clientDetails = this.clientDetailsService.loadClientByClientId(authentication.getPrincipal().toString());
} catch (ClientRegistrationException e) {
throw new BadCredentialsException("Invalid client id or password");
}
if (!passwordEncoder.matches(authentication.getCredentials().toString(), clientDetails.getClientSecret())) {
throw new BadCredentialsException("Invalid client id or password");
}
return new OAuth2Authentication(
new OAuth2Request(null, clientDetails.getClientId(), clientDetails.getAuthorities(), true,
clientDetails.getScope(), clientDetails.getResourceIds(), null, null, null),
null);
}
}
Filter declaration
private BasicAuthenticationFilter basicAuthenticationFilter() {
ClientDetailsUserDetailsService clientDetailsUserDetailsService = new ClientDetailsUserDetailsService(
this.clientDetailsService);
clientDetailsUserDetailsService.setPasswordEncoder(this.passwordEncoder);
return new BasicAuthenticationFilter(
new ClientAuthenticationManager(this.clientDetailsService, this.passwordEncoder));
}
Filter registration
httpSecurity.addFilterBefore(this.basicAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
WARNING!!!
This will prevent any other types of authentication (oauth2, etc.).
ONLY Basic authentication is accepted and ONLY for registered clients.

#PreAuthorize("#oauth2.hasScope('TEST_SCOPE')") On the controller method should be sufficiƫnt. If the client is not authenticated, no scope is available and the scope check will fail.
If you want, you can use the Spring Security expression #PreAuthorize("isAuthenticated()") to check if a client is authenticated: https://docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/reference/htmlsingle/#el-common-built-in
You could also configure the HttpSecurity instead of working with #PreAuthorize

Related

Spring oauth2login oidc grant access based on user info

I'm trying to set up Authentication based on this tutorial: https://www.baeldung.com/spring-security-openid-connect part 7 specifically.
I have filled properties and configured filter chain like this:
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests -> authorizeRequests
.anyRequest().authenticated())
.oauth2Login(oauthLogin -> oauthLogin.permitAll());
return http.build();
}
which works, but now all users from oidc can connect log in. I want to restrict access based on userinfo. E.g. add some logic like:
if(principal.getName() == "admin") {
//allow authentication
}
are there any way to do it?
I tried to create customer provider like suggested here: Add Custom AuthenticationProvider to Spring Boot + oauth +oidc
but it fails with exception and says that principal is null.
You can retrieve user info when authentication is successful and do further checks based user info.
Here is sample code that clears security context and redirects the request:
#Component
public class OAuth2AuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
if(authentication instanceof OAuth2AuthenticationToken) {
OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication;
// OidcUser or OAuth2User
// OidcUser user = (OidcUser) token.getPrincipal();
OAuth2User user = token.getPrincipal();
if(!user.getName().equals("admin")) {
SecurityContextHolder.getContext().setAuthentication(null);
SecurityContextHolder.clearContext();
redirectStrategy.sendRedirect(request, response, "login or error page url");
}
}
}
}
Are you sure that what you want to secure does not include #RestController or #Controller with #ResponseBody? If so, the client configuration you are referring to is not adapted: you need to setup resource-server configuration for this endpoints.
I wrote a tutorial to write apps with two filter-chains: one for resource-server and an other one for client endpoints.
The complete set of tutorials the one linked above belongs to explains how to achieve advanced access-control on resource-server. Thanks to the userAuthoritiesMapper configured in resource-server_with_ui, you can write the same security expressions based on roles on client controller methods as I do on resource-server ones.

Spring Boot OAuth2 password login via basic auth

I have a Spring Boot application with multiple http security configurations. Each of them is using external Keycloak.
API URLs are using Bearer token authentication
swagger URLs are using authentication code flow (user interaction needed)
URLs that authenticates via Basic Auth
First 2 works fine but I can't get basic auth configuration running. For that I would like to use OAuth2 grant type password.
My application.properties oauth2 configuration:
spring.security.oauth2.client.registration.keycloak2.client-id=${KEYCLOAK_RESOURCE}
spring.security.oauth2.client.registration.keycloak2.client-secret=${KEYCLOAK_RESOURCE_CLIENT_SECRET}
spring.security.oauth2.client.registration.keycloak2.authorization-grant-type=password
spring.security.oauth2.client.registration.keycloak2.scope=openid
spring.security.oauth2.client.provider.keycloak2.issuer-uri=${keycloak.auth-server-url}/realms/${keycloak.realm}
My configuration for Basic auth endpoints looks like this:
#Configuration
#Order(2)
public static class ProcessConfigurationAdapter extends WebSecurityConfigurerAdapter {
public static class OAuth2PasswordAuthenticationProvider implements AuthenticationProvider {
private final OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> accessTokenResponseClient;
private final OAuth2UserService<OAuth2UserRequest, OAuth2User> userService;
private final ClientRegistrationRepository clientRegistrationRepository;
private GrantedAuthoritiesMapper authoritiesMapper = ((authorities) -> authorities);
public OAuth2PasswordAuthenticationProvider(
OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> accessTokenResponseClient,
OAuth2UserService<OAuth2UserRequest, OAuth2User> userService,
ClientRegistrationRepository clientRegistrationRepository) {
super();
this.accessTokenResponseClient = accessTokenResponseClient;
this.userService = userService;
this.clientRegistrationRepository = clientRegistrationRepository;
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (!(authentication instanceof UsernamePasswordAuthenticationToken)) {
return null;
}
final UsernamePasswordAuthenticationToken usernamePassword = (UsernamePasswordAuthenticationToken) authentication;
final String username = (String) usernamePassword.getPrincipal();
final String password = (String) usernamePassword.getCredentials();
final String registrationId = "keycloak2";
final ClientRegistration keycloak2 = clientRegistrationRepository.findByRegistrationId(registrationId);
final OAuth2PasswordGrantRequest request = new OAuth2PasswordGrantRequest(keycloak2, username, password);
final OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponseClient.getTokenResponse(request);
final OAuth2User oauth2User = this.userService.loadUser(new OAuth2UserRequest(
keycloak2, accessTokenResponse.getAccessToken(), accessTokenResponse.getAdditionalParameters()));
final Collection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper
.mapAuthorities(oauth2User.getAuthorities());
final OAuth2AuthenticationToken authenticationResult = new OAuth2AuthenticationToken(oauth2User, mappedAuthorities, registrationId);
return authenticationResult;
}
#Override
public boolean supports(Class authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/v1/process/**")
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2Client()
.and()
.httpBasic();
http.csrf().disable();
}
#Bean
public OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> accessTokenResponseClient() {
return new DefaultPasswordTokenResponseClient();
}
#Bean
public OAuth2PasswordAuthenticationProvider oAuth2PasswordAuthenticationProvider(
OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> accessTokenResponseClient,
OAuth2UserService<OAuth2UserRequest, OAuth2User> userService,
ClientRegistrationRepository clientRegistrationRepository) {
// Here I'm missing userService
return new OAuth2PasswordAuthenticationProvider(accessTokenResponseClient, userService, clientRegistrationRepository);
}
}
I've got Parameter 1 of method oAuth2PasswordAuthenticationProvider in com.example.config.SecurityConfig$ProcessConfigurationAdapter required a bean of type 'org.springframework.security.oauth2.client.userinfo.OAuth2UserService' that could not be found.
I thought it would autowire based on configuration in application.properties but no. How can I obtain it?
Password grant flow is deprecated. Don't try to use it.
Authorization-code flow is a protocol between client and authorization-server to authenticate users and acquire access-token for client to act on behalf of those users. It is to be used client side (Angular, React, Vue, Flutter, etc. or Spring modules with Thymeleaf or other sever-side rendering) and has nothing to do with REST API.
To authenticated trusted programs (server-side applications that you can trust to keep a secret actually secret), you should use client-credentials flow to acquire access-tokens (for the client itself, not on behalf of the user). If you write such Spring services, configure it as OAuth2 client with client credentials.
In both cases from the resource-server point of view (the Spring REST API documented with Swagger), this doesn't make a difference: requests come with an Authorization header containing a Bearer access-token, and this is what you should build security-context from. Sample there.

How can I require consent for each unique anonymous user with Spring Security OAuth2?

My app has a singular endpoint. It triggers an OAuth2 authorization grant flow. It is meant to be called only by anonymous users. Each anonymous user represents a different person with different authorizations in the resource server. Consent (i.e., distinct authorization grant) is required from each anonymous user.
What is configuration in Spring Boot OAuth2 to require a consent for each anonymous user?
I'm using Spring Boot oath2-client 2.6.4 and Spring Security 5.6.2.
Currently, I have oauth2client configuration. It does not satisfy requirement. In this configuration, consent is requested only once and applied to all following anonymous callers. All callers share the same grant and access token.
I sense oauth2login may be the appropriate configuration, but I have needful customizations which I have to overcome before I try oauth2login. I have to disable the generated login page which prompts the user to select a provider, and I have to add custom fields to the authorization request. I have not had any success with these customizations in outh2login. So, this approach feels right, but is seemingly unavailable.
For information about this endpoint's caller, see: HL7 FHIR SMART-APP-LAUNCH
There are a number of challenges to this, related to:
My app has a singular endpoint. [...] It is meant to be called only by anonymous users.
This requirement makes it difficult for Spring Security to be of much help. This is because anonymous users typically don't have sessions, and the authorization_code grant is a flow which requires state and therefore a session. As a side note, I am not sure I fully understand how or why the specification you linked to (which is built on OAuth 2.0, as far as I can see) makes sense in the context of a client that allows an anonymous user.
Having said that, this seems possible using only the .oauth2Client() support in Spring Security if you create a custom filter for managing anonymous users. Note: The following assumes that the authorization server does not ignore the launch parameter even if a session exists in the browser.
The following configuration defines and configures this filter, as well as customizing the oauth2Client() to pass the launch parameter to the authorization server. It essentially creates a temporary authentication for the launch parameter to be saved as the principalName in the session for the duration of the flow.
#EnableWebSecurity
public class SecurityConfig {
private static final String PARAMETER_NAME = "launch";
private static final String ROLE_NAME = "LAUNCH_USER";
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, ClientRegistrationRepository clientRegistrationRepository) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().hasRole(ROLE_NAME)
)
.addFilterAfter(authenticationFilter(), AnonymousAuthenticationFilter.class)
.oauth2Client((oauth2) -> oauth2
.authorizationCodeGrant((authorizationCode) -> authorizationCode
.authorizationRequestResolver(authorizationRequestResolver(clientRegistrationRepository))
)
);
return http.build();
}
private OAuth2AuthorizationRequestResolver authorizationRequestResolver(ClientRegistrationRepository clientRegistrationRepository) {
DefaultOAuth2AuthorizationRequestResolver authorizationRequestResolver =
new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository, OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI);
// Configure a request customizer for the OAuth2AuthorizationRequestRedirectFilter
authorizationRequestResolver.setAuthorizationRequestCustomizer((authorizationRequest) -> {
Authentication currentAuthentication = SecurityContextHolder.getContext().getAuthentication();
// Customize request with principal name originally obtained from request parameter
if (currentAuthentication instanceof RequestParameterAuthenticationToken) {
Map<String, Object> additionalParameters = Map.of(PARAMETER_NAME, currentAuthentication.getName());
authorizationRequest.additionalParameters(additionalParameters);
}
});
return authorizationRequestResolver;
}
private RequestParameterAuthenticationFilter authenticationFilter() {
return new RequestParameterAuthenticationFilter(PARAMETER_NAME, AuthorityUtils.createAuthorityList("ROLE_" + ROLE_NAME));
}
/**
* Authentication filter that authenticates an anonymous request using a request parameter.
*/
public static final class RequestParameterAuthenticationFilter extends OncePerRequestFilter {
private final String parameterName;
private final List<GrantedAuthority> authorities;
public RequestParameterAuthenticationFilter(String parameterName, List<GrantedAuthority> authorities) {
this.parameterName = parameterName;
this.authorities = authorities;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
SecurityContext existingSecurityContext = SecurityContextHolder.getContext();
if (existingSecurityContext != null && !(existingSecurityContext.getAuthentication() instanceof AnonymousAuthenticationToken)) {
filterChain.doFilter(request, response);
return;
}
String principalName = request.getParameter(parameterName);
if (principalName != null) {
Authentication authenticationResult = new RequestParameterAuthenticationToken(principalName, authorities);
authenticationResult.setAuthenticated(true);
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(authenticationResult);
SecurityContextHolder.setContext(securityContext);
}
filterChain.doFilter(request, response);
}
}
/**
* Custom authentication token that can be persisted between requests, but is otherwise very similar to
* {#link AnonymousAuthenticationToken}.
*/
public static final class RequestParameterAuthenticationToken extends AbstractAuthenticationToken implements Serializable {
private static final long serialVersionUID = 1L;
private final String principalName;
public RequestParameterAuthenticationToken(String principalName, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principalName = principalName;
}
#Override
public Object getPrincipal() {
return this.principalName;
}
#Override
public Object getCredentials() {
return this.principalName;
}
}
}
You can use this in a controller endpoint, as in the following example:
#RestController
public class LaunchController {
#GetMapping("/app/launch")
public void launch(
#RegisteredOAuth2AuthorizedClient("fhir-client")
OAuth2AuthorizedClient authorizedClient) {
String launchParameter = authorizedClient.getPrincipalName();
String accessToken = authorizedClient.getAccessToken().getTokenValue();
// Use authorizedClient.getAccessToken() to make a request (WebClient)...
// Clear the SecurityContext after the request, to force the next request
// to start the flow over again
SecurityContextHolder.clearContext();
}
}
See related issue #11069 for additional context on this answer.

Connect Spring Authorization server to external IDP and trigger authentication

We created an authorization server with JDBC backend token store. A similar implementation is hosted on GitHub.
It is working perfectly fine in our environment using different grant types. Different web applications use this for SSO, and it issues tokens, which are then used to consume API as well.
We need a way to log a user in, and issue token if the user is returned as authenticated from external IDP, kind of simulating a user logging in manually from the login form.
We have to extend this server with external IDP authentication. So if a user is connected to their domain network, and has ADFS (as an example), expected flow is as follows:
User tries to access a client app
Redirected to authorization server
Instead of entering credentials user can click on a button to authenticate via ADFS (this can be automated too later on)
ADFS should return authentication ok, with user information
Trigger login of that user in the authorization server, so that an OAuth2 token is issued, and redirected back to the client app
We have tried multiple ways to achieve it, and have referred to multiple resources online, but no success yet. Please note that we do not have the need to connect to social media IDP, rather we have to consume response from enterprise-grade like ADFS, One-login etc.
Any initial pointers would be much appreciated.
To authenticate with GitHub and generate spring token which can be used downstream application we can change our codes like below.
In WebSecurityConfigurerAdapter add below code additional to configure(HttpSecurity http)
http.exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/")).and()
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class).addFilter(customBasicAuthFilter);
then in WebSecurityConfigurerAdapter again
#Bean
public FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
#Bean
#ConfigurationProperties("github")
public ClientResources github() {
return new ClientResources();
}
private Filter ssoFilter() {
CompositeFilter filter = new CompositeFilter();
List<Filter> filters = new ArrayList<>();
filters.add(ssoFilter(github(), "/login/github"));
filter.setFilters(filters);
return filter;
}
private Filter ssoFilter(ClientResources client, String path) {
OAuth2ClientAuthenticationProcessingFilter oAuth2ClientAuthenticationFilter = new OAuth2ClientAuthenticationProcessingFilter(
path);
OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext);
oAuth2ClientAuthenticationFilter.setRestTemplate(oAuth2RestTemplate);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(client.getResource().getUserInfoUri(),
client.getClient().getClientId());
tokenServices.setRestTemplate(oAuth2RestTemplate);
oAuth2ClientAuthenticationFilter.setTokenServices(tokenServices);
return oAuth2ClientAuthenticationFilter;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManager);
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(passwordEncoder);
}
add one class ClientResources
class ClientResources {
#NestedConfigurationProperty
private AuthorizationCodeResourceDetails client = new AuthorizationCodeResourceDetails();
#NestedConfigurationProperty
private ResourceServerProperties resource = new ResourceServerProperties();
public AuthorizationCodeResourceDetails getClient() {
return client;
}
public ResourceServerProperties getResource() {
return resource;
}
}
additional to all we need to add GitHub setting in our application.
github.client.clientId = <<Clientid>>
github.client.clientSecret = <<clientSecret>>
github.client.accessTokenUri = https://github.com/login/oauth/access_token
github.client.userAuthorizationUri = https://github.com/login/oauth/authorize
github.client.clientAuthenticationScheme = form
github.resource.userInfoUri = https://api.github.com/user
logging.level.org.springframework.security = DEBUG
Similar way you can do it for other which supports OAuth. I am also exploring for working with ADFS authentication. Query posted on Stackoverflow for the same.

How to bypass UsernamePasswordAuthentication in Spring Security

I'm implementing an API that accepts a JWT as request parameter and on authentication, returns a new JWT.
#RequestMapping(value = "/authenticate/token", method = RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public ResponseEntity authenticate(#RequestParam("login_token") final String token, HttpServletResponse response) {
LOG.debug("Request to login with token : {}", token);
try {
String jwt = authService.loginByToken(token);
response.addHeader(JWTConfigurer.AUTHORIZATION_HEADER, "Bearer " + jwt);
return ResponseEntity.ok(new IdentityToken(jwt));
} catch (AuthenticationException ae) {
LOG.trace("Authentication exception trace: {}", ae);
return new ResponseEntity<>(Collections.singletonMap("AuthenticationException",
ae.getLocalizedMessage()), HttpStatus.UNAUTHORIZED);
}
}
My loginByToken implementation looks as below
#Override public String loginByToken(String token) {
if (!tokenProvider.validateToken(token)) {
throw new BadCredentialsException("Token is invalid.");
}
SecureToken secureToken = tokenProvider.parseJwtToken(token);
User user = userRepository.findByEmail(secureToken.getEmail());
// TODO: Check Account Status is valid, User status is valid
Calendar c = Calendar.getInstance();
c.setTime(new Date());
c.add(Calendar.DATE, Constants.PASSWORD_EXPIRY_DAYS);
if (user.getPasswordExpiryDt() != null
&& user.getPasswordExpiryDt().after(c.getTime())) {
throw new BadCredentialsException("Password changed");
}
// TODO: Find how to create authentication object and return ID token.
// return tokenProvider.createToken(authentication, false);
return token;
}
At this point, I'm not sure how to create an authentication object that contains all user details that I could pass to createToken function that creates an identity token.
Here is my project without the changes mentioned in this post - https://github.com/santoshkt/ngx-pipes-test.
I have read about Anonymous Authentication, PreAuthenticated etc but not sure how to deal with this case. Will appreciate any pointers on how to do this.
If you want to use Spring Security, you should probably not use a Spring MVC endpoint to handle (pre-)authentication.
In your case you probably want to change your Spring security configuration so that it will have a filter that obtains your token from your request parameters and an authentication provider that retrieves the user/authentication object from your token:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/authenticate/token")
.authorizeRequests()
.anyRequest().authenticated()
.and()
// This is a filter bean you'll have to write
.addFilterBefore(filter(), RequestHeaderAuthenticationFilter.class)
// This is your token verifier/decoder
.authenticationProvider(authenticationProvider())
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
For the filter you could extend from AbstractPreAuthenticatedProcessingFilter and make it return the login_token parameter. In here you have to implement two methods being getPreAuthenticatedPrincipal() and getPreAuthenticatedCredentials().
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
// You could already decode your token here to return your username
return request.getParameter("login_token");
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return request.getParameter("login_token");
}
Your authentication provider should be of type PreAuthenticatedAuthenticationProvider and in here you can set an AuthenticationUserDetailsService:
#Bean
public AuthenticationProvider authenticationProvider() {
PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
// service is a bean of type AuthenticationUserDetailsService
// You could autowire this in your security configuration class
provider.setPreAuthenticatedUserDetailsService(service);
return provider;
}
Now you can create your own AuthenticationUserDetailsService to retrieve a UserDetails object based on your token:
#Service
public class TokenAuthenticationUserDetailsService implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
#Override
public UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken authentication) throws UsernameNotFoundException {
// In this case the authentication.getCredentials() will contain your token and you can return a UserDetails object
return new User(/** ... */);
}
}
Since you want to provide the HTML page for the JWT token request the best approach is that you create you own Spring Security Custom Entry Point
You may give a look here for an example
If it's another system to manage the authentication and you want just manage the authorization you can "trust" the other System and then manage your own authorizations; in this case you can use the PreAuthentication Scenario as described here; you can find a sample here
I hope it's useful

Resources