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
195 changes: 195 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,196 @@
# Auth-API-Gateway

A modern reactive Spring Boot API Gateway that provides JWT-based authentication and authorization for microservices. Built with Spring Cloud Gateway and WebFlux for high-performance, non-blocking operations.

## Key Features

- **JWT Authentication** - Stateless authentication with access and refresh tokens
- **User Management** - Registration, login, and profile management
- **Reactive Architecture** - Built on Spring WebFlux for non-blocking I/O
- **Global Exception Handling** - Structured error responses across all endpoints
- **Secure Password Storage** - BCrypt hashing with 12 rounds
- **Token Refresh Mechanism** - 7-day refresh tokens for seamless user experience

## Tech Stack

- **Java 21** - LTS version
- **Spring Boot 3.5.7** - Framework
- **Spring Cloud Gateway** - Reactive gateway
- **Spring Security** - Authentication & authorization
- **PostgreSQL + R2DBC** - Reactive database access
- **JJWT 0.12.5** - JWT token handling
- **Maven** - Build tool

## Quick Start

1. **Clone the repository**
```bash
git clone https://github.com/matthewhou19/auth-api-gateway.git
cd auth-api-gateway
```

2. **Start PostgreSQL database**

**Option A: Using Docker (Recommended)**
```bash
docker compose up -d
```

**Option B: Use existing PostgreSQL**

Make sure PostgreSQL is running on `localhost:5432` with:
- Database: `auth_gateway_db`
- Username: `postgres`
- Password: `password`

3. **Run the application**
```bash
cd apigateway
./mvnw spring-boot:run
```

**Note:** Database tables (`users`, `refresh_tokens`) are created automatically on startup from `schema.sql`. No manual database setup required!
Comment thread
matthewhou19 marked this conversation as resolved.

4. **Access the API**
- Base URL: `http://localhost:8080`
- All endpoints are prefixed with `/auth`

## API Endpoints

### Public Endpoints (No authentication required)

| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/auth/register` | Register a new user |
| POST | `/auth/login` | Login with email/username and password |
| GET | `/auth/refresh/{token}` | Refresh access token using refresh token |

Comment thread
matthewhou19 marked this conversation as resolved.
### Protected Endpoints (Requires JWT)

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/auth/me` | Get authenticated user details |
| DELETE | `/auth/logout` | Logout and invalidate refresh token |

### Example: Register a User

```bash
curl -X POST http://localhost:8080/auth/register \
-H "Content-Type: application/json" \
-d "{\"email\": \"user@example.com\", \"username\": \"johndoe\", \"name\": \"John Doe\", \"password\": \"SecurePass123@\"}"
```

**Note**: Using double quotes and escaped JSON for better shell compatibility. Password uses `@` instead of `!` to avoid shell history expansion issues in some terminals.

### Example: Login

```bash
curl -X POST http://localhost:8080/auth/login \
-H "Content-Type: application/json" \
-d "{\"emailUsername\": \"user@example.com\", \"password\": \"SecurePass123@\"}"
```

Response:
```json
{
"accessToken": "eyJhbGciOiJIUzI1NiJ9...",
"refreshToken": "base64-encoded-token"
}
```

### Example: Access Protected Endpoint

