From 9209739e75bb487fb46e0d875e2abef95cb7e460 Mon Sep 17 00:00:00 2001 From: Eric Gullickson <16152721+ericgullickson@users.noreply.github.com> Date: Mon, 9 Feb 2026 20:51:30 -0600 Subject: [PATCH] feat: add Auth0 WIF token script and update Dockerfile (refs #127) - Create fetch-auth0-token.sh for Auth0 M2M -> GCP WIF token exchange - Add jq to Dockerfile system dependencies - Ensure script is executable in container image Co-Authored-By: Claude Opus 4.6 --- ocr/Dockerfile | 11 +++-- ocr/scripts/fetch-auth0-token.sh | 75 ++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 3 deletions(-) create mode 100755 ocr/scripts/fetch-auth0-token.sh diff --git a/ocr/Dockerfile b/ocr/Dockerfile index e5e7ec0..d1f116b 100644 --- a/ocr/Dockerfile +++ b/ocr/Dockerfile @@ -1,8 +1,8 @@ # Production Dockerfile for MotoVaultPro OCR Service # Uses mirrored base images from Gitea Package Registry # -# Primary engine: PaddleOCR PP-OCRv4 (models baked into image) -# Cloud fallback: Google Vision (optional, requires API key at runtime) +# Primary engine: Google Vision via Auth0 WIF (monthly-capped) +# Fallback engine: PaddleOCR PP-OCRv4 (models baked into image) # Build argument for registry (defaults to Gitea mirrors, falls back to Docker Hub) ARG REGISTRY_MIRRORS=git.motovaultpro.com/egullickson/mirrors @@ -14,7 +14,8 @@ FROM ${REGISTRY_MIRRORS}/python:3.13-slim # - libheif1/libheif-dev: HEIF image support (iPhone photos) # - libglib2.0-0: GLib shared library (OpenCV dependency) # - libmagic1: File type detection -# - curl: Health check endpoint +# - curl: Health check endpoint + Auth0 token fetch +# - jq: JSON parsing for Auth0 token script RUN apt-get update && apt-get install -y --no-install-recommends \ libgomp1 \ libheif1 \ @@ -22,6 +23,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libglib2.0-0 \ libmagic1 \ curl \ + jq \ && rm -rf /var/lib/apt/lists/* # Python dependencies @@ -42,5 +44,8 @@ RUN python -c "from paddleocr import PaddleOCR; PaddleOCR(ocr_version='PP-OCRv4' COPY . . +# Ensure Auth0 WIF token script is executable +RUN chmod +x /app/scripts/fetch-auth0-token.sh + EXPOSE 8000 CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/ocr/scripts/fetch-auth0-token.sh b/ocr/scripts/fetch-auth0-token.sh new file mode 100755 index 0000000..eb010fb --- /dev/null +++ b/ocr/scripts/fetch-auth0-token.sh @@ -0,0 +1,75 @@ +#!/bin/sh +# fetch-auth0-token.sh -- Auth0 M2M token fetcher for Google WIF +# +# Called by the Google Auth library when using executable-sourced +# credentials (see google-wif-config.json). Reads Auth0 client +# credentials from Docker secrets and returns the JWT in the format +# expected by Google's credential helpers. +# +# Exit codes: +# 0 -- success (JSON with token on stdout) +# 1 -- missing secrets or curl/jq failure + +set -e + +CLIENT_ID_FILE="/run/secrets/auth0-ocr-client-id" +CLIENT_SECRET_FILE="/run/secrets/auth0-ocr-client-secret" +AUTH0_DOMAIN="motovaultpro.auth0.com" +AUDIENCE="https://iam.googleapis.com/projects/487954699429/locations/global/workloadIdentityPools/motovaultpro-pool/providers/auth0-provider" + +# Read credentials from Docker secrets +if [ ! -f "$CLIENT_ID_FILE" ]; then + echo "Error: $CLIENT_ID_FILE not found" >&2 + exit 1 +fi +if [ ! -f "$CLIENT_SECRET_FILE" ]; then + echo "Error: $CLIENT_SECRET_FILE not found" >&2 + exit 1 +fi + +CLIENT_ID=$(cat "$CLIENT_ID_FILE" | tr -d '[:space:]') +CLIENT_SECRET=$(cat "$CLIENT_SECRET_FILE" | tr -d '[:space:]') + +# Request M2M token from Auth0 +RESPONSE=$(curl -s --fail-with-body \ + --request POST \ + --url "https://${AUTH0_DOMAIN}/oauth/token" \ + --header 'Content-Type: application/json' \ + --data "{ + \"client_id\": \"${CLIENT_ID}\", + \"client_secret\": \"${CLIENT_SECRET}\", + \"audience\": \"${AUDIENCE}\", + \"grant_type\": \"client_credentials\" + }") + +if [ $? -ne 0 ]; then + echo "Error: Auth0 token request failed" >&2 + echo "$RESPONSE" >&2 + exit 1 +fi + +# Extract the access token +TOKEN=$(echo "$RESPONSE" | jq -r '.access_token') + +if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then + echo "Error: No access_token in Auth0 response" >&2 + echo "$RESPONSE" >&2 + exit 1 +fi + +EXPIRY=$(echo "$RESPONSE" | jq -r '.expires_in') + +# Calculate expiration timestamp (seconds since epoch) +EXPIRATION_TIME=$(($(date +%s) + ${EXPIRY:-3600})) + +# Output in Google executable-sourced credential format +# https://cloud.google.com/iam/docs/workload-identity-federation-with-other-providers#create_a_credential_configuration +cat <