How can I do an SSO to REST API with Spring security? - spring-security

I have an application comprised of an NGINX server, front end server and a backend server.
The NGINX and frontend handle the login (some legacy code I have to use for now), and it is done successfully.
I need to do an SSO to the RESTfull API with Spring Security.
The API is running in a Spring-boot application on an embedded tomcat 8 server.
The spring-security version is 4.0.3, the spring-boot version is 1.3.3.
I started with:
#Configuration
#EnableWebSecurity()
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().permitAll();
}
}
And now I get 401 (unauthorised) from the API, looking at the legacy code and consulting with other developers (I am new to the organisation), I can see that when using an XML configuration they used:
<authentication-manager alias="authenticationManager"/>
So the authentication manager just passes the requests (and they do the rest of the verifications in filters).
I would like to do something similar, or add the verification of data present in the headers at this stage, but I can't find a clear documentation for this that is not for XML configuration.
I thought this might be equivalent to the XML code used in the legacy system, but it doesn't work:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authentication -> {
authentication.setAuthenticated(true);
return authentication;
});
}
I am looking for a correction to my ideas, or alternative ways to do this.

Apparently I had a context problem, this works without the authentication manager configuration

Related

Use custom authorities with Okta Spring Boot Starter

I am using version 1.4.0 of the Okta Spring Boot Starter to validate incoming JWT tokens. By using the Starter, both authentication and authorization works out of the box (the default works so well you don't even need to define anything in your own security configuration).
But the default created roles are not to my liking, so I want to map the incoming scopes & roles to my own defined Spring authorities. When using the plain OAuth starter, one can define a AuthoritiesExtractor bean to do just that:
#Bean
public AuthoritiesExtractor authoritiesExtractor() {
return new YourOwnAuthoritiesExtractor();
}
Next to that, there is also the option to implement a custom jwtAuthenticationConverter in your security configuration:
http
..
.oauth2ResourceServer()
.jwt(jwt -> jwt.jwtAuthenticationConverter(new JwtAuthenticationConverter() {
#Override
protected Collection<GrantedAuthority> extractAuthorities(final Jwt jwt) {
// implementation
}
})
But all of these things seems not to work with the Okta Starter, because the extractors are never called when a JWT token is send to the server. Does someone know how to implement it for Okta?
TL;DR
It's not possible with the standard Okta Starter at the moment.
Explanation
As Okta uses its own configurer to setup the oauth2ResourceServer, you cannot use a custom JwtAuthenticationConverter as it will be overridden by the one Okta defines. Neither can you use the AuthoritiesExtractor, as the interface is not even packaged with the Okta Starter.
To overcome this issue, Okta introduced the concept of the AuthoritiesProvider interface. By defining a custom bean, you can add you own authorities to the ones already set by Okta :
#Bean
AuthoritiesProvider myCustomAuthoritiesProvider() {
return (user, userRequest) -> lookupExtraAuthoritesByName(user.getAttributes().get("email"));
}
Sadly enough, the AuthoritiesProvider interface only supports authorization code flow and not resource servers. As long as #160 is not resolved, there will be no native suport for Opaque and JWT.

Spring-security multiple authentication mechanisms with same credentials

I am trying to use 2 authorization mechanisms simultaneously: LDAP and DAO.
They work in order of configuration. This is not appropriate if the user credentials match in both mechanisms.
(For example, in LDAP there is a user m.smith with a password qwerty and in the database there is a user m.smith with a password qwerty)
Is it possible to somehow convey the flag which mechanism to use at the current moment? Аnother mechanism in this case should be ignored.
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.contextSource(contextSource)
.userSearchFilter("(sAMAccountName={0})")
.userDetailsContextMapper(userDetailsContextMapper);
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
If you have two mechanisms that are completely disjoint then you should probably fix it on upper levels (starting from UI). For instance if user uses LDAP authentication you would send a flag (for instance a HTTP header) that would indicate what auth type to use. Then you can implement custom filter that would perform specific type of authentication (programmatically).
I am not aware of any other solution that could do what you want in runtime.

Configure DefaultMethodSecurityExpressionHandler using Spring Security Java Config

I was trying to use the Role Hierarchy feature provided by Spring Security in a Spring Boot application and I found some problems with the java config.
In that application, I've the GlobalMethodSecurity enabled in order to use the #PreAuthorize annotations. Because of that, what I need to do in order to use the Role Hierarchy support is to create a new instance of a DefaultMethodSecurityExpressionHandler and set an instance of a RoleHierarchyImpl instance with the configuration that we want (the role hierarchy itself).
Although, if I try to do this using a WebSecurityConfigurerAdapter and the configure(HttpSecurity http) method, I'm not able to do that because the .expressionHandler() is expecting a SecurityExpressionHandler<FilterInvocation> and the DefaultMethodSecurityExpressionHandler is a SecurityExpressionHandler<MethodInvocation>.
In order to solve this problem, I found out that the solution is to create a subclass of GlobalMethodSecurityConfiguration and override the method that is creating the ExpressionHandler bean like it's described in the docs
GlobalMethodSecurityConfiguration - Spring Security Docs
By saying this, I'm wondering if the .expressionHandler() method shouldn't also allow to do this configuration. If not, maybe a reference in the javadoc pointing to the GlobalMethodSecurityConfiguration would be useful.
After this, I think that the idea of WebSecurityConfigurerAdapter and the .expressionHandler are just applied to the security that is applied based on a Filter of the http request and it isn't supposed to be applied at the method level, but opinions and advices are welcome to confirm that I'm doing as it's supposed.

How to authenticate WebSocket within an existing HTTP environment with Spring Security

I have a pretty simple requirement (I use Spring-Security 4.0.1) but I can't find any examples on the web except what is been told on this page: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html#websocket-server-handler
It is relatively simple to integrate a WebSocketHandler into other
HTTP serving environments with the help of
WebSocketHttpRequestHandler.
What I have: An implementation of WebSocketHandler that does the job and an HTTP serving environments using a Basic Authentication. My WebApplicationInitializer looks like this:
public class MyWebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
...
// WebSocket support - Handshake
Dynamic ws = servletContext.addServlet("webSocketHttpRequestHandler", new HttpRequestHandlerServlet());
ws.addMapping("/streaming/*");
// Spring Security Filter
FilterRegistration.Dynamic springSecurity = servletContext.addFilter("springSecurityFilterChain", new DelegatingFilterProxy());
springSecurity.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
}
}
This is how I plugged my websocket endpoint to my existing web application.
My WebSocket configuration class looks like (very simplified) this:
#Configuration
public class WebSocketServicesConfig{
#Bean
public WebSocketHttpRequestHandler webSocketHttpRequestHandler() {
return new WebSocketHttpRequestHandler(new StreamingWebSocketHandler());
}
}
StreamingWebSocketHandler implements WebSocketHandler.
I also have a RESTful Web Service (in the same server) that uses the configured Basic Authentication.
What is working: My RESTful Web Service is working with any web browsers. I can do some authenticated queries (credentials can be sent in the HTTP headers).
WebSocket queries are working and ask for authentication the first time I try to do some (under FireFox, a popup appears asking for credentials, once I enter them, client and server are able to communicate via WebSocket messages).
In my WebSocketHandler, the Spring object: WebSocketSession that contains informations about the authenticated user is correct (#getPrincipal() method returns a Authentication containing the right granted Authorities, details and so on...).
Note that once the websocket is authenticated, I can relaunch the query without re-enter them.
What I want: On a user point of view, this is bad because the credentials are required twice:
First for RESTful queries
Second for WebSocket queries
How can I bypass the second authentication assuming the first one succeeded? Is there a way to detect the client has been authenticated and not ask for credentials?
What I don't want: I don't want to use neither Stomp over websocket nor SockJs (I don't need to support old web browsers).

IIS7 - Password Protect Development Server

I have a development server running IIS 7.0 with an ASP.NET MVC Web Application, that authenticates using Forms Authentication/Membership.
I need to be able to prevent unauthorized users from viewing this site. Our customers however should be able to enter a simple username/password to gain access.
After they do so, they should be able to interact with the web application using Forms Authentication as if they just came to an unprotected site.
Any suggestions?
My previous answer said forms auth and basic http auth could live side by side in II7 integrated mode. I was completely wrong and have since made a simple solution.
Using a custom HttpModule you can add basic auth along side regular forms auth
public class CustomBasicAuthHttpModule : IHttpModule
{
private HttpApplication httpApplicationContext;
public void Dispose()
{
}
public void Init(HttpApplication context)
{
this.httpApplicationContext = context;
context.BeginRequest += this.OnBeginRequest;
context.EndRequest += this.OnEndRequest;
}
private void OnBeginRequest(object sender, EventArgs e)
{
// your logic of checking Auth header goes here
if (this.httpApplicationContext.Request.Headers["Authorization"] != "Basic base64-encoded-user:pass")
{
this.httpApplicationContext.Response.StatusCode = 401;
this.httpApplicationContext.Response.End();
}
}
private void OnEndRequest(object sender, EventArgs e)
{
if (this.httpApplicationContext.Response.StatusCode == 401)
{
this.httpApplicationContext.Response.AddHeader("WWW-Authenticate", "Basic");
}
}
then in your web.config
<system.webServer>
<modules>
<add name="CustomBasicAuthHttpModule" type="Namespace.CustomBasicAuthHttpModule, AssemblyName"/>
</modules>
</system.webServer>
I just did this with Helicon Ape. The free license includes 3 sites, which for me was good enough.
If you use this on a site, just remember to check if the license is activated for the site (start menu > helicon > ape > manager, help, license manager).
As Aaron points out, this isn't so straightforward in IIS7. Now, the flip side is this old trick is insecure at best and there are better ways to do it now and being able to use all authenticaiton methods with all apps has lots of advantages. There are some ways to get around this such as:
a) keeping the development site behind a VPN which your clients can access.
b) reverse proxying the site, and letting the proxy do the http authentication.
c) A bit more involved would be to build your app with a demo mode. Trick here is to make it turn on or off from the first request given a special magic query string. Check for that in Session_Start() then tag users that come with it and profit.
We wrote a custom module for IIS to allow certain IP ranges through automatically, and present anyone else with a login dialogue. Once they'd logged in, it stored that fact in their session and simply passed requests through.
Works alright, can be applied to anything in IIS sites or services.

Resources