- DE
- Services
- Our service portfolio
We bring your digital product vision to life, from crafting real-world testable prototypes to delivering comprehensive product solutions.
- Collaboration models
Explore collaboration models customized to your specific needs: Complete nearshoring teams, Local heroes from partners with the nearshoring team, or Mixed tech teams with partners.
- Way of working
Through close collaboration with your business, we create customized solutions aligned with your specific requirements, resulting in sustainable outcomes.
- Our service portfolio
- About Us
- Who we are
We are a full-service nearshoring provider for digital software products, uniquely positioned as a high-quality partner with native-speaking local experts, perfectly aligned with your business needs.
- Meet our team
ProductDock’s experienced team proficient in modern technologies and tools, boasts 15 years of successful projects, collaborating with prominent companies.
- Our locations
We are ProductDocic, a full-service nearshoring provider for digital software products, headquartered in Berlin, with engineering hubs in Lisbon, Novi Sad, Banja Luka, and Doboj.
- Why nearshoring
Elevate your business efficiently with our premium full-service software development services that blend nearshore and local expertise to support you throughout your digital product journey.
- Who we are
- Our work
- Career
- Life at ProductDock
We’re all about fostering teamwork, creativity, and empowerment within our team of over 120 incredibly talented experts in modern technologies.
- Open positions
Do you enjoy working on exciting projects and feel rewarded when those efforts are successful? If so, we’d like you to join our team.
- Candidate info guide
How we choose our crew members? We think of you as a member of our crew. We are happy to share our process with you!
- Life at ProductDock
- Newsroom
- News
Stay engaged with our most recent updates and releases, ensuring you are always up-to-date with the latest developments in the dynamic world of ProductDock.
- Events
Expand your expertise through networking with like-minded individuals and engaging in knowledge-sharing sessions at our upcoming events.
- News
- Blog
- Get in touch
23. May 2024 •2 minutes read
Exploring the Spring Authorization Server
ProductDock
Bojan Ćorić, our software engineer, delivered a talk at the year-end conference titled “Exploring the Spring Authorization Server.” He explored the functionalities of a relatively new authorization server from the Spring ecosystem, offering a potential solution tailored to your requirements.
Spring Authorization Server allows us to build a fully customizable OAuth2 and OpenID Authorization server. In this talk, we will customize the authorization server using common extension points while following best practices.
Spring Authorization Server in practice
Spring Authorization Server is a powerful and flexible identity and access management solution built on top of the Spring ecosystem, specifically designed to simplify the process of securing your applications and APIs. It provides robust authentication and authorization mechanisms, allowing you to control access to your resources effectively. It implements the OAuth 2.1 and OpenId Connect 1.0, simplifying integration with systems compliant with either of these specifications.
Minimal configuration
It works out of the box with minimal configuration. If you go to start.spring.io to generate a new project, the only dependency that you need is the OAuth2 Authorization Server (link to generate project).
The following configuration is required when starting the application as an authorization server:
# https://docs.spring.io/spring-boot/docs/current/reference/html/web.html#web.security.oauth2.authorization-server
spring:
security:
user:
name: user
password: password
oauth2:
authorizationserver:
client:
productdock:
registration:
client-id: "productdock"
client-secret: "{bcrypt}$2a$10$aXhyiUX2incAVeTnN85tueTYgUkqH/hh86i4Ex9FaeoRAyTU71n4i" # bcrypt of productdock
client-authentication-methods:
- "client_secret_basic"
authorization-grant-types:
- "authorization_code"
- "refresh_token"
- "client_credentials"
redirect-uris:
- "http://127.0.0.1/login/oauth2/code/productdock"
scopes:
- "openid"
- "profile"
- "email"
- "phone"
- "address"
require-authorization-consent: true
server:
port: 9090
Explicit configuration
In case you don’t want to configure this via yml file, you can do that explicitly from your code by defining the following beans:
@Bean
public UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager(
User.builder().username("user").password("{bcrypt}$2a$10$K4nPRbjfGTPLmrRu07PMT.h6TWPJvSGuZRUzATYyDHJ1JZBsRrNoi").build()
);
}
@Bean
public DaoAuthenticationProvider authenticationProvider(UserDetailsService userDetailsService) {
var authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
return authProvider;
}
// https://docs.spring.io/spring-authorization-server/reference/getting-started.html#defining-required-components
@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.oidc(Customizer.withDefaults());
http.exceptionHandling(exceptions -> exceptions.authenticationEntryPoint(
new LoginUrlAuthenticationEntryPoint("/login")))
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
return http.build();
}
@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
.formLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient client = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("productdock")
.clientSecret("{bcrypt}$2a$10$0lAOvcI202G9go0h7MD75OczUgjNb4cp2KlPsH13NFqQ0NtipN7dq")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.redirectUri("http://127.0.0.1/login/oauth2/code/productdock")
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.scope(OidcScopes.EMAIL)
.scope(OidcScopes.PHONE)
.scope(OidcScopes.ADDRESS)
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build();
return new InMemoryRegisteredClientRepository(client);
}
If you try now to access your resource server that is protected by this authorization server you will be redirected to the authorization server where you have to input your credentials.
Steps towards production
While this is nice for demonstration purposes, it is not so useful in real-life scenarios.
The first step to making this production-ready is to add some persistent storage so that our state is not lost when the application restarts.
Lets update pom.xml with additional dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
Update the application.yml with database credentials:
spring:
datasource:
username: authservice-rw
password: authservice-rw
url: jdbc:postgresql://localhost:5432/authservice
server:
port: 9090
To use persistent storage instead of in-memory, update the following beans:
@Bean
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
return new JdbcRegisteredClientRepository(jdbcTemplate);
}
@Bean
public JdbcUserDetailsManager jdbcUserDetailsManager(DataSource dataSource) {
return new JdbcUserDetailsManager(dataSource);
}
@Bean
public JdbcOAuth2AuthorizationConsentService jdbcOAuth2AuthorizationConsentService(
JdbcOperations jdbcOperations, RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationConsentService(jdbcOperations, registeredClientRepository);
}
@Bean
public JdbcOAuth2AuthorizationService jdbcOAuth2AuthorizationService(
JdbcOperations jdbcOperations,
RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository);
}
Database scripts for this work
If you wonder how where to find related database scripts for this to work, it is on your classpath:
- classpath:org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql
- classpath:org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql
- classpath:org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql
Managing keys
Another important part of the authorization server is managing keys that are used to sign tokens. Here is an example of how to generate in-memory keys:
@Bean
public JWKSource<SecurityContext> jwkSource() {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAKey rsaKey = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
JWKSet jwkSet = new JWKSet(rsaKey);
return new ImmutableJWKSet<>(jwkSet);
}
private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
Note that this is not a secure way to store your keys. Instead, use secure storage mechanisms such as environment variables, encrypted files, or key vault services such as HashiCorp Vault.
Next steps
With these key concepts, we built a usable authorization server that can be used in real-life scenarios. You can build an admin console to manage clients, users, sessions, etc. You can extend it with your chosen social login provider, such as Google, GitHub, Okta, etc. You can add two-factor authentication.
New Spring Authorization server solution
Bojan explored the functionalities of a relatively new authorization server from the Spring ecosystem. He concluded that the Spring Authorization Server allows us to build a fully customizable OAuth2 and OpenID Authorization server and customize it using common extension points while following best practices. To check how to protect your resources using the Spring Authorization Server check our GitHub repository.
Explore our blog section for exciting topics we’ll share with you soon.