Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Q: Support for client credential and password grants #1

Open
nikos opened this issue Jun 14, 2019 · 10 comments
Open

Q: Support for client credential and password grants #1

nikos opened this issue Jun 14, 2019 · 10 comments
Assignees
Labels
enhancement New feature or request

Comments

@nikos
Copy link

nikos commented Jun 14, 2019

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!

@robotdan
Copy link
Member

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?

@tyduptyler13
Copy link

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.

@tyduptyler13 tyduptyler13 added the enhancement New feature or request label Jun 17, 2019
@damienherve
Copy link

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...

@nikos
Copy link
Author

nikos commented Jun 24, 2019

@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

@nikos
Copy link
Author

nikos commented Jun 24, 2019

@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");
        }
    }
}

@tyduptyler13
Copy link

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

@nikos
Copy link
Author

nikos commented Jun 27, 2019

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());
            }
        };
    }
}

@designermonkey
Copy link

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?

@vlvinogr
Copy link

vlvinogr commented Dec 6, 2019

Any update to support client_credentials grant type? thank you

@robotdan
Copy link
Member

robotdan commented Dec 6, 2019

FusionAuth does not currently support the client credentials grant.

See FusionAuth feature request and ensure to upvote it.
FusionAuth/fusionauth-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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

6 participants