feat: Google Sign-In → Secreted Token → Free Nodecore RPC
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 20/100
- Issue type
- Feature
- Clarity
- Needs clarification
- Activity status
- Stale
- Tech stack
- go, helm, kubernetes, next.js
- Domain
- api, authentication, cloud, infrastructure
Research direction
Start by reviewing the Phase 1–4 checklist and the three open decisions, then inspect the named Next.js API routes, values/erpc.yaml, erpc.yaml, and erpc/clients/http_json_rpc_client.go. Completion requires an agreed reload and OAuth design implemented across the listed components, with token storage, header injection, JWT validation, and rate-limit behavior validated end to end.
Written by the indexing model from the issue text.
Description
Summary
Enable authenticated access to Nodecore RPC through the local Obol Stack (obol.stack). Users authenticate via Google OAuth, and their ID token is stored in a Kubernetes secret. eRPC injects this token into upstream requests, allowing Nodecore to bypass rate limits for authenticated users.
Goals
- Single-user authentication for local development stack
- API key management via Kubernetes secrets
- Auditing/logging user identity at Nodecore
- Rate limit bypass for authenticated requests
Architecture Overview
┌─────────────────────────────────────────────────────────────────────────┐
│ https://obol.stack │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌─────────────────┐ │
│ │ Browser │────────▶│ Traefik │ │
│ │ │ │ (TLS/mkcert) │ │
│ └──────────────┘ └────────┬────────┘ │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Next.js Frontend │ │ eRPC │ │ Other │ │
│ │ (obol-frontend) │ │ /rpc/* │ │ Services │ │
│ └──────────┬──────────┘ └──────┬───────┘ └──────────────┘ │
│ │ │ │
│ │ writes │ reads env │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Kubernetes Secret: obol-oauth-token (namespace: erpc) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ eRPC injects header: X-Nodecore-Token │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Nodecore │ │
│ │ (validates JWT) │ │
│ └──────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
Authentication Flow
Step-by-Step
- User opens
https://obol.stack - Clicks "Sign in with Google"
- Browser initiates Google OAuth PKCE flow
- Google redirects to
https://obol.stack/api/auth/callback - Next.js API route (server-side) exchanges code for tokens
- API route writes ID token to Kubernetes secret
obol-oauth-token - eRPC pod reads token from secret (via environment variable)
- eRPC injects
X-Nodecore-Token: <id_token>on all upstream requests - Nodecore validates JWT and bypasses rate limits
Important Clarification
The browser does NOT write directly to Kubernetes.
The Next.js API route (running server-side in the cluster) has Kubernetes API access and writes the secret.
Component Specifications
1. Ingress (Traefik)
| Requirement | Implementation |
|---|---|
| HTTPS | mkcert for locally-trusted TLS certificate |
| Domain | obol.stack (via /etc/hosts) |
| OAuth Redirect | https://obol.stack/api/auth/callback |
2. Frontend (Next.js)
New API Routes Required:
| Route | Purpose |
|---|---|
GET /api/auth/login |
Initiates Google OAuth PKCE flow |
GET /api/auth/callback |
Handles OAuth callback, writes token to k8s secret |
POST /api/auth/refresh |
Refreshes token and updates secret |
POST /api/auth/logout |
Clears token from secret |
Token Management:
- Frontend must refresh token every 45 minutes
- Uses Google refresh token to obtain new ID token
- Updates Kubernetes secret after each refresh
Kubernetes RBAC Required:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: oauth-token-writer
namespace: erpc
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["obol-oauth-token"]
verbs: ["get", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: frontend-oauth-token-writer
namespace: erpc
subjects:
- kind: ServiceAccount
name: obol-frontend
namespace: obol-frontend
roleRef:
kind: Role
name: oauth-token-writer
apiGroup: rbac.authorization.k8s.io
3. eRPC Configuration
Header Injection (Native Support Confirmed):
eRPC supports custom headers on upstream requests via jsonRpc.headers with environment variable expansion using ${VAR} syntax.
# erpc.yaml
projects:
- id: main
upstreams:
- id: nodecore
endpoint: https://rpc.nodecore.io
jsonRpc:
headers:
X-Nodecore-Token: "${OBOL_OAUTH_TOKEN}"
Helm Values:
# values/erpc.yaml
secretEnv:
OBOL_OAUTH_TOKEN:
secretKeyRef:
name: obol-oauth-token
key: token
4. Token Rotation
Decision Required: eRPC Reload Mechanism
Current Limitation:
eRPC expands environment variables once at startup (os.ExpandEnv()). There is no SIGHUP handler or config hot-reload support.
Options:
| Option | Latency | Downtime | Complexity | Notes |
|---|---|---|---|---|
| A. Stakater Reloader | ~30s | Zero (rolling) | Low | Recommended for simplicity |
| B. Contribute SIGHUP to eRPC | Immediate | Zero | Medium | Requires upstream contribution |
| C. Short pod TTL | Variable | Zero (rolling) | Low | Wasteful, not recommended |
| D. Sidecar token injector | Immediate | Zero | High | Custom development required |
Option A: Stakater Reloader (Will cause rolling restart))
apiVersion: apps/v1
kind: Deployment
metadata:
name: erpc
namespace: erpc
annotations:
secret.reloader.stakater.com/reload: "obol-oauth-token"
spec:
# ... triggers rolling restart when secret changes
Pros:
- No code changes to eRPC
- Battle-tested solution
- Zero downtime with readiness probes
Cons:
- ~30 second propagation delay
- Pod restart on every token refresh (every 45 min)
Option B: Contribute SIGHUP to eRPC
Add signal handler to eRPC upstream:
// Proposed change to cmd/erpc/main.go
sighup := make(chan os.Signal, 1)
signal.Notify(sighup, syscall.SIGHUP)
go func() {
for range sighup {
logger.Info().Msg("SIGHUP received, reloading config...")
cfg, _ = common.LoadConfig(fs, configPath, opts)
// Reinitialize upstreams with new config
}
}()
Then trigger via:
kubectl exec -n erpc deploy/erpc -- kill -HUP 1
Pros:
- Immediate token update
- No pod restart
- Benefits entire eRPC community
Cons:
- Requires upstream PR and approval
- Development and testing effort
5. Nodecore Requirements
JWT Validation:
| Field | Expected Value |
|---|---|
iss (issuer) |
https://accounts.google.com |
aud (audience) |
Google OAuth Client ID |
exp (expiry) |
Must be in the future |
| Signature | Validated against Google JWKS |
JWKS Endpoint: https://www.googleapis.com/oauth2/v3/certs
Rate Limit Behavior:
- Valid JWT → Skip rate limiting
- Invalid/missing JWT → Apply standard rate limits
6. Kubernetes Secret Schema
apiVersion: v1
kind: Secret
metadata:
name: obol-oauth-token
namespace: erpc
type: Opaque
data:
token: <base64-encoded-google-id-token>
Security Considerations
| Concern | Mitigation |
|---|---|
| Token exposure in logs | eRPC does not log header values at INFO level |
| Cross-namespace access | RBAC limits frontend to single secret |
| Token theft from secret | k8s secrets encrypted at rest (k3s default) |
| Man-in-the-middle | TLS required for all traffic |
| Token expiry | 45-minute refresh cycle |
Implementation Phases
Phase 1: Infrastructure
- Configure mkcert TLS certificate for Traefik
- Create
obol-oauth-tokensecret (empty placeholder) - Add RBAC for cross-namespace secret access
- Deploy Stakater Reloader (if Option A chosen)
Phase 2: Frontend OAuth
- Configure Google OAuth Client (redirect URI:
https://obol.stack/api/auth/callback) - Implement
/api/auth/loginroute (PKCE flow initiation) - Implement
/api/auth/callbackroute (token exchange + secret write) - Implement
/api/auth/refreshroute (background refresh) - Add auth state UI (signed in/out indicator)
Phase 3: eRPC Configuration
- Update eRPC Helm values with
secretEnvforOBOL_OAUTH_TOKEN - Add
X-Nodecore-Tokenheader to Nodecore upstream config - Add Reloader annotation to eRPC deployment
- Test header injection with mock upstream
Phase 4: Nodecore Integration
- Implement JWT validation middleware
- Configure rate limit bypass for valid tokens
- Add audit logging for authenticated requests
Open Decisions
| # | Decision | Options | Owner | Due |
|---|---|---|---|---|
| 1 | eRPC reload mechanism | A: Reloader, B: SIGHUP contribution | Team | TBD |
| 2 | Google OAuth Client ID source | Environment var vs ConfigMap | Team | TBD |
| 3 | Token refresh location | Frontend background task vs dedicated service | Team | TBD |
Appendix: eRPC Header Injection Evidence
From erpc/clients/http_json_rpc_client.go:790-793:
// Add custom headers if provided
for k, v := range c.headers {
httpReq.Header.Set(k, v)
}
From erpc/common/config.go:68:
expandedData := []byte(os.ExpandEnv(string(data)))
Environment variables using ${VAR} syntax are expanded at config load time.
Appendix: Google OAuth PKCE Flow
┌──────────┐ ┌──────────────┐
│ Browser │ │ Google │
└────┬─────┘ └──────┬───────┘
│ │
│ 1. GET /api/auth/login │
│──────────────────────────────────────────▶│
│ │
│ 2. Redirect to Google (code_challenge) │
│◀──────────────────────────────────────────│
│ │
│ 3. User authenticates │
│──────────────────────────────────────────▶│
│ │
│ 4. Redirect to callback (code) │
│◀──────────────────────────────────────────│
│ │
│ 5. POST /api/auth/callback │
│───────┐ │
│ │ Exchange code for tokens │
│ │ (with code_verifier) │
│◀──────┘ │
│ │
│ 6. Write token to k8s secret │
│ │
│ 7. Set session cookie, redirect to app │
│ │
- Dominant language
- Go
- Stars
- 11
- Forks
- 1
- PR merge metrics
- No merged PRs in 30d
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from ObolNetwork/obol-stack
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
ObolNetwork/obol-stack#612 ·
-
Difficulty 5/5 Over a week Newbie friendliness 30/100
ObolNetwork/obol-stack#829 ·
-
bug
Difficulty 3/5 1-2 days Newbie friendliness 72/100
ObolNetwork/obol-stack#828 ·
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
ObolNetwork/obol-stack#821 · 1 comment ·
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
ObolNetwork/obol-stack#807 · 1 comment ·
All issues in ObolNetwork/obol-stack
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
-
bug group: validation priority: low
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
codecheckers/chekhov#51 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100