Securing Angular + Spring Boot Application with Okta

Raghavendra Bhat
9 min readDec 7, 2019

--

As we start developing enterprise web applications, we all know at some point of time we need to make sure our web applications are secured enough and allows only authorized access to the resources of our application. Authorized access involve identifying access control roles which user/group can assume or assigned with to perform operations within our application.

Okta is an Identity management and Access management based cloud service. According to Okta’s documentation, It’s an enterprise-grade, identity management service, built for the cloud, but compatible with many on-premises applications. With Okta, IT can manage any employee’s access to any application or device. Okta runs in the cloud, on a secure, reliable, extensively audited platform, which integrates deeply with on-premises applications, directories, and identity management systems. To know more about how Okta integrates applications refer this link.

Okta’s authorization uses OAuth2 protocol. To know more about OAuth2 and Open ID connect (OIDC) refer Okta’s blog about OAuth2.

In this article we will be looking at securing Contacts demo application which allows users to list, create/edit and delete contact. This is the part 2 of the article about securing Contacts App via Okta. Please refer to part 1 of the article (Building web Application with Spring Boot and Angular) on details about the base contacts Application.

You can checkout the complete code of this article from Github repository.

At the end we will have an application with following architecture.

Contacts App component Architecture with OAuth

To containerize this application and deploy it to Kubernetes cluster, you can refer one of the below links.

Kubernetes: Deploying Angular + Spring Boot JAVA application in Google Kubernetes Engine (GKE)

Kubernetes : Deploying Angular + Spring Boot Application in Microsoft Azure Cloud

Configurations in Okta dev portal

To secure our base Application, first we need to set up Okta account and an Application in Okta. You can create a free developer account here.

Once the account is created, we can add an application in Okta dashboard. Under Applications tab click on add Application.

Select Single Page App as a platform and fill the configuration as provided in below screenshot. Here we are specifying only Authorization code as we don't want the token to be returned directly in the URL which can pose security risks as per this Okta blog.

We have two login redirects in our case, one for when user logs in through our UI where Okta Authorization server has to redirect to with authorization code and other is for when user directly invoking our backend exposed APIs. We are selecting PKCE here.

OAuth offers various flows of authorization and choosing the right OAuth flow depends on the use case. You can refer the details about which OAuth flow to choose from this medium blog or from Okta documentation. In our demo application as we are building Angular SPA UI, we are choosing OAuth implicit flow with PKCE( Proof Key Code Exchange).

Authorization server to Authorize requests

We need an authorization server which will authorize all the incoming requests to our applications. In our example we are using default authorization server provided by Okta, if you want to write your own authorization server on authorizing requests, you can do that and you have to link your authorization server end point in the Okta.

You can view the authorization server in Okta portal under below link

Adding group claims to our Contacts application

In our contact application, we are creating one group “Admin” to secure create/edit/delete access to admin user for demonstration purpose.

To create a group and have this available as a part of the access token you can refer this Okta blog. The official Okta blog refers stand alone Spring Boot App set up as Okta web Project and in our case we don’t need to change the type of project, we just have to create groups and enable these group claims as a part of the access token issued by authorization server.

Below screenshot shows creating group and assigning to our contacts application and enabling this group claim in the Access token.

Group management
Group/User assignment for the App
Adding a groups claim to access token issued by Authorization Server

Once we have set up our project in Okta dashboard as per above steps, we can start integrating it in to our Contacts Application to secure application access.

Okta Configurations on contacts front end

To secure Contacts front end, we are going to use Okta’z Angular SDK library to sign in the user by redirecting to the authorization endpoint on our Okta org. You can install this library via npm:

npm install @okta/okta-angular --save

After the installation, you will need the values from the OIDC client that you created in the previous step to instantiate the middleware. You will also need to know your Okta org URL, which you can see on the home page of the Okta Developer console.

In app.module.ts create a config object with PKCE set to true.

const oktaConfig = {issuer: 'https://{yourOktaDomain}/oauth2/default',redirectUri: window.location.origin + '/implicit/callback',clientId: 'your clientId',pkce: true};

and add it under the provider section. Also import OktaAuthModule

import { OktaAuthModule, OktaCallbackComponent } from '@okta/okta-angular';import {OKTA_CONFIG} from '@okta/okta-angular';
....
imports:[
...
OktaAuthModule
...
]
providers: [...{ provide: OKTA_CONFIG, useValue: oktaConfig }
...
]

In the app-routing module, guard the route with the Angular route guard. Here is the snippet of app-routing-module.ts

import { OktaCallbackComponent } from '@okta/okta-angular';
...
const routes: Routes = [
{ path: 'contact-list',canActivate: [ OktaAuthGuard ],component: ContactListComponent },{ path: 'contact',canActivate: [ OktaAuthGuard, AdminGuard ],component: EditContactComponent },{path: 'implicit/callback',component: OktaCallbackComponent},{ path: '',redirectTo: 'contact-list',pathMatch: 'full'},];

Here we are guarding our contact listing with the AuthGuard and any Create, update,delete of contact is guarded with AdminGuard and OktaAuthGurard route guards.

Inside Okta Auth guard’s canActivate method, we verify if user is authenticated or not, if not, redirect to Okta login page else allow user to view the route

