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.
profile
Vishnu ChilamakuruFirst published: 2021-06-25Last updated: 2025-06-25
guest-post,add-authentication-to-play-framework-with-oidc-and-loginradius
Table of Contents

What is OpenID Connect (OIDC) Protocol?

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.

play project homepage

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.

loginradius dashboard

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).

loginradius upgrade subscription 1

loginradius upgrade subscription 2

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

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.

loginradius consent page

Redirect to Protected Index Page

On successful login from the above step, you will be redirected to the protectedindex.scala.html page.

loginradius protected index 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 using LogoutController in conf/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.

Vishnu Chilamakuru
By Vishnu ChilamakuruTechnology and startup enthusiastic. Learning to lead optimised life. I write about system design, databases, microservices, backend development, developer tools, life sciences & books I read
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

Share On:
Share on TwitterShare on LinkedIn