Add Authentication to Play Framework With OIDC and LoginRadius
Learn how to integrate play-pac4j and use its OIDC support to authenticate with LoginRadius using JAVA play framework.

Table of Contents
- What is OpenID Connect (OIDC) Protocol?
- What is Play Framework?
- What is pac4j?
- Getting started
- Create New Play Project
- Run Play Project
- Integrate pac4j with Play Project
- Create an OIDC app in LoginRadius
- Time to test complete Integration
- Source Code
What is OpenID Connect (OIDC) Protocol?

Learn How to Master Digital Trust

The State of Consumer Digital ID 2024

Top CIAM Platform 2024
In this blog post, I will write a step-by-step tutorial to add Authentication to the Play Framework Application with OIDC and LoginRadius. I will be using pac4j and it's play-pac4j integration in this tutorial. Before jumping into the tutorial, let's learn about few concepts.
What is OpenID Connect (OIDC) Protocol?
OIDC is an authentication protocol that allows users to verify their identity when they are trying to access a protected HTTPS endpoint. OIDC is an evolutionary development of ideas implemented earlier in OAuth and OpenID.
OpenID Connect allows clients to request and receive information about authenticated sessions and end-users. It allows all types of clients, including
- Web-based
- Mobile
- Javascript clients etc...
You can find out more details about OIDC here
What is Play Framework?
Play Framework is an open-source web application framework that follows the model–view–controller architectural pattern. It is written in Scala and usable from other programming languages that are compiled to JVM Bytecode.
It makes it easy to build web applications with Java & Scala. Play is based on a lightweight, stateless, web-friendly architecture. Built on Akka, Play provides predictable and minimal resource consumption (CPU, memory, threads) for highly-scalable applications.
What is pac4j?
pac4j is an easy and powerful security engine for Java to authenticate users, get their profiles and manage authorizations in order to secure web applications and web services.
It provides a comprehensive set of concepts and components. It is based on Java and available under the Apache 2 license. It is available for most frameworks/tools and supports most authentication/authorization mechanisms.
pac4j is supported with most of the Java frameworks like
- Spring Web MVC
- Spring Boot
- Spring Security (Spring Boot)
- Play 2.x
- Vertx Spark Java
- Javalin
- Dropwizard
- Lagom
- Akka HTTP & many more...
Getting started
There are multiple options you can get started with the Play framework.
- You can create a new Play project by downloading the starter project here
- Create a new Play application using
sbt
.
For this tutorial, I will be using creating a new play project using sbt
.
Install sbt on Mac OS
1brew install sbt
You can find Installation steps for Windows and Linux here.
Prerequisites
For this tutorial, I will be using
- Java 11
- sbt - 1.5.3
- play-pac4j - 11.0.0-PLAY2.8
- pac4j-oidc - 5.1.0
Create New Play Project
Create a new java play project using the following command
1sbt new playframework/play-java-seed.g8
The above command will prompt you to fill in the project name
and project package structure in organization
as shown below.
1[info] welcome to sbt 1.5.3 (Oracle Corporation Java 11.0.2)
2[info] set current project to new (in build file:/private/var/folders/_9/rcqwq2vx1cl_1sc4xdhb5_5c0000gn/T/sbt_661d1bf7/new/)
3This template generates a Play Java project
4name [play-java-seed]: loginradius-play-oidc
5organization [com.example]: com.loginradius.developer
6Template applied in /Users/vishnuchilamakuru/self/longinradius/./loginradius-play-oidc
Run Play Project
Now that the project is created with a base template. You can test it by running the project using the following command from loginradius-play-oidc
folder.
1sbt run
Now visit http://localhost:9000
, and it should look like this.
Integrate pac4j with Play Project
1. Add pac4j dependencies to build.sbt
Add pac4j dependencies, Java 11 as required, and target version to compile the project in build.sbt
.
1name := """loginradius-play-oidc"""
2organization := "com.loginradius.developer"
3version := "1.0-SNAPSHOT"
4lazy val root = (project in file(".")).enablePlugins(PlayJava)
5scalaVersion := "2.13.6"
6initialize := {
7val _ = initialize.value // run the previous initialization
8val required = "11"
9val current = sys.props("java.specification.version")
10assert(current == required, s"Unsupported JDK: java.specification.version $current != $required")
11}
12scalacOptions += "-target:jvm-11"
13javacOptions ++= Seq("-source", "11", "-target", "11")
14libraryDependencies ++= Seq(
15guice,
16ehcache,
17"org.pac4j" %% "play-pac4j" % "11.0.0-PLAY2.8",
18"org.pac4j" % "pac4j-oidc" % "5.1.0",
19"com.typesafe.play" % "play-cache_2.13" % "2.8.8",
20"com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.12.3"
21)
2. Create Security Module
Create app/modules/SecurityModule.java
. This class configures OIDC, sets up a secure HttpActionAdapter, and registers callback and logout controllers.
- SecurityModule.java
1package modules;
2import com.google.inject.AbstractModule;
3import com.nimbusds.oauth2.sdk.ParseException;
4import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod;
5import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
6import com.typesafe.config.Config;
7import net.minidev.json.JSONObject;
8import org.pac4j.core.authorization.authorizer.RequireAnyRoleAuthorizer;
9import org.pac4j.core.client.Clients;
10import org.pac4j.core.context.session.SessionStore;
11import org.pac4j.oidc.client.OidcClient;
12import org.pac4j.oidc.config.OidcConfiguration;
13import org.pac4j.play.CallbackController;
14import org.pac4j.play.LogoutController;
15import org.pac4j.play.http.PlayHttpActionAdapter;
16import org.pac4j.play.store.PlayCacheSessionStore;
17import play.Environment;
18import java.util.Optional;
19public class SecurityModule extends AbstractModule {
20private final Config configuration;
21
22public SecurityModule(final Environment environment, final Config configuration) {
23 this.configuration = configuration;
24}
25
26@Override
27protected void configure() {
28
29 bind(SessionStore.class).to(PlayCacheSessionStore.class);
30
31 final OidcConfiguration oidcConfiguration = new OidcConfiguration();
32 oidcConfiguration.setDiscoveryURI(configuration.getString("oidc.discoveryUri"));
33 oidcConfiguration.setClientId(configuration.getString("oidc.clientId"));
34 oidcConfiguration.setSecret(configuration.getString("oidc.clientSecret"));
35 oidcConfiguration.setClientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
36
37 addProviderMetadata(oidcConfiguration);
38
39 final OidcClient oidcClient = new OidcClient(oidcConfiguration);
40 oidcClient.addAuthorizationGenerator((ctx, session, profile) -> {
41 profile.addRole("ROLE_ADMIN");
42 return Optional.of(profile);
43 });
44
45 final String baseUrl = configuration.getString("baseUrl");
46 final Clients clients = new Clients(baseUrl + "/callback", oidcClient);
47
48 final org.pac4j.core.config.Config config = new org.pac4j.core.config.Config(clients);
49 config.addAuthorizer("admin", new RequireAnyRoleAuthorizer("ROLE_ADMIN"));
50 config.setHttpActionAdapter(PlayHttpActionAdapter.INSTANCE);
51 bind(org.pac4j.core.config.Config.class).toInstance(config);
52
53 // callback
54 final CallbackController callbackController = new CallbackController();
55 callbackController.setDefaultUrl("/");
56 bind(CallbackController.class).toInstance(callbackController);
57
58 // logout
59 final LogoutController logoutController = new LogoutController();
60 logoutController.setDefaultUrl("/?defaulturlafterlogout");
61 bind(LogoutController.class).toInstance(logoutController);
62
63}
64
65private void addProviderMetadata(OidcConfiguration oidcConfiguration) {
66 JSONObject jsonObj = new JSONObject();
67 jsonObj.appendField("token_endpoint", configuration.getString("oidc.tokenUri"));
68 final OIDCProviderMetadata providerMetaData;
69 try {
70 providerMetaData = OIDCProviderMetadata.parse(jsonObj);
71 oidcConfiguration.setProviderMetadata(providerMetaData);
72 } catch (ParseException e) {
73 e.printStackTrace();
74 }
75}
76
77}
3. Create Secured Endpoint in Controller
In app/controllers/HomeController.java
add the following methods
- A method that is secured by the OIDC client
- A method to show the profile information returned from LoginRadius(or any identity provider) on successful login.
- A method to get profile information
After adding the above methods HomeController.java
looks like this.
1package controllers;
2import com.google.inject.Inject;
3import org.pac4j.core.context.session.SessionStore;
4import org.pac4j.core.profile.ProfileManager;
5import org.pac4j.core.profile.UserProfile;
6import org.pac4j.play.context.PlayContextFactory;
7import org.pac4j.play.java.Secure;
8import org.pac4j.play.store.PlayCacheSessionStore;
9import play.mvc.*;
10import java.util.ArrayList;
11import java.util.List;
12/**
13
14
15This controller contains an action to handle HTTP requests
16
17
18to the application's home page.
19*/
20public class HomeController extends Controller {
21/**
22
23An action that renders an HTML page with a welcome message.
24The configuration in the <code>routes</code> file means that
25this method will be called when the application receives a
26<code>GET</code> request with a path of <code>/</code>.
27*/
28public Result index() {
29return ok(views.html.index.render());
30}
31
32@Secure(clients = "OidcClient")
33public Result oidcIndex(Http.Request req) {
34return protectedIndex(req);
35}
36public Result protectedIndex(Http.Request req) {
37return ok(views.html.protectedindex.render(getProfiles(req)));
38}
39@Inject
40private SessionStore sessionStore;
41private List<UserProfile> getProfiles(Http.Request req) {
42final ProfileManager profileManager = new ProfileManager(PlayContextFactory.INSTANCE.newContext(req), sessionStore);
43final List<UserProfile> profiles = new ArrayList<>();
44if (profileManager.getProfile().isPresent()) {
45profiles.add(profileManager.getProfile().get());
46}
47return profiles;
48}
49
50
51}
4. Create Views
- Update
app/views/index.scala.html
as follows to add the link protected by OIDC authentication.
1@()
2@main("Welcome to Play") {
3<h1>Welcome to Play!</h1>
4<a href="oidc/index.html">Protected URL by OIDC</a>
5}
- Add
app/views/protectedindex.scala.html
with the following content below. This page will be shown after Successful Login using OIDC in LoginRadius.
1@(profileList: java.util.List[org.pac4j.core.profile.UserProfile])
2@import scala.collection.JavaConverters._
3@profiles() = { @profileList.toList }
4<h1>Protected Area</h1>
5<a href="..">Back</a>
6<ul>
7 <li><a href="/logout?url=/?forcepostlogouturl">Logout</a></li>
8</ul>
9<p>
10profiles: @profiles
11</p>
5. Configure Routes
Now we already added methods in HomeController.java
and configured callback, logout controllers in SecurityModule.java
. Let's configure the routes to map to these methods in conf/routes
.
conf/routes
will look like this.
1# Routes
2# This file defines all application routes (Higher priority routes first)
3# ~~~~
4An example controller showing a sample home page
5GET / controllers.HomeController.index
6GET /oidc/index.html controllers.HomeController.oidcIndex(request: Request)
7GET /protected/index.html controllers.HomeController.protectedIndex(request: Request)
8GET /callback @org.pac4j.play.CallbackController.callback(request: Request)
9POST /callback @org.pac4j.play.CallbackController.callback(request: Request)
10GET /logout @org.pac4j.play.LogoutController.logout(request: Request)
11Map static resources from the /public folder to the /assets URL path
12GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
6. Add Application Configuration variables
Finally, configure the variables mentioned in SecurityModule.java
in conf/application.conf
as follows.
1play {
2 modules {
3 enabled += modules.SecurityModule
4 }
5}
6baseUrl = "http://localhost:9000"
7oidc.discoveryUri = "https://cloud-api.loginradius.com/sso/oidc/v2/{loginradius-site-name}/{loginradius-app-name}/.well-known/openid-configuration"
8oidc.clientId = "{clientId}"
9oidc.clientSecret = "{clientSecret}"
10oidc.tokenUri = "https://cloud-api.loginradius.com/sso/oidc/v2/{loginradius-site-name}/token"
Create an OIDC app in LoginRadius
Login to your LoginRadius account or signup here if you don't have one.
Once you log in you can see by default, one application will be created for you. Otherwise, you can create a new application here from the following screen by clicking New App
.
I will be using the existing application itself for this demo as I am using a free plan. In the free plan, you can create only one application (no need for card details).
Upgrade your application subscription to the Developer Pro
Plan to configure OIDC. (Developer Pro
Plan is available with 21 days trial).
Now click on Select & Configure
on your application and navigate to the Integration
Section and Configure Open ID
configuration. You can find step-by-step details to configure OIDC here.
Once you configure these conf/application.conf
will look something like this.
1# This is the main configuration file for the application.
2# https://www.playframework.com/documentation/latest/ConfigFile
3play {
4modules {
5enabled += modules.SecurityModule
6}
7}
8baseUrl = "http://localhost:9000"
9oidc.discoveryUri = "https://cloud-api.loginradius.com/sso/oidc/v2/dev-svv3qlcj2y/pac4j-play-loginradius-demo/.well-known/openid-configuration"
10oidc.clientId = "8ce24413-9b7e-4282-9f5b-e0e5ec13a42a"
11oidc.clientSecret = "c9c61f6e-325f-40d6-89fd-696d17f970eb"
12oidc.tokenUri = "https://cloud-api.loginradius.com/sso/oidc/v2/dev-svv3qlcj2y/token"
13Site Name - dev-svv3qlcj2y
14App Name - pac4j-play-loginradius-demo
Time to test complete Integration
Let's run our application using sbt clean run
and visit http://localhost:9000
. The Home page will look like this.
Home Page
LoginRadius Auth Page (IDX)
Now click on the protected url by OIDC
link on the home page that will redirect you to the LoginRadius Auth Page (IDX), which you configured.
Redirect to Protected Index Page
On successful login from the above step, you will be redirected to the protectedindex.scala.html
page.
Logout Action
play-pac4j provides out of the box LogoutController
which can be used to handle logout flows. It has logout
functionality which has the logout logic implementation to clear the session. Below is the sample logout
functionality from org.pac4j.play.LogoutController
play-pac4j module.
1public CompletionStage<Result> logout(final Http.Request request) {
2 final HttpActionAdapter bestAdapter = FindBest.httpActionAdapter(null, config, PlayHttpActionAdapter.INSTANCE);
3 final LogoutLogic bestLogic = FindBest.logoutLogic(logoutLogic, config, DefaultLogoutLogic.INSTANCE);
4
5 final WebContext context = FindBest.webContextFactory(null, config, PlayContextFactory.INSTANCE).newContext(request);
6
7 return CompletableFuture.supplyAsync(() -> (Result) bestLogic.perform(context, sessionStore, config, bestAdapter, this.defaultUrl,
8 this.logoutUrlPattern, this.localLogout, this.destroySession, this.centralLogout), ec.current());
9}
So to use this LogoutController, we just need to initialize it and define logout route for the same in our routes
. We already initialized LoginController in SecurityModule.java
.
a. Initialize LogoutController
- You can configure default url which application needs to redirect after
logout
action.
1// logout
2 final LogoutController logoutController = new LogoutController();
3 logoutController.setDefaultUrl("/?defaulturlafterlogout");
4 bind(LogoutController.class).toInstance(logoutController);
b. Define Route for LogoutController
- We already configured
/logout
Route usingLogoutController
inconf/routes
.
1GET /logout @org.pac4j.play.LogoutController.logout(request: Request)
On Clicking logout
in our application, the session will be cleared and you will be redirected to the Home page based on our configuration.
Source Code
You can see the full source code for the application developed in this tutorial on GitHub.

