Can I use both introspection server and local check for authorize token? Spring Boot - Security - spring

I want to
introspect JWT token on remote server
and then check locally if scope/aud/iss/exp are correct
How can this be done most easily in Spring Boot?
As I understand first case is something similar to opauqeToken functionality (but I have normal JWT) and second case is more like using jwt
Spring Security only supports JWTs or Opaque Tokens, not both at the same time.
If I use opaqueToken, then validation on remote server is done without any effort (even if that's JWT)
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.mvcMatchers("/api/**").hasAuthority("SCOPE_" + scope)
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.opaqueToken(opaque -> opaque
.introspectionUri(this.introspectionUri)
.introspectionClientCredentials(this.clientId, this.clientSecret)
));
return http.build();
I have scope verified. Now I want to check iss, aud, exp. Is that doable with opaqueToken?
Or should I use jwt auth instead?
IMHO opaqueToken can be JWT, so now the question is how to verify and inspect it locally after remote introspection?
It's kind of hybrid of two different approaches, but hopefully you know the simple way how to do it.

Ok, I think I have my answer. I created my own introspector which is implementing OpaqueTokenIntrospector
public class JwtOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
private OpaqueTokenIntrospector delegate =
new NimbusOpaqueTokenIntrospector(
"introspect-url",
"client-id",
"client-secret"
);
#Override
public OAuth2AuthenticatedPrincipal introspect(String token) {
OAuth2AuthenticatedPrincipal introspected = this.delegate.introspect(token);
// SOME LOGIC
}
}
and I added it as a #Bean
#Bean
public OpaqueTokenIntrospector tokenIntrospector() {
return new JwtOpaqueTokenIntrospector();
}

Related

how microservice use jwt to communicate in springboot

I am using microservice in spring boot and i want to use jwt and oauth2 to access the server.But i just wonder that how microservice other than api gateway get the data in the jwt (id or name) .It seems that it is so tedious to set a decoder in every microservice.
I am thinking that is it possible to decode and add the data at the httprequest and route it the other microservice in apigateway.But it seems that i cant find a setheader method in webflux filter security.
Jwt filter:
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
String authorizationheader= exchange.getRequest().getHeaders().get("Authorization").toString();
String token;
String Username = null;
String iss=null;
//check have tokem
if(authorizationheader !=null&& authorizationheader.startsWith("Bearer ")){
token=authorizationheader.substring(7);
Username=jwtDecoder.decode(token).getSubject();
iss= String.valueOf(jwtDecoder.decode(token).getIssuer());
} //verify by check username and iss
if(Username!=null && iss!=null&& SecurityContextHolder.getContext().getAuthentication()==null){
if(iss.equals("http://localhost:8080")){
UserDetails userDetails=new User(Username,null,null);
UsernamePasswordAuthenticationToken AuthenticationToken=new UsernamePasswordAuthenticationToken(
userDetails,null,userDetails.getAuthorities());
//set username and id to the request
SecurityContextHolder.getContext().setAuthentication(AuthenticationToken);
}
}
return chain.filter(exchange);
}
Securityfilter bean:
#Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity httpSecurity) throws Exception {
return httpSecurity
/*.csrf(csrf -> csrf.ignoringRequestMatchers("/Job/getRegionjobs/**",
"/Job/getalljobs","/login/oauth2/code/google"))*/
.csrf(csrf -> csrf.disable())
.authorizeExchange(auth->auth.anyExchange().authenticated())
.addFilterBefore(jwtFilter, SecurityWebFiltersOrder.AUTHENTICATION)
.oauth2ResourceServer(ServerHttpSecurity.OAuth2ResourceServerSpec::jwt)
//.sessionManagement(session-> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.httpBasic(withDefaults())
.build();
}
Please help
It seems that it is so tedious to set a decoder in every microservice.
No, it is not. Configuring a resource-server (OAuth2 REST API) can be as simple as:
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<!-- replace "webmvc" with "weblux" if your micro-service is reactive -->
<artifactId>spring-addons-webmvc-jwt-resource-server</artifactId>
<version>6.0.12</version>
</dependency>
#Configuration
#EnableMethodSecurity
public static class WebSecurityConfig { }
com.c4-soft.springaddons.security.issuers[0].location=https://localhost:8443/realms/realm1
com.c4-soft.springaddons.security.issuers[0].authorities.claims=realm_access.roles,ressource_access.some-client.roles,ressource_access.other-client.roles
com.c4-soft.springaddons.security.cors[0].path=/some-api
If you don't want to use my starters, you can still create your own copying from it (it is open source and each is composed of 3 files only).
If you don't implement access-control in each micro-service, then you can't bypass the gateway and it's going to be a hell to implement rules involving the resources itself (like only user who created that kind of resource can modify it).

Spring boot with keycloak add api key auth for specific endpoint

