How to Configure OAuth 2.0 in Spring by storing the Auth Codes in DataBase instead of ConCurrentHashMap - jdbc

Currently I am using Spring Security Oauth2 2.3.2 Release. I want to set up a Authorization Code Grant Type Based Authentication.
I debugged the Spring Security "/oauth/authorize" EndPoint. I found below code
private AuthorizationCodeServices authorizationCodeServices = new InMemoryAuthorizationCodeServices();
I debugged more into the code and I found out that AuthorizationCodeServices is implementented by 3 Classes .
1) InMemoryAuthorizationCodeServices
2) JdbcAuthorizationCodeServices
3) RandomValueAuthorizationCodeServices
Default Implementation of Spring provides the InMemoryAuthorizationCodeServices. I want to configure it based on the JdbcAuthorizationCodeServices. Now, for that do I need to write my own endpoint and code or could it be done using any configuration?

Related

OAuth2 / OpenID Connect configuration in Spring Boot and OpenAPI

I am securing my application with OpenID Connect and Keycloak.
My API documentation is OpenAPI 3 which I generate from code.
#Bean
public OpenAPI customOpenAPI() {
OAuthFlows flows = new OAuthFlows();
OAuthFlow flow = new OAuthFlow();
String authUrl = authServerUrl + "/realms/" + realm + "/protocol/openid-connect/auth";
flow.setAuthorizationUrl(authUrl);
Scopes scopes = new Scopes();
flow.setScopes(scopes);
flows = flows.implicit(flow);
return new OpenAPI()
.components(new Components().addSecuritySchemes("keycloak",
new SecurityScheme().type(SecurityScheme.Type.OAUTH2).in(SecurityScheme.In.HEADER).flows(flows)))
.info(new Info().title(appName)
.version(appVersion))
.addSecurityItem(new SecurityRequirement().addList("keycloak",
Arrays.asList("read", "write")));
}
This works fine as I can authenticate via Keycloak's implicit flow. Now I want to secure only some endpoints and not all in my API.
I tried to annotate the desired endpoint with
#SecurityRequirement(name = "keycloak")
This seems to work. When I look in the JSON api-docs of my Swagger UI HTML, I can see that this entity is added to the endpoint configuration.
The problem seems to be that it is also placed as a global statement in the docs, so it is used for every endpoint.
QUESTION:
Is there a way to define the security scheme as non-global so I can place it to specific endpoints?
I could not find much documentation on configuring OpenAPI 3 by code. I've seen a way to add it via Annotation to the main class of the object via #OpenAPIDefinition( but I could not find a documentation for it and I did not figure out how to define the openid connect specifics within this annotation.

Spring Boot Security Oauth2 - adding dynamic OIDC parameters

How do I add OIDC token request parameters dynamically in my app code? I want to add domain_hint based on some data received by my controller from as yet un-authenticated user.
You can implement custom OAuth2AuthorizationRequestResolver
and then add to your spring security configuration
.oauth2Login(req->
req.authorizationEndpoint()
.authorizationRequestResolver(new YourCustomAuthorizationRequestResolver)
)

Issuer URI of Spring Security OAuth2 Authorization Server

I'm developing OAuth 2.0 authorization server and resource server using Spring Security OAuth 2.0 2.3.4.REELEASE. Little did I know that Spring has deprecated it in favor of Spring Security. However, authorization server is not included in the migration to Spring Security. Only the resource server is included as they are encouraging users to use products instead (one is KeyCloak).
But like many others, I really have to develop my own authorization server so I keep on using Spring Security OAuth 2.0 but only for the authorization server. As for resource server, I'll be using the resource server from Spring Security. I think authorization and resource servers are independent and they are based on the standards of OAuth 2.0 so the implementation could be from different frameworks.
My problem is specifying the authorization server via issuer-uri. I could not determine how Spring OAuth 2.0 authorization server exposes its issuer-uri if there is any. I could not find either any docs how to create one if it does not have one be default.
Please help. Thank you.
Since Spring Security Oauth has ended support you should use the Spring Authorization Server now (https://spring.io/projects/spring-authorization-server). For the isser uri there you can use the base uri of the server. To verify this you can check the well-known-Endpoint under /.well-known/openid-configuration.
The isser uri in the authorization server can be edited with the ProviderSettings:
#Bean
public ProviderSettings providerSettings() {
return ProviderSettings.builder()
.issuer("https://authorization-server:8443")
.build();
}
I believe the solution you are looking for lies within the TokenEnhancer. So, implement a custom token enhancer and add the iss key like so.
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("iss", "issuer uri here");
...
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}

Spring Boot 2.0.0 + OAuth2

