Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
/.idea/
/.micronaut/
/build/
AuthGenHash/bin
3 changes: 3 additions & 0 deletions AuthGenHash/.sdkmanrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=21.0.4-tem
10 changes: 6 additions & 4 deletions AuthGenHash/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
plugins {
id("com.github.johnrengelman.shadow") version "8.1.1"
id("io.micronaut.application") version "4.2.1"
id("com.gradleup.shadow") version "${shadowVersion}"
id("io.micronaut.application") version "${micronautPluginVersion}"
id("io.micronaut.test-resources") version "${micronautPluginVersion}"
id("io.micronaut.aot") version "${micronautPluginVersion}"
}

version = "0.1"
Expand All @@ -27,8 +29,8 @@ application {
mainClass.set("io.unityfoundation.auth.AuthGenHashCommand")
}
java {
sourceCompatibility = JavaVersion.toVersion("17")
targetCompatibility = JavaVersion.toVersion("17")
sourceCompatibility = JavaVersion.toVersion("21")
targetCompatibility = JavaVersion.toVersion("21")
}


Expand Down
7 changes: 6 additions & 1 deletion AuthGenHash/gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
micronautVersion=4.2.4
# Java
javaVersion=21
# Micronaut
micronautVersion=4.8.2
micronautPluginVersion=4.5.3
shadowVersion=8.3.6
Binary file modified AuthGenHash/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion AuthGenHash/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
8 changes: 5 additions & 3 deletions AuthGenHash/gradlew
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#

##############################################################################
#
Expand Down Expand Up @@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
Expand Down Expand Up @@ -84,7 +86,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
Expand Down Expand Up @@ -203,7 +205,7 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
Expand Down
22 changes: 12 additions & 10 deletions AuthGenHash/gradlew.bat
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem

@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
Expand Down Expand Up @@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2

goto fail

Expand All @@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto execute

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2

goto fail

Expand Down
4 changes: 2 additions & 2 deletions FrontendDockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Build the frontend and serve with nginx
FROM node:18
FROM node:24 AS builder

COPY frontend frontend
WORKDIR frontend
RUN npm install && npm run build

FROM nginx:1.24.0-alpine

COPY --from=0 /frontend/build /usr/share/nginx/html
COPY --from=builder /frontend/build /usr/share/nginx/html
COPY nginx-default.conf /etc/nginx/conf.d/default.conf
11 changes: 11 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
compose_api:
docker network create unity-network > /dev/null 2>&1 || true
docker compose -f ./docker-compose.local.yml up unity-auth-db unity-auth-api

compose_ui:
docker network create unity-network > /dev/null 2>&1 || true
docker compose -f ./docker-compose.local.yml up unity-auth-db unity-auth-ui

compose_all:
docker network create unity-network > /dev/null 2>&1 || true
docker compose -f ./docker-compose.local.yml up
191 changes: 191 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
# UnityAuth

UnityAuth is a comprehensive authentication and authorization service built with modern microservices architecture. It provides JWT-based authentication, user management, and a web-based administration interface.

## Local Development QuickStart

To launch and be a pure consumer of the auth service, you can use the docker compose from the root:

If running on mac/linux

```sh
docker network create unity-network > /dev/null 2>&1 || true
```

Or simply using compose.

```sh
docker network create unity-network > /dev/null 2>&1 || true
docker-compose -f docker-compose.local.yml up
```

This will start:

- **UnityAuth API** on port http://localhost:8081
- **UnityAuth UI** on port http://localhost:3001
- **MySQL Database** for data persistence

### Hosts File Updates

For consistent internal-external service name resolution, add these to your `/etc/hosts` file

```txt
127.0.0.1 unity-auth-api
127.0.0.1 unity-auth-ui
127.0.0.1 libre311-api
127.0.0.1 libre311-ui
```

You can log in with these accounts.
**Password for all the following accounts is 'test'**

- **Unity Administrator** `[email protected]`
- **Tenant Administrator** `[email protected]`
- **Libre311 Administrator** `[email protected]`
- **Libre311 Request Manager** `[email protected]`
- **Libre311 Jurisdiction Administrator** `[email protected]`
- **Libre311 Jurisdiction Request Manager** `[email protected]`
- **Stl sub-tenant admin** `[email protected]`

## Project Structure

This repository contains three main subprojects:

### 1. UnityAuth (Main Service)

**Location:** `/UnityAuth/`
**Technology:** Java 21 + Micronaut Framework

