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
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:
- Copy the content of
spa/.gitignore
file and paste it at the bottom of.gitignore
file.cat spa/.gitignore >> .gitignore
- Delete
spa/node_modules
,spa/src/favicon.ico
,spa/.git
andspa/.gitignore
.rm -rf spa/node* spa/src/favicon.ico spa/.gitignore spa/.git
- Copy everything from
spa
to root and deletespa
.cp -rf spa/* . cp spa/.??* . rm -rf spa
- Specify
target/classes/static
as the output location ofng build
command inangula.json
file.sed -i -e 's,dist/spa,target/classes/static,' angular.json
Building the Project
Add the following execution command inpom.xml
to install the required packages:
Run<execution> <id>npm-install</id> <goals> <goal>npm</goal> </goals> </execution>
./mvnw generate-resources
to install the required packages. Now add the following execution as well to compile the angular app during maven build.
To stabilize the build, put a<execution> <id>npm-build</id> <goals> <goal>npm</goal> </goals> <configuration> <arguments>run-script build</arguments> </configuration> </execution>
^
before the version of@angular/cli
inpackage.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 port8081
. So, add the following line inapplication.properties
file:
Run the Spring Boot app using IDE orserver.port=8081
./mvnw spring-boot:run
command. Then, visithttp://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...