Build, Run, and Verify
Compile the application, start the Spring Boot server, authenticate an end user through Microsoft Entra ID, and verify that the data grants correctly enforce fine-grained data access control and privilege elevation.
For this section, you’ll need two separate terminal sessions:
-
Terminal 1 (build and run): Use to compile the project, load environment variables, and start the Spring Boot application. This terminal remains occupied while the application runs.
-
Terminal 2 (test): Use to generate Proof Key for Code Exchange (PKCE) values, obtain Entra ID access tokens, and execute
curlrequests against the running API.
Topics:
Build the Application
Configure your Java and Maven environments, then compile the parent project, the Spring provider module, and the sample API into an executable JAR. Perform these tasks in Terminal 1.
-
Set the active JDK.
Ensure Java 17 is your active JDK (if it isn’t already your system default).
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk export PATH=$JAVA_HOME/bin:$PATH -
Set up Maven.
If Maven is not on your system path, point
MAVEN_HOMEto your installation directory. (Ifmvn -versionalready works, you can skip this step.)export MAVEN_HOME=/opt/maven/apache-maven-3.9.x export PATH=$MAVEN_HOME/bin:$PATH # Verify installation (requires Apache Maven 3.9.x or later) mvn -version -
Build the project.
Build the project components in the following order. Starting from the repository root, compile the parent project, the Spring provider module, and finally the sample application.
# a. Build the parent project cd $HOME/ojdbc-extensions mvn -DskipTests clean install # b. Build the Spring provider module cd $HOME/ojdbc-extensions/ojdbc-provider-spring mvn -DskipTests clean install # c. Build the sample application cd APP_DIR mvn -DskipTests clean package
Note: A successful build ends with BUILD SUCCESS. The executable JAR is located at target/employee-records-api-0.0.1-SNAPSHOT.jar.
Run the Application
Continuing in Terminal 1, source your environment variables and launch the Spring Boot application.
-
Load environment variables.
Ensure that you are in the application directory, then source your
.envfile.cd APP_DIR set -a && source .env && set +a -
Start the Spring Boot application.
Run the application using the Maven Spring Boot plug-in.
mvn -DskipTests spring-boot:runLeave this terminal running. Wait for the console to display
Started EmployeeRecordsApiApplicationbefore continuing with the next steps.
Note:
If your host requires an HTTP proxy to reach Entra ID, append the following Java Virtual Machine (JVM) arguments to the run command:
mvn -DskipTests spring-boot:run \
-Dspring-boot.run.jvmArguments="\
-Dhttps.proxyHost=<proxy-host> \
-Dhttps.proxyPort=<port> \
-Dhttp.proxyHost=<proxy-host> \
-Dhttp.proxyPort=<port> \
-Dhttp.nonProxyHosts=localhost"
Get an Access Token
Switching to Terminal 2, execute the Authorization Code with PKCE flow to simulate a user login, ultimately exchanging an Entra ID authorization code for a JWT access token.
-
Create the PKCE helper script.
Navigate to the application directory and create a utility script. You can use this script to easily generate fresh PKCE verifier and challenge codes each time you need to fetch an access token.
cd APP_DIR cat > generate_pkce.sh << 'EOF' #!/bin/bash CODE_VERIFIER=$(openssl rand -base64 64 | tr -d '=+/' | tr -d '\n' | cut -c1-64) CODE_CHALLENGE=$(echo -n "$CODE_VERIFIER" | openssl dgst -sha256 -binary | base64 | tr '+/' '-_' | tr -d '=') # Update .env file in place sed -i "s|CODE_VERIFIER=.*|CODE_VERIFIER='$CODE_VERIFIER'|" .env sed -i "s|CODE_CHALLENGE=.*|CODE_CHALLENGE='$CODE_CHALLENGE'|" .env echo "Generated and updated .env:" echo "CODE_VERIFIER=$CODE_VERIFIER" echo "CODE_CHALLENGE=$CODE_CHALLENGE" EOF chmod +x generate_pkce.sh -
Run the script and load variables.
Execute the script to update your
.envfile with verifier and challenge codes, then source the file to load the new variables into your terminal session../generate_pkce.sh set -a && source .env && set +a -
Generate the authorization URL.
Construct the Entra ID login URL and print it to the console.
AUTH_URL="${HRAPP_AUTH_URI}?client_id=${HRAPP_CLIENT_ID}" AUTH_URL+="&response_type=code&redirect_uri=${REDIRECT_URI}" AUTH_URL+="&scope=${HRAPP_DELEGATED_SCOPE}" AUTH_URL+="&response_mode=query&prompt=consent" AUTH_URL+="&code_challenge=${CODE_CHALLENGE}&code_challenge_method=S256" echo -e "\nOpen this URL in your browser:\n$AUTH_URL\n" -
Authenticate in the browser.
-
Copy the generated URL and open it in a web browser.
-
Sign in as your test user (Emma).
-
After authenticating, your browser redirects to
http://localhost:3000?code=<AUTH_CODE>. It is normal for this page to fail to load. -
From the URL bar, copy the authorization code (everything after
code=and before the next&).
-
-
Exchange the code for an access token.
Use
curlto swap the authorization code for a JWT access token. In the command below, replace<AUTH_CODE_FROM_REDIRECT>with the code you copied.curl -s -X POST "$HRAPP_TOKEN_URI" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=authorization_code" \ --data-urlencode "client_id=$HRAPP_CLIENT_ID" \ --data-urlencode "client_secret=$HRAPP_CLIENT_SECRET" \ --data-urlencode "code=<AUTH_CODE_FROM_REDIRECT>" \ --data-urlencode "redirect_uri=$REDIRECT_URI" \ --data-urlencode "code_verifier=$CODE_VERIFIER" \ --data-urlencode "scope=$HRAPP_DELEGATED_SCOPE"In the JSON response, find the
access_tokenvalue and save it.
Verify Employee Access
In Terminal 2, call the /api/employees endpoint using Emma’s access token you obtained previously.
curl -s --http1.1 -X GET "http://localhost:8080/api/employees" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Accept: application/json" | python3 -m json.tool
Emma’s access token carries EMPLOYEE in its roles claim, which activates the EMPLOYEE_ROLE data role in the database. The EMPLOYEES_OWN_RECORD data grant on this data role ensures only Emma’s record is returned.
[
{
"id": 400,
"name": "Emma Baker",
"salary": 8200,
"phone": "555-0400"
}
]
Verify Privilege Elevation
Now, use the same access token and call the /api/employees/salary-summary endpoint. This demonstrates how the application temporarily elevates Emma’s database privileges, allowing her to view aggregate salary statistics without exposing individual employee salaries.
curl -s --http1.1 -X GET "http://localhost:8080/api/employees/salary-summary" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Accept: application/json" | python3 -m json.tool
Emma’s token still carries only the EMPLOYEE role claim, but for this endpoint the application temporarily activates the COMPENSATION_ANALYST data role. The corresponding data grant (EMPLOYEES_SALARY_SUMMARY) allows access to aggregate salary data across all employees, without exposing individual records:
{
"minSalary": 6900,
"maxSalary": 13000,
"averageSalary": 9826.00,
"employeeCount": 5
}
What happens behind the scenes
-
The application invokes the
getSalarySummary()method, which is tagged with the@RunWithDataRoles(dataRoles = {"COMPENSATION_ANALYST"})annotation. -
A Spring AOP interceptor (
RunWithDataRolesAopConfig.java) catches this request and temporarily adds thecompensation_analystdata role to Emma’s end-user security context. -
The database activates the
hr.employees_salary_summarydata grant, permitting the aggregate salary query. -
Emma receives the minimum salary, maximum salary, average salary, and employee count of their team; however, individual salaries remain hidden.
-
After the method returns, the privilege elevation is immediately deactivated. Emma’s security context reverts to their standard
EMPLOYEE_ROLEdata role.
This temporary privilege elevation is scoped strictly to the execution of that specific method, controlled by the application, and securely enforced at the database level.