The core authentication service that provides:

- JWT token generation and validation
- User authentication and authorization
- RESTful API endpoints for authentication operations
- Database integration with MySQL
- JWK (JSON Web Key) management for token signing
- Flyway database migrations

**Key Features:**

- Micronaut-based microservice architecture
- JWT security with configurable key rotation
- BCrypt password hashing
- Database connection pooling with HikariCP
- Reactive programming support with Reactor

### 2. AuthGenHash (Utility Tool)

**Location:** `/AuthGenHash/`
**Technology:** Java 17 + Micronaut + PicoCLI

A command-line utility for generating secure password hashes compatible with the UnityAuth service.

**Purpose:**

- Generate BCrypt password hashes for administrative users
- Secure password handling (interactive mode prevents history logging)
- Standalone tool for initial system setup and user management

**Usage:**

```bash
cd AuthGenHash
./gradlew shadowJar
java -jar build/libs/AuthGenHash-0.1-all.jar -p
```

### 3. Frontend (Web Administration Interface)

**Location:** `/frontend/`
**Technology:** SvelteKit + TypeScript + Tailwind CSS

A modern web application providing administrative interface for the UnityAuth service.

**Features:**

- User authentication and session management
- User administration and management
- Tenant management capabilities
- Settings configuration
- Responsive design with Tailwind CSS
- TypeScript for type safety
- Comprehensive testing with Playwright and Vitest

**Key Technologies:**

- SvelteKit for the web framework
- TypeScript for type safety
- Tailwind CSS for styling
- Playwright for end-to-end testing
- Vitest for unit testing
- ESLint and Prettier for code quality

## Architecture Overview

The system follows a microservices architecture:

1. **Database Layer:** MySQL database for persistent storage
2. **API Layer:** UnityAuth service provides REST APIs
3. **Frontend Layer:** SvelteKit web application
4. **Utility Layer:** AuthGenHash for administrative tasks

## Client Integration

To integrate with the UnityAuth service, add this configuration to your client application's `application.yaml`:

```yaml
security:
enabled: true
token:
enabled: true
jwt:
enabled: true
signatures:
jwks:
unity:
url: ${AUTH_JWKS:`http://localhost:8081/keys`}
```

## Security Configuration

The service uses JSON Web Keys (JWK) for token signing. To generate primary and secondary keys:

1. Visit <https://mkjwk.org/>
2. Generate JSON Web Keys
3. Set environment variables:
- `JWK_PRIMARY`: Primary signing key
- `JWK_SECONDARY`: Secondary signing key for rotation

## Development Environment

### Prerequisites

- Java 17 or higher
- Node.js 18 or higher
- Docker and Docker Compose
- MySQL 8.0 (if running locally)

### Individual Service Development

#### UnityAuth Service

```bash
cd UnityAuth
./gradlew run
```

#### Frontend Development

```bash
cd frontend
npm install
npm run dev
```

#### AuthGenHash Utility

```bash
cd AuthGenHash
./gradlew shadowJar
java -jar build/libs/AuthGenHash-0.1-all.jar -p
```
10 changes: 10 additions & 0 deletions UnityAuth/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
gradle/caches

# Exclude gradle build caches but not wrapper
.gradle/
.DS_Store

build/

docker-compose.*
DockerfileLocal
3 changes: 3 additions & 0 deletions UnityAuth/.sdkmanrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=21.0.4-tem
34 changes: 34 additions & 0 deletions UnityAuth/DockerfileLocal
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Stage 1: Build with Gradle
FROM gradle:jdk21-jammy AS builder
WORKDIR /workspace

# 1. Copy Gradle wrapper first (rarely changes)
COPY gradle/ gradle/
COPY gradlew gradlew.bat ./

# 2. Copy Gradle config files
COPY gradle.properties settings.gradle ./

# 3. Download Gradle
RUN ./gradlew --version --no-daemon

COPY --chown=gradle:gradle . .

# Run the builder w/ the secret
RUN ./gradlew buildLayers --no-daemon

# Stage 2: Create the final image
FROM eclipse-temurin:21-jre-jammy

WORKDIR /home/app

COPY --from=builder /workspace/build/docker/main/layers/libs /home/app/libs
COPY --from=builder /workspace/build/docker/main/layers/app /home/app/
COPY --from=builder /workspace/build/docker/main/layers/resources /home/app/resources

RUN useradd -u 8877 unity
# Change to non-root privilege
USER unity

EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/home/app/application.jar"]
Loading