```bash
curl -X GET http://localhost:8080/auth/me \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

## Configuration

### Default Settings

The application uses the following default database settings (matching the `docker-compose.yml`):

```yaml
spring:
r2dbc:
url: r2dbc:postgresql://localhost:5432/auth_gateway_db
username: postgres
password: password
```

### Custom Configuration

For production or custom setups, modify `apigateway/src/main/resources/application.yaml`:

**Custom Database:**
```yaml
spring:
r2dbc:
url: r2dbc:postgresql://your-host:5432/your-database
username: your-username
password: your-password
```

**JWT Secret (CRITICAL for Production!):**
```yaml
jwt:
secret: your-secure-secret-key-minimum-32-characters-long
```

⚠️ **Security Warning**: The default JWT secret is for development only. MUST be changed for production!

## Database Schema

The application automatically creates the required database tables on startup:

**Tables:**
- `users` - Stores user account information (id, name, username, email, password)
- `refresh_tokens` - Manages refresh token lifecycle (id, user_id, token, expiry_date, created_at)

**Schema Definition:**
- Table definitions are in `apigateway/src/main/resources/schema.sql`
- Tables are created automatically via Spring's SQL initialization feature
- Uses `CREATE TABLE IF NOT EXISTS` to prevent errors on restart
- Includes indexes on `refresh_tokens` for optimal performance

**Configuration:**
```yaml
spring:
sql:
init:
mode: never # Use 'always' only in dev/test profiles; handle prod schema via migrations
platform: postgresql
```

**Note:** For production environments, use proper database migration tools like Flyway or Liquibase instead of SQL initialization mode.
Comment thread
matthewhou19 marked this conversation as resolved.

## Password Requirements

- Minimum 8 characters
- At least one uppercase letter
- At least one lowercase letter
- At least one digit
- At least one special character

## Project Structure

```
apigateway/
├── src/main/java/vaultweb/apigateway/
│ ├── config/ # Security & JWT configuration
│ ├── controller/ # REST API endpoints
│ ├── service/ # Business logic
│ ├── model/ # Database entities
│ ├── repositories/ # Data access layer
│ ├── dto/ # Request/Response objects
│ ├── exceptions/ # Exception handling
│ └── util/ # Utility classes
└── src/main/resources/
├── application.yaml # Application configuration
└── schema.sql # Database schema (auto-applied on startup)
```

## License

[Add your license here]
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ public Mono<UserDetails> register(@Valid @RequestBody UserRegistrationRequest re
}

@ResponseStatus(HttpStatus.OK)
@GetMapping("/switch-jwt/{token}")
public Mono<AuthResponse> switchJwtToken(@PathVariable @Valid @NotEmpty String token) {
@GetMapping("/refresh/{token}")
public Mono<AuthResponse> refreshToken(@PathVariable @Valid @NotEmpty String token) {
return authService.switchToken(token);
}

Expand Down
8 changes: 6 additions & 2 deletions apigateway/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ spring:
application:
name: apigateway
r2dbc:
url: r2dbc:postgresql://localhost:5432/mydatabase
url: r2dbc:postgresql://localhost:5432/auth_gateway_db
username: postgres
password: password
sql:
init:
mode: never # Use 'always' only in dev/test profiles; handle prod schema via migrations
platform: postgresql
Comment thread
DenizAltunkapan marked this conversation as resolved.

jwt:
secret: b9f4f7c2-6d8a-4a41-9e3a-99c42e67d9ab-53f1fa3434aa12af9df7e98e894e-change-me

auth:
publicUrls: /auth/login,/auth/register,/auth/reset-password,/auth/switch-jwt/**
publicUrls: /auth/login,/auth/register,/auth/reset-password,/auth/refresh/**
23 changes: 23 additions & 0 deletions apigateway/src/main/resources/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-- Users table
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
username VARCHAR(255) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL
);

-- Refresh tokens table
CREATE TABLE IF NOT EXISTS refresh_tokens (
id BIGSERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token VARCHAR(500) NOT NULL,
expiry_date TIMESTAMP NOT NULL,
created_at TIMESTAMP NOT NULL
);

-- Create index on user_id for better query performance
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_user_id ON refresh_tokens(user_id);

-- Create index on token for faster lookups
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_token ON refresh_tokens(token);
18 changes: 18 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
services:
postgres:
image: postgres:16-alpine
container_name: auth-gateway-db
# NOTE: These credentials are intended for local development only.
# For any shared or production-like environment, override POSTGRES_USER,
# POSTGRES_PASSWORD, and POSTGRES_DB with secure, environment-specific values.
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: auth_gateway_db
Comment thread
matthewhou19 marked this conversation as resolved.
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data

volumes:
postgres_data: