Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 22 additions & 10 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,31 +50,43 @@
</scm>

<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.5.RELEASE</version>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
<version>7.0.2</version>
</dependency>

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>7.0.2</version>
</dependency>

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>7.0.2</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.1.RELEASE</version>
<version>7.0.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
<version>2.18.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
</dependency>
</dependencies>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AccessToken;

/**
* @author Tyler Scott
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.fusionauth.security;

import org.springframework.security.oauth2.client.registration.ClientRegistration;

/**
* Configuration class for OpenID Connect that holds client registration details
* and the OpenID Connect UserInfo endpoint as described in section <code>5.3</code>
* of OpenID Connect Core 1.0.
*
* @author Tyler Scott
* @see <a href="https://openid.net/specs/openid-connect-core-1_0.html#UserInfo">https://openid.net/specs/openid-connect-core-1_0.html#UserInfo</a>
*/
public class OpenIDConnectClientRegistration {
private ClientRegistration clientRegistration;
private String userInfoUri;

public OpenIDConnectClientRegistration(ClientRegistration clientRegistration, String userInfoUri) {
this.clientRegistration = clientRegistration;
this.userInfoUri = userInfoUri;
}

public ClientRegistration getClientRegistration() {
return clientRegistration;
}

public void setClientRegistration(ClientRegistration clientRegistration) {
this.clientRegistration = clientRegistration;
}

public String getUserInfoUri() {
return userInfoUri;
}

public void setUserInfoUri(String userInfoUri) {
this.userInfoUri = userInfoUri;
}

}
52 changes: 32 additions & 20 deletions src/main/java/io/fusionauth/security/OpenIDConnectFilter.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.fusionauth.security;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

import com.fasterxml.jackson.databind.JsonNode;
Expand All @@ -17,10 +17,11 @@
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.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.web.client.RestTemplate;

Expand All @@ -29,47 +30,58 @@
*/
public class OpenIDConnectFilter extends AbstractAuthenticationProcessingFilter {
@Autowired
private OpenIDAuthorizationCodeResourceDetails openIDResourceDetails;
private OpenIDConnectClientRegistration openIDClientRegistration;

private OAuth2RestOperations restTemplate;
private OAuth2AuthorizedClientManager authorizedClientManager;

public OpenIDConnectFilter(String defaultFilterProcessesUrl) {
public OpenIDConnectFilter(String defaultFilterProcessesUrl, OAuth2AuthorizedClientManager authorizedClientManager) {
super(defaultFilterProcessesUrl);
this.authorizedClientManager = authorizedClientManager;
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 {
// Create OAuth2AuthorizeRequest for the registered client
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
.withClientRegistrationId(openIDClientRegistration.getClientRegistration().getRegistrationId())
.principal("anonymous") // For client credentials or similar flows
.build();

// Get the authorized client using the manager
OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest);

if (authorizedClient == null) {
throw new BadCredentialsException("Could not authorize OAuth2 client");
}

OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
FusionAuthUserDetails user = new FusionAuthUserDetails(getUserInfo(accessToken), accessToken);
return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
} catch (OAuth2AuthenticationException e) {
throw new BadCredentialsException("Could not obtain access token", e);
} catch (Exception e) {
throw new BadCredentialsException("Failed to validate the token", e);
}
}

public void setRestTemplate(OAuth2RestTemplate restTemplate2) {
restTemplate = restTemplate2;
public void setAuthorizedClientManager(OAuth2AuthorizedClientManager authorizedClientManager) {
this.authorizedClientManager = authorizedClientManager;
}

private JsonNode getUserInfo(OAuth2AccessToken accessToken) throws IOException {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + accessToken.getValue());
headers.set("Authorization", "Bearer " + accessToken.getTokenValue());

HttpEntity<String> httpEntity = new HttpEntity<>(headers);
ResponseEntity<String> response = new RestTemplate().exchange(openIDResourceDetails.getUserInfoUri(), HttpMethod.GET, httpEntity, String.class);
ResponseEntity<String> response = new RestTemplate().exchange(openIDClientRegistration.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() + "]");
"Status code [" + response.getStatusCode().value() + "] Message [" + response.getBody() + "]");
}

private static class NoopAuthenticationManager implements AuthenticationManager {
Expand Down