Featured Posts
How to Implement JWT Authentication for CRUD APIs in Deno
Multi-Factor Authentication (MFA) with Redis Cache and OTP
Introduction to SolidJS
Build a Modern Login/Signup Form with Tailwind CSS and React
Implement HTTP Streaming with Node.js and Fetch API
NestJS: How to Implement Session-Based User Authentication
NestJS User Authentication with LoginRadius API
How to Authenticate Svelte Apps
Flutter Authentication: Implementing User Signup and Login
How to Secure Your LoopBack REST API with JWT Authentication
Node.js User Authentication Guide
Your Ultimate Guide to Next.js Authentication
Local Storage vs. Session Storage vs. Cookies
How to Secure a PHP API Using JWT
Using JWT Flask JWT Authentication- A Quick Guide
Build Your First Smart Contract with Ethereum & Solidity
What are JWT, JWS, JWE, JWK, and JWA?
How to Build an OpenCV Web App with Streamlit
32 React Best Practices That Every Programmer Should Follow
How to Build a Progressive Web App (PWA) with React
Bootstrap 4 vs. Bootstrap 5: What is the Difference?
JWT Authentication — Best Practices and When to Use
What Are Refresh Tokens? When & How to Use Them
How to Upgrade Your Vim Skills
How to Implement Role-Based Authentication with React Apps
How to Authenticate Users: JWT vs. Session
How to Use Azure Key Vault With an Azure Web App in C#
How to Implement Registration and Authentication in Django?
11 Tips for Managing Remote Software Engineering Teams
Implementing User Authentication in a Python Application
Add Authentication to Play Framework With OIDC and LoginRadius