Use Spring Security and Feature Flags to Test in Production
Okta is used as an Access Management and Identity platform. In short, Okta shoulders the responsibility of secure authentication and authorization, allowing you to spend time on other necessary tasks, such as the business logic of your app.
Not unlike peanut butter and chocolate, Okta and Spring Boot already go together well, and adding in feature flags care of Split allows you to test your app without having to redeploy. Talk about testing production in a smart way! In addition to all of this, you are also able to leverage Okta’s groups to easily manage who should see new content and who sees old content.
In this tutorial, I will be going through the steps to show you how to test in production. I will walk you through how to set up an Okta org, and configure an OpenID Connect App. I will also cover how to integrate Okta into a simple Spring Boot and Spring security app. Lastly, I will show you how to add feature flags to deliver a new UI experience for select users; who you can organize by group.
Get Started With Okta
OpenID Connect (OIDC) rides on top of OAuth 2.0 for a modern Single Sign-on, authentication and authorization standard. Okta provides these standards as a service. Don’t know anything about these standards yet? Good news - You don’t have to! By following some simple instructions to provision an Okta org and setup a hosted instance of OIDC, you can easily integrate a Spring Boot app with just configuration. Let’s get started with 3 easy steps:
If you’re interested in learning more about OIDC and OAuth 2.0, here and here are good places to start. Look for more links to posts on OIDC and OAuth 2.0 at the end of this post.
Register for an Okta Org
Head on over to: https://developer.okta.com/signup. Fill out the form and click Get Started.
You’ll get an email from Okta to confirm your address. Click on ACTIVATE MY ACCOUNT. You’ll next see an onboarding page in your Okta Org. You can skip this for now.
Let’s add a few users and groups for use later in the Spring Boot app.
Add Users to your Okta Org
Click Users on the menu bar at the top. Here, you’ll see the user already created for you with the name and email you submitted to create the Okta org.
Click Add Person. This will bring you to the input form for adding a new user:
Change the Password field to Set by admin and uncheck User must change password on first login. Create the following users by filling out the form and clicking on Save and Add Another for each:
First name | Last name | Username | Primary email | Password |
---|---|---|---|---|
Bob | Belcher | bob@belcher.com | bob@belcher.com | 123456aA$ |
Linda | Belcher | linda@belcher.com | linda@belcher.com | 123456aA$ |
Tina | Belcher | tina@belcher.com | tina@belcher.com | 123456aA$ |
Gene | Belcher | gene@belcher.com | gene@belcher.com | 123456aA$ |
Louise | Belcher | louise@belcher.com | louise@belcher.com | 123456aA$ |
Next, you’ll add some groups and assign some users to those groups.
Add Groups to Your Okta Org
Choose Users > Groups from the top-level menu. Here, you’ll see the built-in Everyone
group. As you might imagine, every current and a new user is automatically added to this group.
Click Add Group. This will bring you to the input form for adding a new group:
Enter BETA_TESTER in both the Name and Group Description fields. Click Add Group.
Click the link to the newly created BETA_TESTER group. Click Manage People. Click on each of Tina, Gene, and Louise on the left. Notice that those users are moved from the left to the right, indicating that they will be members of the group. Click Save to finalize these changes.
Now, you’re Okta org is configured with five users (the Belcher family), 3 of whom belong to the BETA_TESTER group.
Create an OIDC App in Your Okta Org
Next up, you’ll create an OIDC app that the Spring Boot app uses below. A deep dive into how OIDC works is outside the scope of this post, but check the links at the bottom if you’re interested in learning more about OIDC.
Click Applications from the top menu bar.
Click Add Application. Choose Web from the available app types and click Next. Update the Name field. Change the Login redirect URIs to: http://localhost:8080/login/oauth2/code/okta
. Leave all the other fields as their defaults and click Done at the bottom.
At the top of the General tab, you’ll see the Client Credentials section at the top. Copy the values for Client ID and Client secret. You’ll use these values below to configure Spring Boot.
Configure the OIDC App to Return Groups
The last Okta configuration step is to make sure that the list of groups a user belongs to is returned when a user authenticates. You’ll see below that this integrates very easily with Spring Security.
Click API > Authorization Servers from the top menu bar. Click the default link under the list of Authorization Servers (it should be the only one right now).
Click the Claims tab. Click Add Claim:
Fill the form in with the following (leave anything not named as default):
Field | value |
---|---|
Name | groups |
Include in token type | ID Token Always |
Value type | Groups |
Filter | Matches regex .* |
Click Create to finish.
Your Okta org is all ready to go to provide authentication and authorization services to your application. This is the great thing about Okta - with a little bit of configuration, you get all the services you need for auth allowing you to focus on the primary mission of your app.
All of the configuration you did in this section can be done via the Okta Management API. If this is something that interests you, check out the documentation here.
Next up, you’ll wire up Spring Boot with Spring Security to your Okta org. When you see how easy it is, your mind might just get blown.
Get Started With Spring Boot
The Spring team over at Pivotal has done an amazing job of making getting started with Spring Boot super easy. The Spring Initializr project over at https://start.spring.io allows you to select everything you need for the app you want to build.
For our purposes (and to keep things simple), you just need:
Component | Description |
---|---|
Spring Web | RESTful APIs and Spring MVC |
Thymeleaf | Server-side Java templating engine |
Okta | Easy integration with Okta; includes Spring Security |
Spring Initializr even makes it easy to load a pre-configured project from a direct link.
And, you can download the project from the command line with:
curl -G \
--data 'type=maven-project' \
--data 'language=java' \
--data 'bootVersion=2.3.4.RELEASE' \
--data 'baseDir=okta-split-example' \
--data 'groupId=com.okta.examples' \
--data 'artifactId=okta-split-example' \
--data 'name=okta-split-example' \
--data 'packageName=com.okta.examples.okta_split_example' \
--data 'packaging=jar' \
--data 'javaVersion=11' \
--data 'dependencies=web,thymeleaf,okta' \
--data-urlencode 'description=Feature Flags with Okta, Split and Spring Security' \
https://start.spring.io/starter.zip \
-o okta-split-example.zip
Expand the downloaded zip archive and open it in the IDE of your choice.
You can also find the completed application over on the Okta Developer GitHub repo.
Integrate With Okta
In order for this Spring Boot app to connect to your Okta org you need to set up an src/main/resources/application.yml
file with just three configuration parameters:
xxxxxxxxxx
okta
oauth2
issuer <yourOktaDomain>/oauth2/default
clientId <oidc client id>
clientSecret <oidc client secret>
At this point, the Spring Boot app is ready to run! Execute:
xxxxxxxxxx
./mvnw spring-boot:run
Navigate to: http://localhost:8080
. You should immediately be redirected to Okta to authenticate. After authenticating, you should end up with a 404
error, as you haven’t added any content to your app yet. You’ll do that next.
Create a Basic Thymeleaf Template
The last two pieces that are needed for a basic Model-View-Controller (MVC) app are a controller and a view in Spring Boot.
Create a Controller
Add a file called HomeController
with the following:
xxxxxxxxxx
public class HomeController {
"/") (
public String home( OidcUser user, Model model) {
model.addAttribute("username", user.getPreferredUsername());
model.addAttribute("roles", user.getAuthorities());
return "home";
}
}
Using the @AuthenticationPrincipal
annotation, the authenticated OidcUser
is autowired into the home method. A Model
object is passed into the method as well.
The Model
object is updated with the authenticated user’s username and a list of the Spring Security authorities associated with that user.
Since the project is configured with the Thymeleaf templating engine, returning a String
at the end of the controller method will automatically return a template with that name. In this case, Thymeleaf will look for a template named: home.html
.
Create a View
Create a file called: src/main/resources/templates/home/html
with the following:
xxxxxxxxxx
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head></head>
<body>
<h2>
<span th:inline="text">Hello, [[${username}]]!</span>
</h2>
<p></p>
Here are your roles:
<br/>
<ul th:each="role : ${roles}">
<li th:inline="text">[[${role}]]</li>
</ul>
</body>
</html>
This template displays the username
from the model. It then uses the Thymeleaf construct th:each
to iterate over the list of roles
from the model.
Restart the application, navigate once again to http://localhost:8080
and login as: tina@belcher.com
.
Amongst a number of default roles and scopes, you should see that she has the BETA_TESTER
role.
At this point, the Spring Boot + Spring Security application is fully functional (such as it is).
Next, I talk about feature flags with Split and then I bring Spring and Split together.
Get Started With Feature Flags
Split is a platform for combining feature flags and experiments with data, giving you more confidence in releases of your app.
In the example for this post, you integrate a split treatment such that a select group of users sees a new ‘beta’ interface for your app while ordinary users see the current production interface.
When our imaginary beta testing is complete, you can enable the new interface for all users.
Best of all, you could repeat this process, creating new beta experiences to be tested by your group and eventually making that generally available.
Getting setup with a free developer account for Split is as easy as 1, 2, 3:
- Go to: https://split.io, click: Free Account
- Fill out the registration form and click: SIGN UP
- Follow the link you receive in Email and set a password.
Create the Treatment in Split
Treatments allow you to define settings and behaviors for what you want to test. For my example, we want to set up a treatment that will return on or off depending on whether or not you are part of the beta tester group.
To start, click DE in the upper left. Choose Admin Settings and API Keys. Copy the value for sdk Type in the prod-default Environment. You’ll need this in the Spring Boot app shortly.
NOTE: If you’re new to using Split and/or on the free tier, the button in the upper left will say
DE
for default. If you’ve set up multiple workspaces, then the button will be labeled with the first two letters of the workspace name.
Next, Click Splits on the left-hand side and click Create Split. Give it a Name. Leave the other defaults and click Create.
Next, click Add Rules on the Targeting Rules tab. Split automatically adds on and off treatment definitions and sets off as the default.
For the use-case in this example, we want to add a group for which the treatment will return a value of on.
Click Add Rule in the Set Targeting Rules section. Here, we want to have the treatment return on if the user is in the group of beta testers. To accomplish this, enter groups in the Add attribute field. From the Select matcher dropdown, choose Set > has any of and enter BETA_TESTER in the field. Change the serve dropdown to on.
This now makes it read like an English sentence: “If the user has an attribute called groups and the groups list contains the value BETA_TESTER, then serve ‘on’ for the treatment”
Click Save Changes
Click Confirm on the summary screen.
Integrate the Treatment with Spring Security
Edit the pom.xml
file in the project. Add the following dependency:
xxxxxxxxxx
<dependency>
<groupId>io.split.client</groupId>
<artifactId>java-client</artifactId>
<version>4.0.1</version>
</dependency>
You can get the complete application, including the integration with Split over at the Okta Developer GitHub repo.
This brings the Split Java SDK into scope for the project.
Next, add a configuration to the project to make the Split Java Client available to the application. Here’s SplitConfig.java
:
xxxxxxxxxx
public class SplitConfig {
"#{ @environment['split.api-key'] }") (
private String splitApiKey;
public SplitClient splitClient() throws Exception {
SplitClientConfig config = SplitClientConfig.builder()
.setBlockUntilReadyTimeout(1000)
.enableDebug()
.build();
SplitFactory splitFactory = SplitFactoryBuilder.build(splitApiKey, config);
SplitClient client = splitFactory.client();
client.blockUntilReady();
return client;
}
}
Add the following to the src/main/resources/application.yml
file:
xxxxxxxxxx
split
api-key <your Split API Key>
Notice that it’s using the
@Value
annotation to pull in the Split API Key from the environment. This is a best practice. You should never hardcode an API Key into an application nor commit it in a git repo. In this case,application.yml
is listed in the.gitignore
file to ensure it’s not added to the git repo.
This is the key line that sets up the Split Client for use elsewhere in the code:
xxxxxxxxxx
SplitFactory splitFactory = SplitFactoryBuilder.build(splitApiKey, config);
Let’s set up a new template called home-beta.html
(It’s mostly copypasta from the original template and should be located in: src/main/resources/templates
):
xxxxxxxxxx
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head></head>
<body>
<h1>WELCOME TO THE BETA EXPERIENCE</h1>
<h2>
<span th:inline="text">Hello, [[${username}]]!</span>
</h2>
<p></p>
Here are your roles:
<br/>
<ul th:each="role : ${roles}">
<li th:inline="text">[[${role}]]</li>
</ul>
</body>
</html>
The last piece of the puzzle is in the HomeController
. I want to have the app render the new home-beta
template if the authenticated user is in the BETA_TESTER
group. Here’s the updated controller:
xxxxxxxxxx
public class HomeController {
SplitClient splitClient;
public HomeController(SplitClient splitClient) {
this.splitClient = splitClient;
}
"/") (
public String home( OidcUser user, Model model) {
model.addAttribute("username", user.getPreferredUsername());
model.addAttribute("roles", user.getAuthorities());
List<String> groups = user.getAuthorities().stream()
.map(GrantedAuthority::getAuthority).collect(Collectors.toList());
String inBeta = splitClient.getTreatment(
user.getPreferredUsername(),
"BETA_UI_EXPERIENCE",
Map.of("groups", groups)
);
return "on".equals(inBeta) ? "home-beta" : "home";
}
}
The first thing to notice is that I am injecting the SplitClient
using constructor dependency injection.
The real magic happens with the splitClient.getTreatment
call. The first parameter is the username provided by Spring Security for the authenticated user.
Note that in much of the Split documentation, this first parameter is referred to as a
key
. Don’t confuse this with the API Key, which should NEVER be used as the first parameter to thegetTreatment
call.
The second parameter is the name of the treatment in Split that we want to target.
The third parameter sends a map where the key is groups and the value is the list of group names that the authenticated user belongs to. In the case of our user linda, this will be Everyone (among some other defaults). In the case of our user louise, this will be Everyone and BETA_TESTER.
The last line of the controller method now returns the home-beta template if the result of getTreatment is on and the home template otherwise.
Fire up the app and try to login as linda in an incognito window. You should see the same home template as before. Kill that incognito window, open another one and login as louise. You should see the new beta template.
Make Your New Functionality Generally Available
Now that we have different treatments of our home template for regular users and beta testers, you may be wondering how to go about making the beta template available to everyone?
Split makes it easy-peasy. Go back to your Split definition for BETA_UI_EXPERIENCE and switch the serve setting in the Set The Default Rule section from off to on. Save and Confirm the change.
Without restarting the Spring Boot application, login as linda again. You should now see the same page that you saw for louise earlier.
Pretty cool, eh?
How to Repeat the Beta / Release Cycle
With this architecture in place, it’s now very easy to set up a new beta cycle. The steps would be something like this:
- Copy the
home-beta.html
template tohome.html
(now that it’s ready for production) - Create a new
home-beta.html
template - Set the default rule back to off in Split
- Redeploy the app
- Let your beta testers test the new experience
- When ready, set the default rule back to on in Split
- NO NEED TO REDEPLOY
You could do this over and over again and never touch the controller code. The only thing that’s changing are the templates and the settings in split.
This approach also lends itself to changing who is in your beta test program without having to change your code.
In a real application, you’d be working with a database or an Identity Management system where you could add and remove users from the BETA_TESTER group. With Okta, you can easily manage who belongs to the group from the admin console or via the Okta Management API. Those users would always see the latest and greatest beta while ordinary users would see only the current release.
Learn More About Building Secure Applications
I hope you’ve seen how useful it can be to set up different experiences for different users using Okta, Split and the native functionality built into Spring Security.
To continue learning about authentication, authorization and feature flags and experimentation, check out these links:
- Easy Session Sharing in Spring Boot with Spring Session and MySQL
- Deploy a Secure Spring Boot App to Heroku
- Use PKCE with OAuth 2.0 and Spring Boot for Better Security
- Leverage Spring Security to Test in Production
- Build a CRUD App with Spring Boot and MongoDB
- 7 Ways Feature Flags Improve Software Development
If you like this blog post and want to see more like it, follow @oktadev on Twitter, subscribe to our YouTube channel, or follow us on LinkedIn. As always, please leave a comment below if you have any questions.