async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {this.authenticated = await this.okta.isAuthenticated();if (this.authenticated) { return true; }// Redirect to login flow.this.oktaAuth.loginRedirect();return false;}

Similarly, AdminGuard’s canActivate method checks whether logged in users group claim is ‘Admin’ or not if not it declines to show the route with the alert.

async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {this.user = await this.okta.getUser();const result = this.user.groups.find((group) => group === 'Admin');if (result) {return true;} else {alert('User is not authorized to perform this operation');return false; }}

Below is the sequence of interactions happening when the user tries to view the route /contact-list to list contacts

Sequence Diagram of Contacts listing

Below is the sequence diagram to understand how PKCE works. In case of SPA app, if the access token is returned directly from the redirect URL, any browser extension can access this token before our application can see it and can pose potential security threats. PKCE mitigates this by generating SHA256 hash of random code verifier string and includes it as a code challenge as a part of the access code request. When access token exchange request is made for the access code, authorization server can re generate SHA256 hash from the code verifier and verify against store the code challenge.

The angular library does all these things of SHA256 conversion and exchange tokens for access code with code verifier and code challenge for us under the hood.

Contacts App Implicit grant flow with PKCE

Okta Configurations on contacts back end

For Spring Boot support of Okta, we need to install following dependencies

<dependency><groupId>com.okta.spring</groupId><artifactId>okta-spring-boot-starter</artifactId><version>1.2.1</version><exclusions><exclusion><groupId>org.springframework.security.oauth</groupId><artifactId>spring-security-oauth2</artifactId></exclusion></exclusions></dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId><artifactId>spring-security-oauth2</artifactId><version>2.4.0.RELEASE</version></dependency>

With Spring Security OAuth2 and okta starter in class path, Spring configures Okta support during its start up.

Here we are providing below configuration values for Spring to set up and connect with our Okta App

okta.oauth2.client-id=<clientId>okta.oauth2.issuer=https://{yourOktaDomain}/oauth2/defaultokta.oauth2.groups-claim=groups

In SpringSecurity Class, we mention about our springboot App is the resource server

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Profile("dev")
@Slf4j
public class SecurityDevConfiguration extends WebSecurityConfigurerAdapter {
@Overrideprotected void configure(HttpSecurity http) throws Exception{http.cors().and().csrf().disable();http.cors().configurationSource(request -> new CorsConfiguration(corsConfiguratione()));// http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll()// .and().http.antMatcher("/**").authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll().antMatchers("/").permitAll().anyRequest().authenticated();http.oauth2ResourceServer();}

With this configuration our application end points are protected. If you try to access the contacts end point at http://localhost:8080/api/contacts it returns 401 as it doesn’t pass through authentication filter checks.

To enable method level security to secure create/update & delete to only admin user we first have to enable method level security in SpringSecurityConfig class with following annotation

@EnableGlobalMethodSecurity(prePostEnabled = true)

And the end points we want to protect we can use PreAuthorize role checks like below

@PreAuthorize("hasAuthority('Admin')")public Contact saveContact(@RequestBody Contact contact,@AuthenticationPrincipal Principal userInfo) {

To get logged in user details we can use @AuthenticationPrincipal Principal object.

Securing API access from front end App with Access Token

Now we have both front end back end is protected with Okta, if our front end needs to make API requests it has to pass the access token in the authorization header. In angular it can be done using HttpInterceptor.In Auth Interceptor class we can set access token for every http request like below

private async handleAccess(request: HttpRequest<any>, next: HttpHandler): Promise<HttpEvent<any>> {const accessToken = await this.oktaAuth.getAccessToken();if ( accessToken) {request = request.clone({setHeaders: {Authorization: 'Bearer ' + accessToken}});}
return next.handle(request).toPromise();
}}

Contacts direct API access via external API server

With all above configurations done correctly, our UI can successfully access backend APIs. In many enterprise applications, where we have several micro services accessed by our application UI and also few micro services which are exposed directly to other apps or users. Since our application is set as SPA it currently supports only by javascript code access. To expose our micro services as APIs to other API consumers, we have to create an Okta backend App as a web and then allow single sign on to our existing micro services created as SPA as technically these are 2 separate applications in Okta.

To work around with this problem, I have created a small solution where we still use OAuth’s implicit code flow with PKCE but this time rather than redirecting user to login screen (API requests cannot login to Okta manually as our user in this case will be a API user), we follow below sequence of steps.

Direct Backend API access sequence

Here I have created a backend API server, a wrapper micro service which groups the list of APIs which we want to expose as back end APIs.

This wrapper issues access token with login end point and then make subsequent API calls with the access token.

This wrapper micro service acts as a proxy to our underlying contacts backend API and exposes desired services outside. It uses Spring Cloud Open Feign library for writing declarative Rest clients. It makes writing web service clients super easier. It simplifies amount of code to be written to consume a Restful web service. It also has interceptor support to any middleware activities before making API requests. In this case we will be using the interceptor to set the access token to propagate user access.You can refer more about Feign here.

With all above configurations done properly, All the end points of our application + Front end components are secured and only the authorized users can access our application. To Containerize this application and deploy to Kubernetes cluster please refer one of the below articles.

Kubernetes: Deploying Angular + Spring Boot JAVA application in Google Kubernetes Engine (GKE)

Kubernetes : Deploying Angular + Spring Boot Application in Microsoft Azure Cloud

Raghavendra Bhatt
Technical Architect, Capgemini USA
LinkedIn

--

--