Serving Angular App from Spring Boot Server for Secure ๐Ÿ” OAuth Flow

Serving Angular App from Spring Boot Server for Secure ๐Ÿ” OAuth Flow

Learn to use OAuth on Spring Boot Server and to serve Angular app post successful authentication

ยท

7 min read

In the first part of the series, we built an angular app that uses OAuth2 for user authentication. In the fourth part, we discussed some of the limitations of this approach. In this article, we will create a spring boot server that will authenticate the users using OAuth flow and then will serve the angular application. That way we don't need to store any credentials or access tokens in the browser. Thus, this is a more secure method for OAuth flow. Some familiarity with Spring Boot and Angular is required to follow along with this article. If you are using Windows Operating System, then you should use any shell that allows Unix commands inside Windows (Git Bash for example). Now, let's start coding ๐Ÿ‘จโ€๐Ÿ’ป

Serve Angular App using Spring Boot

First, let's try to serve an angular application using the spring boot server. After that, we will add security to it. The following steps are mentioned here by Dave Syer.

Create a Spring Boot Application

First of all, create a spring boot application with web dependency. If your IDE has integration to create it, use that or use the following command:

curl start.spring.io/starter.tgz -d dependencies=web | tar -zxvf -
./mvnw install

Now, we will install npm locally in the project in order to install Angular CLI.

Install NPM Locally

Frontend Maven Plugin lets you install Node / NPM locally for your project, install dependencies with NPM, and much more. Add this plugin to the project and specify the related commands in the pom.xml file.

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <!-- Frontend Maven Plugin -->
            <plugin>
                <groupId>com.github.eirslett</groupId>
                <artifactId>frontend-maven-plugin</artifactId>
                <version>1.6</version>
                <configuration>
                    <!-- Node Version to use -->
                    <nodeVersion>v14.17.6</nodeVersion>
                </configuration>
                <executions>
                    <!-- Command to install node and npm locally in the project -->
                    <execution>
                        <id>install-npm</id>
                        <goals>
                            <goal>install-node-and-npm</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

And then run the following command to apply the changes.

./mvnw generate-resources

For the first time, it will take some time. But later on, this step won't take much time.

Install Angular CLI

We use Angular CLI to create Angular applications which can be installed using npm which we installed in the previous step. Create a file called npm with the following content:

#!/bin/sh
cd $(dirname $0)
PATH="$PWD/node/":$PATH
node "node/node_modules/npm/bin/npm-cli.js" "$@"

And then, run the following command to make the file executable:

chmod +x npm

This wrapper allows us to use local node and npm for the app instead of the ones which are installed on the system. Then, run the following command to install the Angular CLI:

./npm install @angular/cli

Now, create similar wrappers for Angular CLI as well. Create a file called ng with the following content:

#!/bin/sh
cd $(dirname $0)
PATH="$PWD/node/":"$PWD":$PATH
node_modules/@angular/cli/bin/ng.js "$@"

Then, run the following command to make it executable:

chmod +x ng

To test the wrapper, run the following commands:

./ng --version

It prints versions of Node and Angular CLI.

Create an Angular App

We can either create a new angular app or can pull any existing app. Let's create a new one. We use Angular CLI to create an angular app. Run the following command (inside the spring boot project):

./ng new spa

