diff --git a/tenants/demo/30-apps-stubs.yaml b/tenants/demo/30-apps-stubs.yaml index 570b37c..4c1234b 100644 --- a/tenants/demo/30-apps-stubs.yaml +++ b/tenants/demo/30-apps-stubs.yaml @@ -1,58 +1,7 @@ -# Apps reais do tenant demo: backend Resource Server (Node) + SPA OIDC Code+PKCE ---- -apiVersion: v1 -kind: ConfigMap -metadata: { name: backend-code, namespace: demo-prod } -data: - server.js: | - const http = require('http'); - const https = require('https'); - const crypto = require('crypto'); - const ISSUER = process.env.ISSUER; - const JWKS_URL = process.env.JWKS_URL; - const ORIGIN = process.env.CORS_ORIGIN || '*'; - let keys = {}, keysAt = 0; - function fetchJson(url) { - return new Promise((resolve, reject) => { - const lib = url.startsWith('https') ? https : http; - lib.get(url, (r) => { let d = ''; r.on('data', c => d += c); r.on('end', () => { try { resolve(JSON.parse(d)); } catch (e) { reject(e); } }); }).on('error', reject); - }); - } - async function key(kid) { - if (Date.now() - keysAt > 300000) { const j = await fetchJson(JWKS_URL); keys = {}; j.keys.forEach(k => keys[k.kid] = k); keysAt = Date.now(); } - return keys[kid]; - } - async function verify(token) { - const [h, p, s] = token.split('.'); - const header = JSON.parse(Buffer.from(h, 'base64url').toString()); - const jwk = await key(header.kid); - if (!jwk) throw new Error('unknown kid'); - const pub = crypto.createPublicKey({ key: jwk, format: 'jwk' }); - const ok = crypto.verify('RSA-SHA256', Buffer.from(h + '.' + p), pub, Buffer.from(s, 'base64url')); - if (!ok) throw new Error('bad signature'); - const c = JSON.parse(Buffer.from(p, 'base64url').toString()); - if (c.iss !== ISSUER) throw new Error('bad issuer'); - if (c.exp * 1000 < Date.now()) throw new Error('expired'); - return c; - } - const server = http.createServer(async (req, res) => { - res.setHeader('Content-Type', 'application/json'); - res.setHeader('Access-Control-Allow-Origin', ORIGIN); - res.setHeader('Access-Control-Allow-Headers', 'Authorization, Content-Type'); - if (req.method === 'OPTIONS') { res.statusCode = 204; res.end(); return; } - if (req.url === '/api/public/health') { res.end(JSON.stringify({ status: 'UP', service: 'athletic-map-backend', tenant: 'demo' })); return; } - if (req.url === '/api/me') { - const a = req.headers.authorization || ''; - if (!a.startsWith('Bearer ')) { res.statusCode = 401; res.end(JSON.stringify({ error: 'unauthorized' })); return; } - try { - const c = await verify(a.slice(7)); - res.end(JSON.stringify({ subject: c.sub, preferredUsername: c.preferred_username, email: c.email, roles: (c.realm_access || {}).roles || [], issuer: c.iss, validatedBy: 'athletic-map-backend (Node Resource Server)' })); - } catch (e) { res.statusCode = 401; res.end(JSON.stringify({ error: 'invalid_token', detail: String(e.message) })); } - return; - } - res.statusCode = 404; res.end(JSON.stringify({ error: 'not_found' })); - }); - server.listen(8080, () => console.log('backend listening on 8080')); +# Apps do tenant demo: +# - backend: Spring Boot OAuth2 Resource Server (imagem athletic-map-backend:1.2, porta 8083) +# - frontend: SPA OIDC Authorization Code + PKCE (keycloak-js) chamando /api/me +# - bff: stub (whoami) --- apiVersion: apps/v1 kind: Deployment @@ -65,28 +14,25 @@ spec: spec: containers: - name: backend - image: node:20-alpine - command: ["node", "/app/server.js"] + image: docker.io/library/athletic-map-backend:1.2 + imagePullPolicy: Never env: - - { name: ISSUER, value: "https://auth-demo.187.77.37.184.nip.io/realms/athleticmap" } - - { name: JWKS_URL, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } - - { name: CORS_ORIGIN, value: "https://demo.187.77.37.184.nip.io" } - ports: [{ containerPort: 8080 }] - volumeMounts: [{ name: code, mountPath: /app }] + - { name: ATM_JWK_SET_URI, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } + - { name: ATM_ISSUER, value: "https://auth-demo.187.77.37.184.nip.io/realms/athleticmap" } + - { name: ATM_TENANT, value: "demo" } + ports: [{ containerPort: 8083 }] readinessProbe: - httpGet: { path: /api/public/health, port: 8080 } - initialDelaySeconds: 5 + httpGet: { path: /api/public/health, port: 8083 } + initialDelaySeconds: 20 periodSeconds: 10 - volumes: - - name: code - configMap: { name: backend-code } + failureThreshold: 24 --- apiVersion: v1 kind: Service metadata: { name: backend, namespace: demo-prod } spec: selector: { app: backend } - ports: [{ port: 80, targetPort: 8080 }] + ports: [{ port: 80, targetPort: 8083 }] --- apiVersion: apps/v1 kind: Deployment diff --git a/tenants/piloto/30-apps-stubs.yaml b/tenants/piloto/30-apps-stubs.yaml index 3725f1a..fadca05 100644 --- a/tenants/piloto/30-apps-stubs.yaml +++ b/tenants/piloto/30-apps-stubs.yaml @@ -14,11 +14,12 @@ spec: spec: containers: - name: backend - image: docker.io/library/athletic-map-backend:1.1 + image: docker.io/library/athletic-map-backend:1.2 imagePullPolicy: Never env: - { name: ATM_JWK_SET_URI, value: "http://keycloak:8080/realms/athleticmap/protocol/openid-connect/certs" } - { name: ATM_ISSUER, value: "https://auth.187.77.37.184.nip.io/realms/athleticmap" } + - { name: ATM_TENANT, value: "piloto" } ports: [{ containerPort: 8083 }] readinessProbe: httpGet: { path: /api/public/health, port: 8083 }