Skip to content

Commit

Permalink
fix JwtAuthenticationProvider and missing authorities
Browse files Browse the repository at this point in the history
  • Loading branch information
lukaskabc committed Sep 4, 2024
1 parent eb90c8d commit fc79e8e
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 12 deletions.
37 changes: 31 additions & 6 deletions doc/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,35 @@ termit:

TermIt can operate in two authentication modes:

1. Internal authentication means
2. [Keycloak](https://www.keycloak.org/) -based
1. Internal authentication
2. OAuth2 based (e.g. [Keycloak](https://www.keycloak.org/))

By default, OAuth2 is disabled and internal authentication is used
To enable it, set termit security provider to `oidc`
and provide issuer-uri and jwk-set-uri.

**`application.yml` example:**
```yml
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://keycloak.lan/realms/termit
jwk-set-uri: http://keycloak.lan/realms/termit/protocol/openid-connect/certs
termit:
security:
provider: "oidc"
```
**Environmental variables example:**
```
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUERURI=http://keycloak.lan/realms/termit
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWKSETURI=http://keycloak.lan/realms/termit/protocol/openid-connect/certs
TERMIT_SECURITY_PROVIDER=oidc
```

TermIt will automatically configure its security accordingly
(it is using Spring's [`ConditionalOnProperty`](https://www.baeldung.com/spring-conditionalonproperty)).

By default, Keycloak is disabled (see `keycloak.enabled` in `application.yml`). To enable it, set `keycloak.enabled` to `true` and
provide additional required Keycloak parameters - see the [Keycloak Spring Boot integration docs](https://www.keycloak.org/docs/latest/securing_apps/#_spring_boot_adapter).
TermIt will automatically configure its security (it is using Spring's [`ConditionalOnProperty`](https://www.baeldung.com/spring-conditionalonproperty))
accordingly.
**Note that termit-ui needs to be configured for mathcing authentication mode.**
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,15 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.build();
}

/**
* An attempt to replicate auth provider from HttpSecurity
* @see cz.cvut.kbss.termit.security.WebSocketJwtAuthorizationInterceptor
*/
@Bean
public JwtAuthenticationProvider jwtAuthenticationProvider(JwtDecoder jwtDecoder) {
return new JwtAuthenticationProvider(jwtDecoder);
final JwtAuthenticationProvider provider = new JwtAuthenticationProvider(jwtDecoder);
provider.setJwtAuthenticationConverter(grantedAuthoritiesExtractor());
return provider;
}

private CorsConfigurationSource corsConfigurationSource() {
Expand Down
17 changes: 14 additions & 3 deletions src/main/java/cz/cvut/kbss/termit/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtException;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
Expand Down Expand Up @@ -126,9 +126,20 @@ private JwtAuthenticationFilter authenticationFilter(AuthenticationManager authe
return authenticationFilter;
}

/**
* @see cz.cvut.kbss.termit.security.WebSocketJwtAuthorizationInterceptor
*/
@Bean
public JwtAuthenticationProvider jwtAuthenticationProvider(JwtDecoder jwtDecoder) {
return new JwtAuthenticationProvider(jwtDecoder);
final JwtGrantedAuthoritiesConverter authoritiesConverter = new JwtGrantedAuthoritiesConverter();
authoritiesConverter.setAuthorityPrefix(""); // this removes default "SCOPE_" prefix
// otherwise, all granted authorities would have this prefix
// (like "SCOPE_ROLE_RESTRICTED_USER", we want just ROLE_...)
final JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
converter.setJwtGrantedAuthoritiesConverter(authoritiesConverter);
final JwtAuthenticationProvider provider = new JwtAuthenticationProvider(jwtDecoder);
provider.setJwtAuthenticationConverter(converter);
return provider;
}

private CorsConfigurationSource corsConfigurationSource() {
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/cz/cvut/kbss/termit/security/TermitJwtDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@
import cz.cvut.kbss.termit.service.security.TermItUserDetailsService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtClaimNames;
import org.springframework.security.oauth2.jwt.JwtException;

import java.util.Objects;
import java.util.stream.Collectors;

/**
* @see #decode(String)
*/
public class TermitJwtDecoder implements org.springframework.security.oauth2.jwt.JwtDecoder {

private final JwtUtils jwtUtils;
Expand All @@ -21,6 +27,11 @@ public TermitJwtDecoder(JwtUtils jwtUtils, TermItUserDetailsService userDetailsS
this.userDetailsService = userDetailsService;
}

/**
* Decodes JWT token (without the {@code Bearer} prefix)
* and ensures its validity.
* @throws JwtException with cause, when token could not be decoded or verified
*/
@Override
public Jwt decode(String token) throws JwtException {
try {
Expand All @@ -36,6 +47,10 @@ public Jwt decode(String token) throws JwtException {

SecurityUtils.verifyAccountStatus(existingDetails.getUser());

claims.put("scope", existingDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority)
.collect(Collectors.toSet()));
claims.putIfAbsent(JwtClaimNames.SUB, existingDetails);

return new Jwt(token, claims.getIssuedAt().toInstant(), claims.getExpiration()
.toInstant(), expanded.getHeader(), claims);
} catch (cz.cvut.kbss.termit.exception.JwtException | NullPointerException e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package cz.cvut.kbss.termit.security;

import cz.cvut.kbss.termit.security.model.TermItUserDetails;
import cz.cvut.kbss.termit.service.security.SecurityUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.HttpHeaders;
import org.springframework.messaging.Message;
Expand All @@ -12,7 +14,6 @@
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;
Expand All @@ -23,7 +24,9 @@
/**
* Authenticates STOMP CONNECT messages
* <p>
* Retrieves token from the {@code Authorization} header and authenticates the session.
* Retrieves token from the {@code Authorization} header
* and uses {@link JwtAuthenticationProvider} to authenticate the token.
* @see <a href="https://stackoverflow.com/a/45405333/12690791">Consult this Stackoverflow answer</a>
*/
@Component
public class WebSocketJwtAuthorizationInterceptor implements ChannelInterceptor {
Expand Down

0 comments on commit fc79e8e

Please sign in to comment.