This creates a new angular app inside the spring boot project. Follow these steps to move this app to the root:

  1. Copy the content of spa/.gitignore file and paste it at the bottom of .gitignore file.
    cat spa/.gitignore >> .gitignore
    
  2. Delete spa/node_modules, spa/src/favicon.ico, spa/.git and spa/.gitignore.
    rm -rf spa/node* spa/src/favicon.ico spa/.gitignore spa/.git
    
  3. Copy everything from spa to root and delete spa.
    cp -rf spa/* .
    cp spa/.??* .
    rm -rf spa
    
  4. Specify target/classes/static as the output location of ng build command in angula.json file.
    sed -i -e 's,dist/spa,target/classes/static,' angular.json
    

    Building the Project

    Add the following execution command in pom.xml to install the required packages:
    <execution>
     <id>npm-install</id>
     <goals>
         <goal>npm</goal>
     </goals>
    </execution>
    
    Run ./mvnw generate-resources to install the required packages. Now add the following execution as well to compile the angular app during maven build.
     <execution>
         <id>npm-build</id>
         <goals>
             <goal>npm</goal>
         </goals>
         <configuration>
             <arguments>run-script build</arguments>
         </configuration>
     </execution>
    
    To stabilize the build, put a ^ before the version of @angular/cli in package.json file. To build continuously, run ./ng build --watch command in a separate terminal window and the angular app will be built whenever any related file changes. We want to run this server on port 8081. So, add the following line in application.properties file:
    server.port=8081
    
    Run the Spring Boot app using IDE or ./mvnw spring-boot:run command. Then, visit http://localhost:8081 in a browser. You should see the Angular starting app.

Explanation

In angular.json file we have specified target/classes/static as the output path of ng build. We have also defined to run ng build command whenever the spring boot app starts using Frontend Maven Plugin. Now, whenever the server starts, it builds the angular app and puts all the files in target/classes/static directory, which is the default directory to be served by the spring application. Now, whenever the user tries to access the spring boot app, index.html file from target/classes/static is served. Let's modify the app.component.html file of the angular app and replace its content with the following code:

<h3>{{title}}</h3>
<div>
  Welcome to {{title}}
</div>

Since ./ng build --watch command is running. Angular CLI detects the changes in html file and rebuilds the angular app. Now, refresh the browser page and we see the updated page. That's how it works. Now, we are ready to add security ๐Ÿ” to the app.

Secure the app using OAuth2

Now, let's make our app an OAuth2 client using spring-boot-starter-oauth2-client dependency.

Adding the dependency

Add the spring-boot-starter-oauth2-client dependency to the spring boot app by inserting the following lines in the pom.xml file:

    <dependencies>
        ...other dependencies

        <!-- To create OAuth2 Client -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-client</artifactId>
        </dependency>

        ... other dependencies
    </dependencies>

Now, our app is an OAuth2 client. We just need to configure it.

Creating app in Identity Provider

In the first part of this series, we created an OAuth2 client in Authorization Server / Identity Provider. We chose Single Page Application and that is the reason Okta did not provide client secret for the app. But, now we are serving the app from a spring boot backend. And we can securely store credentials and access token in the backend. So, we need to create another app because we need client secrets this time. If you don't know how to create an app in Identity Provider, then read the first part here. Create a new app in Identity Provider, but this time chose Web Application as Application Type not Single Page Application since authentication and tokens will be managed on the server, not in the browser. Use http://localhost:8081/login/oauth2/code/{IdentityProviderId} as Sign-in Redirect URI and http://localhost:8081 as Sign-out Redirect URI while creating the app. Here IdentityProviderId is the unique id of the identity provider with respect to our Spring Boot App. For example, you can use okta for Okta, keycloak for Keycloak, etc. At this point, you should have client-id, client-secret, and issuer-uri of the app. In case of Okta, the issuer-uri is https://{OKTA_DOMAIN}/oauth2/default.

Configuring Spring Boot App

Add the following configurations to the application.properties file of the app:

spring.security.oauth2.client.registration.{IdentityProviderId}.client-id={ClientId}
spring.security.oauth2.client.registration.{IdentityProviderId}.client-secret={ClientSecret}
spring.security.oauth2.client.registration.{IdentityProviderId}.scope=openid,profile,email
spring.security.oauth2.client.registration.{IdentityProviderId}.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.{IdentityProviderId}.redirect-uri={RedirectURI}
spring.security.oauth2.client.provider.{IdentityProviderId}.issuer-uri={IssuerURI}

Restart the app and visit http://localhost:8081 in a private window of the browser. You are asked to Sign in to the app. Enter the username and password of any user which you created in Identity Provider (Please do so, if you haven't done already). After successful login, we can see our angular app.

Summary

In this article, we

โœ… created a spring boot server, which serves an angular app.

โœ… added OAuth2 authentication to the spring boot server to secure the angular app.

Source code can be found here (oauth2clientserver directory). In the next article, I will explain how to use this Spring Boot Server as a proxy while making API requests to the Resource Server. Stay tuned for the next part. Thanks for reading. Keep learning...

ย