Can we override CheckTokenEndpoint and provide custom CheckTokenEndpoint in Spring Oauth2? - spring

My application has separate authorization server and resource server. Authorization server provides access token to resource server. Resource server then sends the request for protected resource with access token.
Resource server uses RemoteTokenServices to validate whether the access token is proper or not.
#Bean
public RemoteTokenServices remoteTokenServices(final #Value("${auth.server.url}") String checkTokenUrl,
final #Value("${auth.server.clientId}") String clientId,
final #Value("${auth.server.clientsecret}") String clientSecret)
{
final RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
remoteTokenServices.setCheckTokenEndpointUrl(checkTokenUrl+"?name=value");
remoteTokenServices.setClientId(clientId);
remoteTokenServices.setClientSecret(clientSecret);
remoteTokenServices.setAccessTokenConverter(accessTokenConverter());
return remoteTokenServices;
}
application.yml
auth:
server:
url: http://localhost:9191/api/oauth/check_token/
clientId: clientid
clientsecret: secret
I want to pass additional parameter like resource id so that I can verify if the token is authorized for that resource or not.
I want to get that parameter in org.springframework.security.oauth2.provider.endpoint.CheckTokenEndpoint,
and want override below method to add some logic. Is it possible?
#RequestMapping(value = "/oauth/check_token")
#ResponseBody
public Map<String, ?> checkToken(#RequestParam("token") String value) {
OAuth2AccessToken token = resourceServerTokenServices.readAccessToken(value);
if (token == null) {
throw new InvalidTokenException("Token was not recognised");
}
if (token.isExpired()) {
throw new InvalidTokenException("Token has expired");
}
OAuth2Authentication authentication = resourceServerTokenServices.loadAuthentication(token.getValue());
Map<String, ?> response = accessTokenConverter.convertAccessToken(token, authentication);
return response;
}
How to send some parameter to oauth/check_token and override checkToken() method?
Basically what I am doing is when access token is generated, I am saving some record about the resources that token is allowed for.
When I receive the request for the resource on resource server, I want to pass the resource id to auth server and want to verify the token is authorized for that resource or not?

I've just create new CustomCheckTokenEndpoint and copy whole code of CheckTokenEndpoint then override checkToken(...) method
#Configuration
#EnableAuthorizationServer
public class CustomAuthorizationServer extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.pathMapping("/oauth/check_token", "/my/oauth/check_token");
}
}
**CustomCheckTokenEndpoint.java**
public class CustomCheckTokenEndpoint {
// copy whole CheckTokenEndpoint
#RequestMapping(value = "/my/oauth/check_token")
#ResponseBody
public Map<String, ?> checkToken(String value) {
// your code will be here
}
}

Related

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.

Spring boot authorization returns 403 for any authorization request using #RolesAllowed, #Secured or #PreAuthorize

I've been working from this article (and a few other similar ones): https://medium.com/omarelgabrys-blog/microservices-with-spring-boot-authentication-with-jwt-part-3-fafc9d7187e8
The client is an Angular 8 app which acquires a Jwt from an independent microservice. Trying to add filter(s) to a different microservice to require specific authorization via jwt roles.
Consistently receiving 403 errors.
Security Config:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true,
securedEnabled = true,
jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurityConfig() {}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and().csrf().disable()
// make sure we use stateless session; session won't be used to store user's state.
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// Add a filter to validate the tokens with every request
.addFilterAfter(new JwtAuthorizationFilter2(), UsernamePasswordAuthenticationFilter.class)
// authorization requests config
.authorizeRequests()
// Any other request must be authenticated
.anyRequest().authenticated();
}
}
Filter:
public class JwtAuthorizationFilter2 extends OncePerRequestFilter {
private final String HEADER = "Authorization";
private final String PREFIX = "Bearer ";
private final String SECRET = "foo";
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String token = request.getHeader(SecurityConstants.HEADER_STRING);
if (token != null) {
// parse the token.
DecodedJWT decoded = JWT.require(Algorithm.HMAC512(SecurityConstants.SECRET.getBytes()))
.build()
.verify(token.replace(SecurityConstants.TOKEN_PREFIX, ""));
String user = decoded.getSubject();
List<SimpleGrantedAuthority> sgas = Arrays.stream(
decoded.getClaim("roles").asArray(String.class))
.map( s -> new SimpleGrantedAuthority(s))
.collect( Collectors.toList());
if (sgas != null) {
sgas.add(new SimpleGrantedAuthority("FOO_Admin"));
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
user,
null,
sgas);
SecurityContextHolder.getContext().setAuthentication(auth);
}
else {
SecurityContextHolder.clearContext();
}
chain.doFilter(request, response);
}
}
}
This code works fine without any authorization requirements defined, but if an authorization is defined in WebSecurityConfig, or by decorating a controller method, http 403 is received for all requests in scope.
Examples:
.authorizeRequests().antMatchers("/**").hasRole("FOO_Admin")
// or any of these
#PreAuthorize("hasRole('FOO_Admin')")
#RolesAllowed({"FOO_Admin"})
#Secured({"FOO_Admin"})
Device get(#PathVariable String id) {
// some code
}
When code is halted at SecurityContextHolder.getContext().setAuthentication(auth),
auth.authenticated = true
and
auth.authorities includes a SimpleGrantedAuthority for "FOO_Admin"
So I'm wondering whether:
The FilterChain needs an Authentication Filter (or does authentication occur in JwtAuthorizationFilter2?)?
There is not a spelling or formatting or capitalization difference to role name.
I'm stupefied. Any help would be greatly appreciated.
#PreAuthorize("hasRole('FOO_Admin')) expects the user has an authority ROLE_FOO_Admin, which will be prefixed by ROLE_. However, the user only has the authority FOO_Admin , hence it fails to access the method.
You have several options:
(1) Change the prefix by declaring a GrantedAuthorityDefaults bean:
#Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("FOO");
}
And use #PreAuthorize(hasRole('Admin')) to secure the method.
(2) Or more simpler is to use #PreAuthorize("hasAuthority('FOO_Admin')") , which will directly check if the user has the authority FOO_Admin , without adding any prefix to it.
P.S JwtAuthorizationFilter2 only verifies if an user is valid and get the related user information which prepare for the authorization user later. It is an authentication and I would rename it to JwtAuthenticationFilter2 to describe more exactly what it does actually.

Spring secure endpoint with only client credentials (Basic)

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

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