in spring boot app I have:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(jsr250Enabled = true)
#Slf4j
public class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
#Autowired
List<String> aIPWhiteList;
#Autowired
List<String> bIPWhiteList;
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
String aIPAddressesFilterStr = defineIPFilters(aIPWhiteList);
String bIPAddressesPFilterStr = defineIPFiltersbIPWhiteList);
http.authorizeRequests()
.antMatchers("/order/a/**").access(aIPAddressesFilterStr)
.antMatchers("/b/order").access(bIPAddressesFilterStr)
.anyRequest().permitAll();
http.cors().and().csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
private String defineP4IPFilters(List<String> whiteList) {
StringBuilder ipAddressesFilterStr = new StringBuilder();
for (String ip: whiteList) {
ipAddressesFilterStr.append("hasIpAddress('").append(ip).append("') or ");
}
return ipAddressesFilterStr.substring(0, ipAddressesFilterStr.length() - 4);
}
}
I wonder how can I for this "b/order" make another auth, based on API Key stored in headers.
Basically only for this 1 endpoint I want authorize users differently.
Other endpoints are authorized by keycloak, are done from registered users.
But here I would like to auth it only by api key that is static.
any ideas ?
thanks!
I have two ideas which should save you quite some trouble, even if does not answer directly your question:
Do not use KeycloakWebSecurityConfigurerAdapter
It is part of the (very) deprecated Keycloak adapters for spring. Use spring-boot-starter-oauth2-resource-server instead. Refer to those tutorials for various ways to do it (with Keycloak)
Use OAuth2 client-credentials flow in place of API key
It serves that exact purpose: authenticate a trusted programmatic clients with "static" secrets.
With Keycloak, just declare "confidential" clients ("Client authentication" set to "On" and "Service Accounts Roles" enabled). Secret is to be retrieved from a "credentials" tab for this clients in Keycloak admin console. You can then define and assign different roles for each client if needed (such roles will appear in access-tokens, so you'll be able to use it in spring-security access control decisions)
Such clients will authorize their requests to resource-server(s) with access-tokens issued by your Keycloak instance just as other clients (used by humans) do. Only the protocol to get tokens (OAuth2 flow) differs: client-credentials for "robots" and authorization-code for "humans".
From the resource-server point of view, there will be absolutely no difference: all requests will be authorized with access-tokens issued by the same authorization-server => no need for a different authentication mechanism on some endpoints, just apply regular role-based access-control or wahtever else written with spring-security expressions.

How can I implement access tokens for a public Spring Boot API?

I have a public facing API created with Spring Boot. I want users of the API to include a token with requests so that I can ensure they are allowed to access it and also track how many requests are made within a certain period of time.
I'm a little confused on how to approach this because everything I am reading online is where a user sends their user/password and the application returns a token. This is not what I'm looking for.
I want something similar to when you use Google APIs or similar APIs where your token never changes unless you want it to change and you do not send Google API your user/pass on the first request.
Maybe I'm not using the correct terminology. Can someone point me in the right direction?
If you want to authenticate using a static token for each user you will have to create a custom AuthenticationManager that gets your request header and tries to match it aginst known keys.
Example using single key, you'd have to add a lookup to user table if needed
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception
{
http.authorizeRequests(auth -> auth
.anyRequest().authenticated());
http.addFilter(tokenFilter());
http.csrf().disable();
return http.build();
}
public Filter tokenFilter()
{
AuthenticationTokenFilter filter = new AuthenticationTokenFilter(authenticationTokenHeader);
filter.setAuthenticationManager(authenticationManager());
return filter;
}
protected AuthenticationManager authenticationManager()
{
final AuthenticationManager authenticationManager = new AuthenticationManager()
{
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException
{
String principal = (String) authentication.getPrincipal();
if (!authenticationTokenValue.equals(principal))
{
throw new BadCredentialsException("The API key was not found or not the expected value.");
}
authentication.setAuthenticated(true);
return authentication;
}
};
return authenticationManager;
}
Do keep in mind that this approach is not the most secure, and if you'r application is public facing I would not recommend using this. And would recommend either using Authorization header with username/password or JWT
As a side note, I think you'r mistaken on how the google API authenticates. To the best of my knowledge all google APIs use Oauth2 for authentication, the static key/file you have does not provide access to the API it is only good for retrieving a short lived access token, in essence this would be not much different from standard JWT where you use some form of credentials to get a token with which you access the API.
for more info on JWT authentication:
https://www.baeldung.com/spring-security-oauth-jwt
https://blog.softtek.com/en/token-based-api-authentication-with-spring-and-jwt

Spring Boot 2.0 web flux custom authentication -- how to?

There are plenty of examples of minimal configurations of Spring Boot 2.0 security which compile or don't depending on which milestone or release candidate you try.
What is a minimal configuration that is not HTTP Basic, that will (1) let me access the HTTP request (headers, cookies, etc.) and also call my own authentication manager?
I would like to look at the headers and cookies, and decide from those who the user is, and whether or not the user is authenticated. How I do that should not matter to this answer -- the question is, what is the minimal Spring security config in order to allow me to hook in to the security infrastructure, so that my authentication is there in the reactive endpoints?
EDIT:
This works with Spring Boot 2.0.0.RC2, so my question could be, is this a correct way to introduce custom authentication into Spring Security?
#Configuration
#EnableWebFluxSecurity
public class SecurityConfiguration {
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
AuthenticationWebFilter authenticationFilter = new AuthenticationWebFilter(authentication -> {
authentication.setAuthenticated(true);
return Mono.just(authentication);
});
authenticationFilter.setAuthenticationConverter(serverWebExchange ->
Mono.just(new AbstractAuthenticationToken(new ArrayList<>()) {
#Override
public Object getCredentials() {
return null;
}
#Override
public Object getPrincipal() {
return "jim";
}
}));
return http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.FORM_LOGIN)
.authorizeExchange()
.anyExchange()
.authenticated()
.and()
.build();
}
}
You can imagine that in the converter, I am free to look into the request by way of serverWebExchange and inspect any headers or cookies I wish, and that later in the upper lambda (standing in for ReactiveAuthenticationManager) I can actually decide whether or not it should be authenticated.

