Why This Isn’t Secure

Why This Isn’t Secure

  • Single secret for everything: If one service or dev leaks the key, all access is compromised.

  • No user or service identity: You can’t differentiate which service made the request (no audit trail).

  • No expiration: Keys never expire unless you rotate manually.

  • Bearer token misuse: Bearer JWTs should be signed tokens carrying claims, not static secrets.


What Should Happen Instead

1. Identity Service Issues JWTs


2. Each Microservice Validates JWT

  • log-service checks:

    • Signature is valid.

    • Audience = logging-service.

    • Issuer = identity-backend.

    • Expiry not passed.

  • No need to share LOG_SERVICE_KEY between services.


3. Rotate & Revoke

  • You can rotate signing keys without redeploying every service.

  • You can revoke tokens for one service/user without affecting others.


4. Service-to-Service Auth Model

  • For internal service calls (careergpt → logging):

    • Service authenticates with identity-backend once.

    • Gets a short-lived JWT (e.g., 15 min).

    • Sends that JWT in Authorization: Bearer.

  • For user-facing requests:

    • User logs in, identity-backend issues JWT.

    • Frontend uses that JWT to call careergpt-backend (and careergpt calls logging).


Where Your Current Setup Fits

  • Right now, LOG_SERVICE_KEY is a stopgap: quick way to lock down endpoints.

  • But in production, it should evolve into:

    • JWT validation (HMAC or RSA).

    • No hardcoded shared secrets between services.

    • Short-lived tokens issued by identity service.


Want to Transition?

We can design this incrementally:

  1. Keep current LOG_SERVICE_KEY for MVP (service-to-service lock).

  2. Add JWT validation to logging service.

  3. Modify identity service to issue JWTs.

  4. Update careergpt-backend to fetch JWTs and pass them.


Do you want me to redesign the logging service now to validate real JWTs (future-proof)?
Or keep static key for MVP and show next-step plan to migrate to proper JWT-based service authentication?

Perfect timing — now that the logging microservice MVP is stable, we can spin up the identity-backend as the next microservice. I’ll outline the setup for cPanel and provide MVP code that issues JWTs for other microservices (like careergpt-backend and logging-service) to validate.


Phase 1: Setup for cPanel

1. Create identity-backend directory

  • Inside your repo or as a separate service:

identity-backend/
├── app.py
├── passenger_wsgi.py
├── requirements.txt
├── config.py
├── models.py (optional for future user/service storage)
└── .gitignore

2. Environment variables in cPanel

Add these in the Python App Environment Variables:

JWT_SECRET_KEY = <generate-a-strong-random-string>
JWT_ISSUER = identity-backend
JWT_EXPIRATION_MINUTES = 15

(Later we’ll add support for RSA public/private keys, but HMAC is simpler for MVP.)

3. Application startup file

Set to:

passenger_wsgi.py

Contents:

from app import app as application

Phase 2: MVP Code for Identity Backend

config.py

import os
from dotenv import load_dotenv

# Load .env if available (local dev)
load_dotenv()

JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY", "dev-secret")  # HMAC key for signing
JWT_ISSUER = os.getenv("JWT_ISSUER", "identity-backend")
JWT_EXPIRATION_MINUTES = int(os.getenv("JWT_EXPIRATION_MINUTES", 15))

app.py

from flask import Flask, request, jsonify
import jwt
import datetime
from config import JWT_SECRET_KEY, JWT_ISSUER, JWT_EXPIRATION_MINUTES

app = Flask(__name__)

# Generate a JWT for a service (e.g., logging-service, careergpt-backend)
@app.route("/token", methods=["POST"])
def generate_token():
    """
    Request body:
    {
      "sub": "careergpt-backend",
      "aud": "logging-service"
    }
    """
    data = request.get_json()

    if not data or "sub" not in data or "aud" not in data:
        return jsonify({"error": "Missing required claims (sub, aud)"}), 400

    now = datetime.datetime.utcnow()
    payload = {
        "iss": JWT_ISSUER,
        "sub": data["sub"],
        "aud": data["aud"],
        "iat": now,
        "exp": now + datetime.timedelta(minutes=JWT_EXPIRATION_MINUTES)
    }

    token = jwt.encode(payload, JWT_SECRET_KEY, algorithm="HS256")
    return jsonify({"token": token})

# Verify JWT (optional debug endpoint)
@app.route("/verify", methods=["POST"])
def verify_token():
    data = request.get_json()
    token = data.get("token")

    try:
        decoded = jwt.decode(token, JWT_SECRET_KEY, algorithms=["HS256"], audience=data.get("aud"))
        return jsonify({"valid": True, "claims": decoded})
    except jwt.ExpiredSignatureError:
        return jsonify({"valid": False, "error": "Token expired"}), 401
    except jwt.InvalidTokenError as e:
        return jsonify({"valid": False, "error": str(e)}), 401

if __name__ == "__main__":
    app.run(port=5002)

requirements.txt

Flask==2.3.2
PyJWT==2.8.0
python-dotenv==1.0.0

Phase 3: How It Works

  1. Generate token for service

curl -X POST https://aurorahours.com/identity/token \
  -H "Content-Type: application/json" \
  -d '{"sub":"careergpt-backend","aud":"logging-service"}'
  1. Use token in logging-service

curl https://aurorahours.com/logging/logs \
  -H "Authorization: Bearer <JWT_FROM_IDENTITY>"
  1. logging-service validates token (next phase: replace LOG_SERVICE_KEY check with JWT validation)


Next Steps

  • Deploy this MVP to cPanel (same process as logging-service).

  • Test issuing and verifying tokens.

  • Update logging-service to accept JWT from identity-backend and validate claims (issuer, audience, expiry).

  • Remove static LOG_SERVICE_KEY once JWT works end-to-end.


Do you want me to write the JWT validation code for logging-service now (so it’s ready to accept tokens immediately)?
Or deploy identity-backend first, confirm it works on cPanel, and then integrate logging-service?

Comments

Popular posts from this blog

Feature: Audit log for one login, and identity service

Getting started - Build your data science lab environment

QA - Run #1 - Results