Open nikos opened 5 years ago
Hi @nikos thanks for the question.
FusionAuth does not currently support the client credentials grant, this will likely be coming this summer with a suite of IoT features.
We could look into adding the Password grant to this library, or if you'd like to submit a PR that would be great as well.
I'm not a Spring expert at all... so without digging back into the code your guess is as good as mine. If I have some cycles to take a look and see if I can stub it out I'll report back.
If you're looking to use the password or credentials grant, does this mean you'll be using it for something other than a normal web login workflow?
Hi,
I recently heard that spring started including an OpenID Connect feature and I haven't had the time to look into it. When I get the chance I will update our example code to use the latest spring features.
I upvoted this issue because i encountered the exact same problem. The filter given in this library is only useful for the Authorization Code grant, by analyzing the token in response of the redirectUri after a succesful login.
I made a custom TokenFilter that i register in the security chain, which check the validity of the token (with the public certificate), and then extract the informations needed in the JWT to create an authenticated user, with authorities extracted from the 'roles' key in the claims.
If i have time, i'll submit a PR of my work to help people, but for the moment the class is not generic at all and only fits to my specific needs...
@tyduptyler13
I recently heard that spring started including an OpenID Connect feature and I haven't had the time to look into it. When I get the chance I will update our example code to use the latest spring features.
Hi Typler, are you refering to the changes in Spring Security 5.1.x ? https://github.com/spring-projects/spring-security/wiki/OAuth-2.0-Features-Matrix Might be quite an impact worth a new branch since there seem many changes compared to Spring Security OAuth 2.x
@damienherve my current approach is to make use of Spring's "standard" class when it comes to the password grants flow: org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails
and add userInfoUri for accessing OpenID metadata.
OpenIDResourceDetails
package io.fusionauth.patch.oauth;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
public interface OpenIDResourceDetails extends OAuth2ProtectedResourceDetails {
String getUserInfoUri();
void setUserInfoUri(String userInfoUri);
}
OpenIDPasswordResourceDetails
package io.fusionauth.patch.oauth;
import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails;
public class OpenIDPasswordResourceDetails extends ResourceOwnerPasswordResourceDetails implements OpenIDResourceDetails {
private String userInfoUri;
public String getUserInfoUri() {
return userInfoUri;
}
public void setUserInfoUri(String userInfoUri) {
this.userInfoUri = userInfoUri;
}
}
OpenIDConnectFilter
package io.fusionauth.patch.oauth;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import io.fusionauth.security.FusionAuthUserDetails;
// Patches io.fusionauth.security.OpenIDConnectFilter
public class OpenIDConnectFilter extends AbstractAuthenticationProcessingFilter {
private final OpenIDResourceDetails openIDResourceDetails;
private final OAuth2RestOperations restTemplate;
public OpenIDConnectFilter(String defaultFilterProcessesUrl, OpenIDResourceDetails openIDResourceDetails, OAuth2RestOperations restTemplate) {
super(defaultFilterProcessesUrl);
this.openIDResourceDetails = openIDResourceDetails;
this.restTemplate = restTemplate;
setAuthenticationManager(new NoopAuthenticationManager());
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
OAuth2AccessToken accessToken;
try {
accessToken = restTemplate.getAccessToken();
} catch (final OAuth2Exception e) {
throw new BadCredentialsException("Could not obtain access token", e);
}
try {
FusionAuthUserDetails user = new FusionAuthUserDetails(getUserInfo(accessToken), accessToken);
return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
} catch (Exception e) {
throw new BadCredentialsException("Failed to validate the token", e);
}
}
private JsonNode getUserInfo(OAuth2AccessToken accessToken) throws IOException {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + accessToken.getValue());
HttpEntity<String> httpEntity = new HttpEntity<>(headers);
ResponseEntity<String> response = new RestTemplate().exchange(openIDResourceDetails.getUserInfoUri(), HttpMethod.GET, httpEntity, String.class);
if (response.getStatusCode() == HttpStatus.OK) {
return new ObjectMapper().readTree(response.getBody());
}
throw new BadCredentialsException("Failed to request user details from the UserInfo API. " +
"Status code [" + response.getStatusCodeValue() + "] Message [" + response.getBody() + "]");
}
private static class NoopAuthenticationManager implements AuthenticationManager {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
throw new UnsupportedOperationException("No authentication should be done with this AuthenticationManager");
}
}
}
I started working on a new example, based on how its going I think this repo will be deprecated in favor of just using spring security 5.
https://github.com/FusionAuth/fusionauth-spring-security-example/tree/SpringSecurity5
After further experimentation with Spring Security 5.1 it turns out that with a valid JWT at hand, securing your resources in your Spring Boot application is as straight forward as:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/resources/**", "/error");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.mvcMatchers("/products/**").hasAuthority("MYROLE")
.and()
.oauth2ResourceServer()
.jwt()
.jwkSetUri("http://localhost:9011/.well-known/jwks.json")
.jwtAuthenticationConverter(grantedAuthoritiesExtractor())
// this disables session creation on Spring Security
//.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
;
}
private Converter<Jwt, AbstractAuthenticationToken> grantedAuthoritiesExtractor() {
return new JwtAuthenticationConverter() {
@Override
protected Collection<GrantedAuthority> extractAuthorities(Jwt jwt) {
Collection<String> authorities = (Collection<String>) jwt.getClaims().get("roles");
return authorities.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
};
}
}
Does anyone have any news for us non-java devs on how this may be going? Is there a simple method for allowing our customers apps to authenticate with our APIs using client_credentials
?
Any update to support client_credentials grant type? thank you
FusionAuth does not currently support the client credentials grant.
See FusionAuth feature request and ensure to upvote it. https://github.com/FusionAuth/fusionauth-issues/issues/155
If you have a business requirement for this, hit the FusionAuth Contact form and we can provide you a quote with a timeline.
Thanks!
It seems that currently this library concentrates on supporting OAuth2 authorization code grants.
Are there any plans to support also client credential and password grants?
For the time being, would you recommend to extend the OpenIDConnectFilter to support also other type of OAuth2ProtectedResourceDetails implementations than currently limiting it to OpenIDAuthorizationCodeResourceDetails. Any hints very appreciated, thanks in advance!