Connecting Spring Security OAuth2 with SAML SSO

We’re having a microservices architecture based on spring boot where we have multiple microservices talking to each other and also a Javascript UI that connects to the different microservices.
Since this is an internal application and we have the requirement to connect them to our SAML2 endpoint to provide SSO, I’m getting a bit of a headache to connect all of this together. Ideally the microservices use oAuth2 between themselves (JWT) and the UI, but User Authentication is done through SAML2
The following I want to achieve with this:
UI Clients talk to the microservices by using JWT
Microservices use JWT as well to talk to each other. When a user initiates a request to a microservice and that microservice needs more data from another one, it uses the users JWT token (this should be fairly easy to do).
Having one central authentication microservice which is responsible for generating new tokens and authenticate the user against the SAML endpoint.
Storing some SAML details (e.g. Roles) in the authentication microservice
So I have tried many different things. What I can say is the following:
Using OAuth between microservices and JWT works fine and is not really an issue (e.g. this link is a nice tutorial to set this up http://www.swisspush.org/security/2016/10/17/oauth2-in-depth-introduction-for-enterprises )
Using SAML with spring-security-saml-dsl is also straight forward and works pretty well
I have implemented JWT in combination of spring-security-saml-dsl and that works also well (similar to this: https://www.sylvainlemoine.com/2016/06/06/spring-saml2.0-websso-and-jwt-for-mobile-api/ except that I use spring-security-saml-dsl) which I don’t like because it uses to much custom code with all the filters, etc. but would be a way to go.
I guess where I struggle with is the connection points of oauth2 Resource Server and the SAML services.
Regarding SAML I have the following that works fine:
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Value("${security.saml2.metadata-url}")
String metadataUrl;
#Value("${server.ssl.key-alias}")
String keyAlias;
#Value("${server.ssl.key-store-password}")
String password;
#Value("${server.port}")
String port;
#Value("${server.ssl.key-store}")
String keyStoreFilePath;
#Autowired
SAMLUserDetailsService samlUserDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/oauth/**").authenticated()
.and().exceptionHandling()
.and()
.authorizeRequests()
.antMatchers("/saml*").permitAll()
.anyRequest().authenticated()
.and()
.apply(saml()).userDetailsService(samlUserDetailsService)
.serviceProvider()
.keyStore()
.storeFilePath("saml/keystore.jks")
.password(this.password)
.keyname(this.keyAlias)
.keyPassword(this.password)
.and()
.protocol("https")
.hostname(String.format("%s:%s", "localhost", this.port))
.basePath("/")
.and()
.identityProvider()
.metadataFilePath(this.metadataUrl);
}
}
and that works fine. so when I hit a protected endpoint I will get redirected and can login through saml. I get the userdetails then in the samlUserDetailsService.
Regarding oauth I have something like this:
#Configuration
#EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore())
.tokenEnhancer(accessTokenConverter())
.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("ABC"); //needs to be changed using certificates
return converter;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("acme")
.secret("acmesecret")
.authorizedGrantTypes("refresh_token", "authorization_code")
.autoApprove(true)
.scopes("webapp")
.accessTokenValiditySeconds(60)
.refreshTokenValiditySeconds(3600);
}
}
This part also works fine with other micorservices where I have #EnableResourceServer
As far as I understand the OAuth part, the ClientDetailsServiceConfigurer just configures the client applications (in my case the other microservices) and I should use client_credentials kind of grant for this (but aren't sure). But how I would wire in the SAML part is not clear to me...
As an alternative I'm thinking about splitting this up. Creating a microservice that is an OAuth Authorization Service and another one that does the SAML bit. In this scenario, the SAML Microservice would connect to SAML and provide an endpoint like /me if the user is authenticated. The OAuth Authorization Service would then use the SAML Microservice to check if a user is Authenticated there and provide a token if that is the case. I would also do the same regarding refresh tokens.
As far as I understand this, I would implement this kind of logic in the
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {} method.
If there's a better approach, let me know!

Resources