Improve oauth2 (#3014)
* feat: add custom header to oauth2 req * fix: current.getClass() raise NPE * fix: use access token instead of id token * fix: missing dependency * feat: add oauth2 login from swagger-ui * docs: update oauth2 configuration
This commit is contained in:
committed by
GitHub
parent
394048a583
commit
8d83218dc8
@@ -62,18 +62,93 @@ information on password encoders in Spring Security.
|
|||||||
|
|
||||||
### OpenID Connect
|
### OpenID Connect
|
||||||
|
|
||||||
hawkbit supports authentication providers which use the OpenID Connect standard, an authentication layer built on top of
|
HawkBit supports authentication providers which use the OpenID Connect standard, an authentication layer built on top of
|
||||||
the OAuth 2.0 protocol.
|
the OAuth 2.0 protocol. OIDC integration can be enabled on UI and in server:
|
||||||
An example configuration is given below.
|
|
||||||
|
- **Hawkbit UI** — redirects users to an OIDC provider for authentication
|
||||||
|
- **Hawkbit Management Server** — validates JWT bearer tokens on Management API requests
|
||||||
|
|
||||||
|
#### Hawkbit UI
|
||||||
|
|
||||||
|
Enable OIDC login for the UI and register the provider using standard Spring Boot OAuth2 client properties:
|
||||||
|
|
||||||
```properties
|
```properties
|
||||||
spring.security.oauth2.client.registration.oidc.client-id=clientID
|
# Enable hawkBit OIDC UI login
|
||||||
spring.security.oauth2.client.provider.oidc.issuer-uri=https://oidc-provider/issuer-uri
|
hawkbit.server.security.oauth2.client.enabled=true
|
||||||
spring.security.oauth2.client.provider.oidc.jwk-set-uri=https://oidc-provider/jwk-set-uri
|
|
||||||
|
# Register the provider (replace "myidp" with any name)
|
||||||
|
spring.security.oauth2.client.provider.myidp.issuer-uri=https://idp.example.com/
|
||||||
|
spring.security.oauth2.client.registration.myidp.client-id=my-client-id
|
||||||
|
spring.security.oauth2.client.registration.myidp.client-secret=my-client-secret
|
||||||
|
spring.security.oauth2.client.registration.myidp.scope=openid,email,profile
|
||||||
|
spring.security.oauth2.client.registration.myidp.provider=myidp
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: at the moment only DEFAULT tenant is supported. By default the resource_access/<client id>/roles claim is mapped
|
Some providers (e.g. Auth0) require additional parameters in the authorization request. Use `additional-query-string-params` to pass
|
||||||
to hawkBit permissions.
|
them:
|
||||||
|
|
||||||
|
```properties
|
||||||
|
# Add extra parameters to the OAuth2 authorization request
|
||||||
|
hawkbit.server.security.oauth2.client.additional-query-string-params.audience=https://my-api-audience
|
||||||
|
```
|
||||||
|
|
||||||
|
Spring Security automatically sets the `redirect_uri` for the authorization code callback. It constructs the value as:
|
||||||
|
|
||||||
|
```
|
||||||
|
{baseUrl}/login/oauth2/code/myidp
|
||||||
|
```
|
||||||
|
|
||||||
|
This URL must be registered as an allowed redirect URI in your identity provider (IdP).
|
||||||
|
|
||||||
|
#### Hawkbit Management Server
|
||||||
|
|
||||||
|
Enable JWT bearer token validation for the Management REST API:
|
||||||
|
|
||||||
|
```properties
|
||||||
|
# Enable hawkBit default JWT resource server
|
||||||
|
hawkbit.server.security.oauth2.resourceserver.enabled=true
|
||||||
|
|
||||||
|
# JWK issuer URI for token signature verification
|
||||||
|
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://idp.example.com/
|
||||||
|
```
|
||||||
|
|
||||||
|
hawkBit maps JWT claims to the username, tenant, and permissions of the authenticated user. The claim paths are
|
||||||
|
configurable and support dot-notation for nested claims (e.g. `resource_access.my-client.roles`):
|
||||||
|
|
||||||
|
```properties
|
||||||
|
# Claim path for the hawkBit username (default: preferred_username)
|
||||||
|
hawkbit.server.security.oauth2.resourceserver.jwt.claim.username=preferred_username
|
||||||
|
|
||||||
|
# Claim path for hawkBit roles/permissions (default: roles)
|
||||||
|
hawkbit.server.security.oauth2.resourceserver.jwt.claim.roles=roles
|
||||||
|
|
||||||
|
# Claim path for the hawkBit tenant (default: DEFAULT)
|
||||||
|
hawkbit.server.security.oauth2.resourceserver.jwt.claim.tenant=tenant
|
||||||
|
```
|
||||||
|
|
||||||
|
To allow HTTP Basic authentication alongside OAuth2:
|
||||||
|
|
||||||
|
```properties
|
||||||
|
hawkbit.server.security.allow-http-basic-on-o-auth-enabled=true
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Swagger UI
|
||||||
|
|
||||||
|
To enable OAuth2 authorization from Swagger UI, configure the authorization and token endpoints:
|
||||||
|
|
||||||
|
```properties
|
||||||
|
springdoc.oauth-flow.authorizationUrl=https://idp.example.com/oauth2/authorize
|
||||||
|
springdoc.oauth-flow.tokenUrl=https://idp.example.com/oauth2/token
|
||||||
|
```
|
||||||
|
|
||||||
|
Swagger UI automatically sets the `redirect_uri` when initiating the authorization flow. It constructs the value as:
|
||||||
|
|
||||||
|
```
|
||||||
|
{window.location.origin}{base-path}/swagger-ui/oauth2-redirect.html
|
||||||
|
```
|
||||||
|
|
||||||
|
This URL must be registered as an allowed redirect URI in your identity provider (IdP) configuration.
|
||||||
|
|
||||||
|
|
||||||
### Permissions
|
### Permissions
|
||||||
|
|
||||||
|
|||||||
@@ -10,11 +10,14 @@
|
|||||||
package org.eclipse.hawkbit.mgmt.rest.resource;
|
package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import io.swagger.v3.oas.models.info.Info;
|
import io.swagger.v3.oas.models.info.Info;
|
||||||
|
import io.swagger.v3.oas.models.security.OAuthFlow;
|
||||||
|
import io.swagger.v3.oas.models.security.OAuthFlows;
|
||||||
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||||
import io.swagger.v3.oas.models.servers.Server;
|
import io.swagger.v3.oas.models.servers.Server;
|
||||||
@@ -22,11 +25,17 @@ import io.swagger.v3.oas.models.servers.ServerVariable;
|
|||||||
import io.swagger.v3.oas.models.servers.ServerVariables;
|
import io.swagger.v3.oas.models.servers.ServerVariables;
|
||||||
import io.swagger.v3.oas.models.tags.Tag;
|
import io.swagger.v3.oas.models.tags.Tag;
|
||||||
import org.eclipse.hawkbit.rest.OpenApi;
|
import org.eclipse.hawkbit.rest.OpenApi;
|
||||||
|
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
||||||
import org.springdoc.core.models.GroupedOpenApi;
|
import org.springdoc.core.models.GroupedOpenApi;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.config.Customizer;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnProperty(value = OpenApi.HAWKBIT_SERVER_OPENAPI_ENABLED, havingValue = "true", matchIfMissing = true)
|
@ConditionalOnProperty(value = OpenApi.HAWKBIT_SERVER_OPENAPI_ENABLED, havingValue = "true", matchIfMissing = true)
|
||||||
@@ -37,10 +46,37 @@ public class MgmtOpenApiConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty(
|
@ConditionalOnProperty(
|
||||||
value = "hawkbit.server.openapi.mgmt.enabled",
|
value = "hawkbit.server.openapi.mgmt.enabled", havingValue = "true", matchIfMissing = true)
|
||||||
havingValue = "true",
|
public GroupedOpenApi mgmtApi(
|
||||||
matchIfMissing = true)
|
@Value("${hawkbit.server.openapi.mgmt.tenant-endpoint.enabled:false}") final boolean tenantEndpointEnabled,
|
||||||
public GroupedOpenApi mgmtApi(@Value("${hawkbit.server.openapi.mgmt.tenant-endpoint.enabled:false}") final boolean tenantEndpointEnabled) {
|
@Value("${springdoc.oauth-flow.authorizationUrl:}") final String authorizationUrl,
|
||||||
|
@Value("${springdoc.oauth-flow.tokenUrl:}") final String tokenUrl,
|
||||||
|
@Autowired(required = false) @Qualifier("hawkbitOAuth2ResourceServerCustomizer") final Customizer<OAuth2ResourceServerConfigurer<HttpSecurity>> oauth2ResourceServerCustomizer,
|
||||||
|
final HawkbitSecurityProperties hawkbitSecurityProperties
|
||||||
|
) {
|
||||||
|
boolean oauth2Enabled = oauth2ResourceServerCustomizer != null;
|
||||||
|
Map<String, SecurityScheme> securitySchemeMap = new HashMap<>();
|
||||||
|
final SecurityRequirement securityRequirement = new SecurityRequirement();
|
||||||
|
if (!oauth2Enabled || hawkbitSecurityProperties.isAllowHttpBasicOnOAuthEnabled()) {
|
||||||
|
securityRequirement.addList(BASIC_AUTH_SEC_SCHEME_NAME);
|
||||||
|
securitySchemeMap.put(BASIC_AUTH_SEC_SCHEME_NAME,
|
||||||
|
new SecurityScheme()
|
||||||
|
.description(BASIC_AUTH_SEC_SCHEME_NAME + " Authentication")
|
||||||
|
.type(SecurityScheme.Type.HTTP)
|
||||||
|
.scheme("basic"));
|
||||||
|
}
|
||||||
|
if (oauth2Enabled) {
|
||||||
|
securityRequirement.addList(BEARER_AUTH_SEC_SCHEME_NAME);
|
||||||
|
securitySchemeMap.put(BEARER_AUTH_SEC_SCHEME_NAME,
|
||||||
|
new SecurityScheme()
|
||||||
|
.description(BEARER_AUTH_SEC_SCHEME_NAME + " Authentication")
|
||||||
|
.type(SecurityScheme.Type.OAUTH2)
|
||||||
|
.flows(new OAuthFlows()
|
||||||
|
.authorizationCode(new OAuthFlow().authorizationUrl(authorizationUrl).tokenUrl(tokenUrl))
|
||||||
|
.clientCredentials(new OAuthFlow().tokenUrl(tokenUrl)))
|
||||||
|
.bearerFormat("JWT")
|
||||||
|
.scheme("bearer"));
|
||||||
|
}
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
return GroupedOpenApi
|
return GroupedOpenApi
|
||||||
.builder()
|
.builder()
|
||||||
@@ -62,23 +98,11 @@ public class MgmtOpenApiConfiguration {
|
|||||||
.variables(new ServerVariables().addServerVariable("tenant", tenantSeverVariable())),
|
.variables(new ServerVariables().addServerVariable("tenant", tenantSeverVariable())),
|
||||||
new Server().url("/"))
|
new Server().url("/"))
|
||||||
: List.of(new Server().url("/")))
|
: List.of(new Server().url("/")))
|
||||||
.addSecurityItem(new SecurityRequirement()
|
.addSecurityItem(securityRequirement)
|
||||||
.addList(BASIC_AUTH_SEC_SCHEME_NAME)
|
|
||||||
.addList(BEARER_AUTH_SEC_SCHEME_NAME))
|
|
||||||
.components(
|
.components(
|
||||||
openApi
|
openApi
|
||||||
.getComponents()
|
.getComponents()
|
||||||
.addSecuritySchemes(BASIC_AUTH_SEC_SCHEME_NAME,
|
.securitySchemes(securitySchemeMap))
|
||||||
new SecurityScheme()
|
|
||||||
.description(BASIC_AUTH_SEC_SCHEME_NAME + " Authentication")
|
|
||||||
.type(SecurityScheme.Type.HTTP)
|
|
||||||
.scheme("basic"))
|
|
||||||
.addSecuritySchemes(BEARER_AUTH_SEC_SCHEME_NAME,
|
|
||||||
new SecurityScheme()
|
|
||||||
.description(BEARER_AUTH_SEC_SCHEME_NAME + " Authentication")
|
|
||||||
.type(SecurityScheme.Type.HTTP)
|
|
||||||
.bearerFormat("JWT")
|
|
||||||
.scheme("bearer")))
|
|
||||||
.tags(sort(openApi.getTags())))
|
.tags(sort(openApi.getTags())))
|
||||||
.build();
|
.build();
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|||||||
@@ -68,12 +68,9 @@
|
|||||||
<artifactId>spring-security-aspects</artifactId>
|
<artifactId>spring-security-aspects</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.security</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-security-oauth2-resource-server</artifactId>
|
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
|
||||||
</dependency>
|
<scope>compile</scope>
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.security</groupId>
|
|
||||||
<artifactId>spring-security-oauth2-jose</artifactId>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- Spring - END -->
|
<!-- Spring - END -->
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
@@ -165,8 +166,10 @@ public class MgmtSecurityConfiguration {
|
|||||||
final String tenantClaim = claim.getTenant();
|
final String tenantClaim = claim.getTenant();
|
||||||
final String rolesClaim = claim.getRoles();
|
final String rolesClaim = claim.getRoles();
|
||||||
oauth2ResourceServerConfigurer.jwt(jwtConfigurer -> jwtConfigurer.jwtAuthenticationConverter(jwt -> {
|
oauth2ResourceServerConfigurer.jwt(jwtConfigurer -> jwtConfigurer.jwtAuthenticationConverter(jwt -> {
|
||||||
final String username = followPathInJwtClaims(jwt, usernameClaim, String.class);
|
final String username = Objects.requireNonNull(followPathInJwtClaims(jwt, usernameClaim, String.class));
|
||||||
final String tenant = tenantClaim == null ? "DEFAULT" : followPathInJwtClaims(jwt, tenantClaim, String.class);
|
final String tenant = tenantClaim == null
|
||||||
|
? "DEFAULT"
|
||||||
|
: Objects.requireNonNull(followPathInJwtClaims(jwt, tenantClaim, String.class));
|
||||||
final Collection<GrantedAuthority> authorities = Optional
|
final Collection<GrantedAuthority> authorities = Optional
|
||||||
.ofNullable(followPathInJwtClaims(jwt, rolesClaim, Collection.class))
|
.ofNullable(followPathInJwtClaims(jwt, rolesClaim, Collection.class))
|
||||||
.map(resourceRoles -> ((Collection<String>) resourceRoles).stream()
|
.map(resourceRoles -> ((Collection<String>) resourceRoles).stream()
|
||||||
@@ -189,8 +192,10 @@ public class MgmtSecurityConfiguration {
|
|||||||
for (final String chunk : chunks) {
|
for (final String chunk : chunks) {
|
||||||
if (current instanceof Map<?, ?> map) {
|
if (current instanceof Map<?, ?> map) {
|
||||||
current = map.get(chunk);
|
current = map.get(chunk);
|
||||||
} else if (current == null) {
|
if (current == null) {
|
||||||
|
log.warn("Path {} not found in claim", path);
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.warn("Unexpected claim type for path {} (chunk {})! Expected a Map but got {}", path, chunk, current.getClass());
|
log.warn("Unexpected claim type for path {} (chunk {})! Expected a Map but got {}", path, chunk, current.getClass());
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import org.eclipse.hawkbit.sdk.HawkbitClient;
|
|||||||
import org.eclipse.hawkbit.sdk.HawkbitServer;
|
import org.eclipse.hawkbit.sdk.HawkbitServer;
|
||||||
import org.eclipse.hawkbit.sdk.Tenant;
|
import org.eclipse.hawkbit.sdk.Tenant;
|
||||||
import org.eclipse.hawkbit.ui.view.util.Utils;
|
import org.eclipse.hawkbit.ui.view.util.Utils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.cache.annotation.EnableCaching;
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
@@ -41,7 +42,10 @@ import org.springframework.security.authentication.BadCredentialsException;
|
|||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
|
||||||
|
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||||
|
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
||||||
|
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Theme("hawkbit")
|
@Theme("hawkbit")
|
||||||
@@ -56,15 +60,24 @@ public class HawkbitUiApp implements AppShellConfigurator {
|
|||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private static final String AUTHORIZATION_HEADER = "Authorization";
|
private static final String AUTHORIZATION_HEADER = "Authorization";
|
||||||
private static final RequestInterceptor AUTHORIZATION = requestTemplate -> {
|
|
||||||
|
private static RequestInterceptor authorizationInterceptor(final OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) {
|
||||||
|
return requestTemplate -> {
|
||||||
final Authentication authentication = Objects.requireNonNull(
|
final Authentication authentication = Objects.requireNonNull(
|
||||||
SecurityContextHolder.getContext().getAuthentication(), "No authentication available in security context!");
|
SecurityContextHolder.getContext().getAuthentication(), "No authentication available in security context!");
|
||||||
final Object principal = Objects.requireNonNull(authentication.getPrincipal(), "User is null!");
|
if (authentication instanceof OAuth2AuthenticationToken oauth2Token) {
|
||||||
if (principal instanceof OidcUser oidcUser) {
|
// line from /org/springframework/security/oauth2/client/web/client/OAuth2ClientHttpRequestInterceptor.java#authorizeClient
|
||||||
requestTemplate.header(
|
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(oauth2Token
|
||||||
AUTHORIZATION_HEADER,
|
.getAuthorizedClientRegistrationId()).principal(authentication).build();
|
||||||
"Bearer " + oidcUser.getIdToken().getTokenValue());
|
OAuth2AuthorizedClient authorizedClient = oAuth2AuthorizedClientManager.authorize(authorizeRequest);
|
||||||
|
if (authorizedClient != null) {
|
||||||
|
requestTemplate.header(AUTHORIZATION_HEADER, "Bearer " + authorizedClient.getAccessToken().getTokenValue());
|
||||||
} else {
|
} else {
|
||||||
|
log.warn("No authorized client found for principal {} — request will be sent without Authorization header", oauth2Token
|
||||||
|
.getName());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final Object principal = Objects.requireNonNull(authentication.getPrincipal(), "User is null!");
|
||||||
final String user = String.valueOf(principal);
|
final String user = String.valueOf(principal);
|
||||||
final Object pass = Objects.requireNonNull(authentication.getCredentials(), "Password is not available!");
|
final Object pass = Objects.requireNonNull(authentication.getCredentials(), "Password is not available!");
|
||||||
requestTemplate.header(
|
requestTemplate.header(
|
||||||
@@ -72,6 +85,7 @@ public class HawkbitUiApp implements AppShellConfigurator {
|
|||||||
"Basic " + Base64.getEncoder().encodeToString((user + ":" + pass).getBytes(ISO_8859_1)));
|
"Basic " + Base64.getEncoder().encodeToString((user + ":" + pass).getBytes(ISO_8859_1)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static final ErrorDecoder DEFAULT_ERROR_DECODER = new ErrorDecoder.Default();
|
private static final ErrorDecoder DEFAULT_ERROR_DECODER = new ErrorDecoder.Default();
|
||||||
private static final ErrorDecoder ERROR_DECODER = (methodKey, response) -> {
|
private static final ErrorDecoder ERROR_DECODER = (methodKey, response) -> {
|
||||||
@@ -85,12 +99,14 @@ public class HawkbitUiApp implements AppShellConfigurator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
HawkbitClient hawkbitClient(final HawkbitServer hawkBitServer) {
|
HawkbitClient hawkbitClient(final HawkbitServer hawkBitServer,
|
||||||
|
@Autowired(required = false) final OAuth2AuthorizedClientManager authorizedClientManager) {
|
||||||
|
final RequestInterceptor authorization = authorizationInterceptor(authorizedClientManager);
|
||||||
return new HawkbitClient(
|
return new HawkbitClient(
|
||||||
hawkBitServer, null, null, null,
|
hawkBitServer, null, null, null,
|
||||||
ERROR_DECODER,
|
ERROR_DECODER,
|
||||||
(tenant, controller) -> controller == null
|
(tenant, controller) -> controller == null
|
||||||
? AUTHORIZATION
|
? authorization
|
||||||
: HawkbitClient.DEFAULT_REQUEST_INTERCEPTOR_FN.apply(tenant, controller));
|
: HawkbitClient.DEFAULT_REQUEST_INTERCEPTOR_FN.apply(tenant, controller));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.ui.security;
|
package org.eclipse.hawkbit.ui.security;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@@ -16,13 +18,32 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
import org.springframework.security.config.Customizer;
|
import org.springframework.security.config.Customizer;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||||
|
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver;
|
||||||
|
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;
|
||||||
|
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class Oauth2ClientConfig {
|
|
||||||
@Bean(name = "hawkbitOAuth2ClientCustomizer")
|
|
||||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.oauth2.client", name = "enabled")
|
@ConditionalOnProperty(prefix = "hawkbit.server.security.oauth2.client", name = "enabled")
|
||||||
|
public class Oauth2ClientConfig {
|
||||||
|
|
||||||
|
@Bean(name = "hawkbitOAuth2ClientCustomizer")
|
||||||
@ConditionalOnMissingBean(name = "hawkbitOAuth2ClientCustomizer")
|
@ConditionalOnMissingBean(name = "hawkbitOAuth2ClientCustomizer")
|
||||||
Customizer<OAuth2LoginConfigurer<HttpSecurity>> defaultOAuth2ClientCustomizer() {
|
Customizer<OAuth2LoginConfigurer<HttpSecurity>> defaultOAuth2ClientCustomizer() {
|
||||||
return Customizer.withDefaults();
|
return Customizer.withDefaults();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public OAuth2AuthorizationRequestResolver authorizationRequestResolver(
|
||||||
|
ClientRegistrationRepository repo,
|
||||||
|
OidcClientProperties properties) {
|
||||||
|
|
||||||
|
final Map<String, String> additionalQueryStringParams = properties.getOauth2().getClient().getAdditionalQueryStringParams();
|
||||||
|
final DefaultOAuth2AuthorizationRequestResolver resolver = new DefaultOAuth2AuthorizationRequestResolver(repo,
|
||||||
|
OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI);
|
||||||
|
resolver.setAuthorizationRequestCustomizer(
|
||||||
|
customizer -> customizer.additionalParameters(params -> params.putAll(additionalQueryStringParams)));
|
||||||
|
return resolver;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.ui.security;
|
package org.eclipse.hawkbit.ui.security;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
@@ -29,6 +32,7 @@ public class OidcClientProperties {
|
|||||||
public static class Client {
|
public static class Client {
|
||||||
|
|
||||||
private boolean enabled = false;
|
private boolean enabled = false;
|
||||||
|
private Map<String, String> additionalQueryStringParams = new HashMap<>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user