Does Spring Boot 2 + Sping Security OAuth2 still support the #AuthorizationServer annotation? From reading the release notes some things haven't been ported over:
Oauth2 Support
Here is the relevant section of my build.grade:
Auth Server
// security
compile "org.springframework.boot:spring-boot-starter-security:${springBootVersion}"
// oauth
// https://mvnrepository.com/artifact/org.springframework.security.oauth/spring-security-oauth2
compile "org.springframework.security.oauth:spring-security-oauth2:2.2.1.RELEASE"
Client Server
// support for Oauth2 user token services not yet migrated into Spring Boot 2.0
compile "org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.0.1.BUILD-SNAPSHOT"
And right now my Authorization Server Oauth2 endpoints just return a 401 when i try to pass a client-id and client-secret in as Basic Authentication to /oauth/token. Passing in a username and password gives a different code path. So it looks like the OAuth filters aren't quite lined up.
I also found this as well: Spring Boot 2 OAuth2 starter changes.
Has there been a configuration update or do I need a different set of gradle dependencies to restore the Authorization Server to it's previous state?
Thanks!
UPDATE
I wanted to close the loop on this question. In addition to encrypting the client-secrets. The RedisTokenStore issue has also been resolved as of Spring OAuth 2.3.2: Spring OAuth 2.3.2
Spring Security 5 uses a modernized password storage, see OAuth2 Autoconfig:
If you use your own authorization server configuration to configure the list of valid clients through an instance of ClientDetailsServiceConfigurer as shown below, take note that the passwords you configure here are subject to the modernized password storage that came with Spring Security 5.
To solve your problem, see Spring Security Reference:
Troubleshooting
The following error occurs when one of the passwords that are stored has no id as described in the section called “Password Storage Format”.
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
at org.springframework.security.crypto.password.DelegatingPasswordEncoder$UnmappedIdPasswordEncoder.matches(DelegatingPasswordEncoder.java:233)
at org.springframework.security.crypto.password.DelegatingPasswordEncoder.matches(DelegatingPasswordEncoder.java:196)
The easiest way to resolve the error is to switch to explicitly provide the PasswordEncoder that you passwords are encoded with. The easiest way to resolve it is to figure out how your passwords are currently being stored and explicitly provide the correct PasswordEncoder. If you are migrating from Spring Security 4.2.x you can revert to the previous behavior by exposing a NoOpPasswordEncoder bean. For example, if you are using Java Configuration, you can create a configuration that looks like:
Reverting to NoOpPasswordEncoder is not considered to be secure. You should instead migrate to using DelegatingPasswordEncoder to support secure password encoding.
#Bean
public static NoOpPasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
if you are using XML configuration, you can expose a PasswordEncoder with the id passwordEncoder:
<b:bean id="passwordEncoder"
class="org.springframework.security.crypto.NoOpPasswordEncoder" factory-method="getInstance"/>
Alternatively, you can prefix all of your passwords with the correct id and continue to use DelegatingPasswordEncoder. For example, if you are using BCrypt, you would migrate your password from something like:
$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
to
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
OAuth2 AuthorizationServer uses basic authentication.
So, you also need to encode your client secret with delegatedPasswordEncoder in AuthorizationServerConfig to completely solve "There is no PasswordEncoder mapped for the id "null" " exception.
Yao Liu's answer solved my problem.
1) created a bean to auto wire PasswordEncoder;
#Bean
public PasswordEncoder passwordEncoder() {
String idForEncode = "bcrypt";
Map<String, PasswordEncoder> encoderMap = new HashMap<>();
encoderMap.put(idForEncode, new BCryptPasswordEncoder());
return new DelegatingPasswordEncoder(idForEncode, encoderMap);
}
2) Auto wired passwordEncoder in AuthorizationServerConfig class;
#Autowired
private PasswordEncoder passwordEncoder;
3) encoded CLIENT_SECRET with passwordEncoder.
#Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
configurer
.inMemory()
.withClient(CLIENT_ID)
.secret(passwordEncoder.encode(CLIENT_SECRET))
.authorizedGrantTypes(GRANT_TYPE_FOR_LOGIN, GRANT_TYPE_FOR_REFRESH)
.scopes(SCOPE_READ, SCOPE_WRITE)
.accessTokenValiditySeconds(TOKEN_VALIDITY_SECONDS)
.refreshTokenValiditySeconds(TOKEN_VALIDITY_SECONDS)
.resourceIds(RESOURCES_IDS);
}
That's it.
As #false_memories stated above, with Spring Boot 2 you need to encode your secret. In my project, it looks like:
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
String secretEncoded = passwordEncoder().encode("secret");
clients.inMemory().withClient("some-web-app").secret(secretEncoded).accessTokenValiditySeconds(expiration)
.scopes("read", "write").authorizedGrantTypes("password", "refresh_token").resourceIds("resource");
}
I wanted to close the loop on this question. In addition to encrypting the client-secrets. The RedisTokenStore issue has also been resolved as of Spring OAuth 2.3.2: Spring OAuth 2.3.2

Using/configuring Spring Security with Spring 4 and Hibernate

I want to implement the login/logout (authentication/authorization) system of my Spring 4 MVC application with Spring Security.
Currently I use a very simple hand-made implementation which basically does nothing more than comparing the entered username and MD5 hashed password with the database values by looking up the user by the username using a custom service method and comparing the encrypted passwords.
If the passwords match, the username of the logged in member is saved in the session and a ControllerAdvice looks up the Member object for the user using the username in the session prior to each request. The checkLogin method returns true is username and password match:
#Service("loginService")
#Transactional
public class LoginServiceImpl implements LoginService {
private MemberDao dao;
//more methods
#Override
public boolean checkLogin(String username, String password) {
String hashedPassword = getPasswordHash(password);
return dao.checkLogin(username, hashedPassword);
}
}
This does work but is not a very elegant solution, does not handle different roles and is probably not very secure. Besides I want to become familiar with Spring Security.
Reading the official tutorial for Spring Security (http://docs.spring.io/spring-security/site/docs/4.0.4.RELEASE/reference/htmlsingle/#tech-userdetailsservice) the way to go to authenticate against the Login service method does not become clear to me.
The tutorial discusses authentication direct against the database but I cannot find anything about using a Service method to perform the authentication and in my layered architecture, the database is hidden behind the Servoce and Dao (Hibernate) layers.
Also most examples in the tutorial use XML based instead of Java based configuration which I use for my application.
After having search a lot with search engines, I still have not found a tutorial which implements Spring Security in a Spring MVC application using a familiar layered structure using a Service and Dao layer.
Do I need to bypass Service and DAO/Hibernate layers and authenticate directory against the database? Or write a custom authentication-provider implementing UserDetailsService as described in this post?
Spring Security 3 database authentication with Hibernate
And is configuring Spring Security possible with Java based configuration only? I am a bit lost with this issue so I hope for some hints...

Resources