Skip to content

Commit 29f0b83

Browse files
authored
Merge pull request #66 from davidortinau/feature/39-user-secrets
feat: set up .NET user-secrets workflow (#39)
2 parents ccc8680 + 7a72ea8 commit 29f0b83

File tree

10 files changed

+279
-38
lines changed

10 files changed

+279
-38
lines changed

.squad/agents/jayne/history.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,22 @@
1818
- "It compiles" is NOT sufficient — must verify in running app
1919
- Must call `CacheService.InvalidateVocabSummary()` after recording attempts or dashboard is stale
2020
- Playwright must use `pressSequentially` not `fill()` for Blazor server-side binding
21+
- "It compiles" is NOT sufficient — must verify in running app
22+
- Must call `CacheService.InvalidateVocabSummary()` after recording attempts or dashboard is stale
23+
- Playwright must use `pressSequentially` not `fill()` for Blazor server-side binding
2124
- Test users: David (Korean, f452438c-...), Jose (Spanish, 8d5f7b4a-...), Gunther (German, c3bb57f7-...)
25+
26+
## Work Sessions
27+
28+
### 2026-03-13 — Cross-Agent Update: Azure Deployment Issues
29+
30+
**Status:** In Progress
31+
**GitHub Issues:** #39-#65 created by Zoe (Lead)
32+
**Jayne's Assignment:** N/A (testing/QA support for phase execution)
33+
34+
**Phase Execution Order:** Phase 2 (Secrets) → Phase 1 (Auth, localhost-testable) → Phase 3 (Infra) → Phase 4 (Pipeline) → Phase 5 (Hardening)
35+
36+
**E2E Testing Support:** Jayne to verify each phase's integration tests per e2e-testing skill (mandatory for every feature/fix).
37+
38+
**Critical Path:** CoreSync SQLite→PostgreSQL migration (#55, XL) — requires comprehensive data migration testing.
39+

.squad/agents/kaylee/history.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,26 @@
1616
- Activity pages: PageHeader, activity-content area, footer with activity-input-bar
1717
- Word Association activity at `/word-association` — latest activity, has Grade-first UX flow
1818
- Dashboard activities listed in `src/SentenceStudio.UI/Pages/Index.razor`
19+
20+
## Work Sessions
21+
22+
### 2026-03-13 — Cross-Agent Update: Azure Deployment Issues
23+
24+
**Status:** In Progress
25+
**GitHub Issues:** #39-#65 created by Zoe (Lead)
26+
**Kaylee's Assignments:** 8 issues
27+
28+
**Issues Assigned to Kaylee:**
29+
- #44 WebApp OIDC Integration (Phase 1, size:L)
30+
- #45 MAUI MSAL Implementation (Phase 1, size:XL)
31+
- #56 CI Workflow Setup (Phase 4, size:M)
32+
- #57 Deploy Workflow (Phase 4, size:L)
33+
- #58 Staging Environment (Phase 4, size:M)
34+
- #60 Azure Monitor/Application Insights (Phase 5, size:M)
35+
- #62 CORS Configuration (Phase 5, size:S)
36+
- #64 Auto-Scaling Rules (Phase 5, size:M)
37+
38+
**Phase Execution Order:** Phase 2 (Secrets) → Phase 1 (Auth, localhost-testable) → Phase 3 (Infra) → Phase 4 (Pipeline) → Phase 5 (Hardening)
39+
40+
**Critical Path:** CoreSync SQLite→PostgreSQL migration (#55, XL).
41+

.squad/agents/wash/history.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,23 @@
1717
- DI registration in `SentenceStudioAppBuilder.cs` (AppLib) and `Program.cs` (WebApp)
1818
- Aspire env var config: `builder.Configuration["AI:OpenAI:ApiKey"]` not `["AI__OpenAI__ApiKey"]`
1919
- Server DB at: `/Users/davidortinau/Library/Application Support/sentencestudio/server/sentencestudio.db`
20+
- Server DB at: `/Users/davidortinau/Library/Application Support/sentencestudio/server/sentencestudio.db`
2021
- UserProfileId columns for multi-user data isolation — all repos filter by active_profile_id
22+
23+
## Work Sessions
24+
25+
### 2026-03-13 — Cross-Agent Update: Azure Deployment Issues
26+
27+
**Status:** In Progress
28+
**GitHub Issues:** #39-#65 created by Zoe (Lead)
29+
**Wash's Role:** Deployment orchestration support
30+
31+
**Phase Execution Order:** Phase 2 (Secrets) → Phase 1 (Auth, localhost-testable) → Phase 3 (Infra) → Phase 4 (Pipeline) → Phase 5 (Hardening)
32+
33+
**Wash Coordination Points:**
34+
- Phase 4 (Pipeline) — CI/deploy workflows — coordinate with Kaylee's automation
35+
- Phase 3.5 (Container Apps) — deployment target provisioning
36+
- Critical Path: CoreSync SQLite→PostgreSQL migration (#55, XL) — coordinate safe data migration in production
37+
38+
**Key Dependencies:** Zoe coordinates Phase 1-3 decisions; Kaylee implements CI/deploy automation; Captain provides Azure portal access.
39+

.squad/agents/zoe/history.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,46 @@
1818
- Build with TFM: `dotnet build -f net10.0-maccatalyst`
1919
- E2E testing is mandatory for every feature/fix
2020
- Activities follow pattern: `activity-page-wrapper``PageHeader``activity-content``activity-input-bar`
21+
22+
## Work Sessions
23+
24+
### 2026-03-13 — GitHub Issues Created for Azure + Entra ID Plan
25+
26+
**Status:** Complete
27+
**Issues Created:** 27 issues (#39#65)
28+
**Dependencies:** All cross-referenced with dependency links
29+
30+
**Cross-Team Impact:**
31+
- **Kaylee:** 8 issues assigned (#44–45, #56–59, #60)
32+
- **Captain:** 1 issue assigned (#42)
33+
- Issues propagated to respective agent history files
34+
35+
See `.squad/decisions.md` for full decision record.
36+
37+
### 2025-07-22 — Created GitHub Issues for Azure Deployment + Entra ID Plan
38+
39+
**Status:** Complete
40+
**Issues Created:** 27 issues (#39-#65)
41+
**Decision:** Reframed issue #39 (2.1) from "security emergency" to "best practices" — no secrets were committed to git history.
42+
43+
**Issue Mapping to Plan:**
44+
45+
- **Phase 1 (Auth):** #42 (Entra registrations) → #43 (JWT API) → #44 (WebApp OIDC) → #45 (MAUI MSAL) → #46 (CoreSync) → #47 (Integration tests)
46+
- **Phase 2 (Secrets):** #39 (user-secrets) → #40 (config all projects) → #41 (HTTPS/headers) → #54 (Key Vault integration)
47+
- **Phase 3 (Infrastructure):** #48 (azure.yaml) → #49 (PostgreSQL) → #50 (Redis) → #51 (Blob) → #52 (Container Apps) → #53 (Key Vault) → #55 (CoreSync DB migration)
48+
- **Phase 4 (Pipeline):** #56 (CI) → #57 (Deploy) → #58 (Staging) → #59 (Migrations)
49+
- **Phase 5 (Hardening):** #60 (Monitoring) → #61 (Rate limit) → #62 (CORS) → #63 (Health) → #64 (Scaling) → #65 (Audit logging)
50+
51+
**Team Assignments:**
52+
- Zoe (Lead): 14 issues (auth foundational work, infra decisions, hardening architecture)
53+
- Kaylee (Full-stack): 8 issues (WebApp OIDC, MAUI MSAL, CI/deploy workflows, monitoring)
54+
- Captain (David): 1 issue (#42 - requires Azure portal/Entra ID access)
55+
56+
**Dependencies Validated:** All 27 issues cross-referenced with dependency links. Phase order preserved for execution.
57+
58+
**Key Learnings:**
59+
- No security emergency: appsettings.json with secrets already in .gitignore
60+
- User-secrets workflow as team best practice (Phase 2.1)
61+
- Phase 1 testable entirely on localhost with Entra ID redirecting to `http://localhost`
62+
- CoreSync SQLite→PostgreSQL migration is critical path item (Phase 3.7, XL size)
63+
- Aspire-native provisioning via `azd` avoids manual Bicep maintenance

.squad/decisions.md

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,71 @@
22

33
## Active Decisions
44

5-
No decisions recorded yet.
5+
### 1. GitHub Issues Created for Azure + Entra ID Plan (2026-03-13)
6+
7+
**Status:** DOCUMENTED
8+
**Date:** 2026-03-13
9+
**Author:** Zoe (Lead)
10+
11+
27 GitHub issues decompose the Azure deployment + Entra ID authentication plan into actionable work items across 5 phases. All issues linked with dependency references and assigned to team members.
12+
13+
**Key Decisions:**
14+
- **Reframed Issue #39:** User-secrets workflow as team best practice (not security emergency) — no secrets accidentally committed; `appsettings.json` already in `.gitignore`
15+
- **Execution Order:** Phase 2 → Phase 1 → Phase 3 → Phase 4 → Phase 5 (security-first approach)
16+
- **Phase 1 Testable Locally:** Auth flow fully validates on `localhost` without Azure deployment
17+
- **Team Assignments:** Zoe (14 issues, architecture/infra), Kaylee (8 issues, UI/deploy), Captain (1 issue, Azure portal)
18+
- **Critical Path:** CoreSync SQLite→PostgreSQL migration (Phase 3.7, XL complexity)
19+
20+
**Issue Mapping:**
21+
| Phase | Count | Issues |
22+
|-------|-------|--------|
23+
| Phase 1 (Auth) | 7 | #42-47 |
24+
| Phase 2 (Secrets) | 4 | #39-41, #54 |
25+
| Phase 3 (Infrastructure) | 8 | #48-53, #55 |
26+
| Phase 4 (Pipeline) | 4 | #56-59 |
27+
| Phase 5 (Hardening) | 6 | #60-65 |
28+
29+
**Learnings:**
30+
- Aspire-native provisioning (`azd`) generates Bicep — no manual templates needed
31+
- Localhost testing of auth eliminates blocker for early validation
32+
- DevAuthHandler alongside Entra ID maintains developer velocity
33+
- User-secrets as team best practice enables secure local dev
34+
35+
**Next Steps:**
36+
1. Captain: Register Entra ID app registrations (#42)
37+
2. Zoe: Begin user-secrets setup (#39-40)
38+
3. Kaylee: Begin CI workflow (#56)
39+
4. All phases proceed in parallel where dependencies allow
40+
41+
---
42+
43+
### 2. Architecture Plan: Azure Deployment with Entra ID Authentication (2026-03-13)
44+
45+
**Status:** REFERENCE
46+
**Date:** 2026-03-13
47+
**Author:** Zoe (Lead)
48+
49+
Comprehensive architecture plan for transitioning SentenceStudio from local-dev-only to production-ready Azure deployment with real authentication. Covers 5 phases from secret management through hardening, with technical decisions, risk register, and cost estimates.
50+
51+
**Key Technical Decisions:**
52+
1. **Aspire-Native Provisioning over Raw Bicep** — AppHost defines resources; `azd` generates Bicep
53+
2. **Keep DevAuthHandler Alongside Entra ID** — Developer velocity: use DevAuthHandler for local dev, Entra ID for production
54+
3. **PostgreSQL over Azure SQL** — Aligns with AppHost declaration and CoreSync support
55+
4. **Single-Tenant First** — Start with single Entra ID tenant; multi-tenant support added later
56+
5. **Token Caching:** SecureStorage (MAUI) and Redis (WebApp)
57+
58+
**3 App Registrations Required:**
59+
- SentenceStudio API (Web API — resource server)
60+
- SentenceStudio WebApp (Web app, confidential — Blazor Server)
61+
- SentenceStudio Native (Mobile/Desktop, public — MAUI clients)
62+
63+
**Scopes Exposed:**
64+
- `api://sentencestudio/user.read` — user profile, vocabulary
65+
- `api://sentencestudio/user.write` — modify user data, submit answers
66+
- `api://sentencestudio/ai.access` — AI chat, speech synthesis, image analysis
67+
- `api://sentencestudio/sync.readwrite` — CoreSync bi-directional sync
68+
69+
**Estimated Monthly Cost (Production):** ~$107-252
670

771
## Governance
872

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Decision: Set up .NET user-secrets workflow
2+
3+
**Author:** Wash (Backend Dev)
4+
**Issue:** #39
5+
**Date:** 2025-07-17
6+
**Branch:** feature/39-user-secrets
7+
8+
## Summary
9+
10+
Established the .NET user-secrets pattern for secure local development across all server-side projects.
11+
12+
## What Changed
13+
14+
1. **Initialized user-secrets** for `SentenceStudio.Api` and `SentenceStudio.WebApp` via `dotnet user-secrets init`. Workers and AppHost already had UserSecretsId configured.
15+
16+
2. **Created `secrets.template.json`** at repo root documenting every secret key, organized by project context (AppHost/Aspire, Api standalone, WebApp standalone, MAUI apps).
17+
18+
3. **Updated `README.md`** section 3 ("API Keys and Secrets") with three clear paths:
19+
- Option A: Aspire (recommended) -- set secrets once in AppHost, they flow to all services via `WithEnvironment()`
20+
- Option B: Standalone projects -- set secrets per-project with `dotnet user-secrets`
21+
- Option C: MAUI mobile/desktop -- use gitignored `appsettings.json` in AppLib
22+
23+
## How Secrets Flow
24+
25+
The AppHost uses Aspire Parameters (`builder.AddParameter("openaikey", secret: true)`) which resolve from the AppHost's user-secrets under `Parameters:openaikey`. These are then passed to child projects via `.WithEnvironment("AI__OpenAI__ApiKey", openaikey)`. Aspire normalizes `__` to `:` in configuration, so `AI__OpenAI__ApiKey` becomes `AI:OpenAI:ApiKey` at the receiving end.
26+
27+
## Projects with UserSecretsId
28+
29+
| Project | UserSecretsId |
30+
|---------|---------------|
31+
| AppHost | d8521a4e-969b-4696-9990-45dea324bda8 |
32+
| Api | 9ae3953f-a490-41b3-a2b8-a8e2555b4615 |
33+
| WebApp | 33f95f89-d495-4311-b6cb-53a47b5c34e6 |
34+
| Workers | dotnet-SentenceStudio.Workers-8ded0183-d135-40b2-b2d4-b49b096922b8 |
35+
36+
## Secrets Inventory
37+
38+
| Secret | AppHost Parameter | Api Key | WebApp Key |
39+
|--------|-------------------|---------|------------|
40+
| OpenAI | Parameters:openaikey | AI:OpenAI:ApiKey | Settings:OpenAIKey |
41+
| ElevenLabs | Parameters:elevenlabskey | ElevenLabsKey | Settings:ElevenLabsKey |
42+
| Syncfusion | Parameters:syncfusionkey | N/A | N/A |
43+
44+
## No Data Impact
45+
46+
No database changes. No existing secrets were moved or deleted. The AppHost's existing user-secrets remain intact and functional.

README.md

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -131,56 +131,61 @@ cd src
131131
dotnet restore
132132
```
133133

134-
### 3. API Keys Configuration
134+
### 3. API Keys and Secrets
135135

136-
Sentence Studio requires API keys for AI and text-to-speech functionality. You'll need to obtain and configure the following:
136+
Sentence Studio requires API keys for AI and text-to-speech functionality. Obtain the following keys before proceeding:
137137

138-
#### Required API Keys
138+
| Secret | Required | Where to get it |
139+
|--------|----------|-----------------|
140+
| **OpenAI API Key** | Yes | https://platform.openai.com/api-keys |
141+
| **ElevenLabs API Key** | Yes | https://elevenlabs.io/app/speech-synthesis |
142+
| **Syncfusion License Key** | Optional | https://www.syncfusion.com/account/downloads |
143+
| **HuggingFace Token** | Optional | https://huggingface.co/settings/tokens |
139144

140-
1. **OpenAI API Key** - For AI-powered language learning features
141-
- Visit: https://platform.openai.com/api-keys
142-
- Create an account and generate an API key
143-
- Ensure your account has sufficient credits or an active subscription
145+
See `secrets.template.json` at the repo root for the full key structure.
144146

145-
2. **ElevenLabs API Key** - For text-to-speech functionality
146-
- Visit: https://elevenlabs.io/app/speech-synthesis
147-
- Create an account and generate an API key
148-
- Free tier available with usage limitations
147+
#### Option A: Running with .NET Aspire (recommended)
149148

150-
3. **HuggingFace Token** (Optional) - For additional AI model access
151-
- Visit: https://huggingface.co/settings/tokens
152-
- Create an account and generate an access token
149+
When running through the AppHost, secrets are managed centrally and passed to all services automatically. Set them once in the AppHost project:
153150

154-
4. **Syncfusion Key** (Optional) - For premium UI components
155-
- Visit: https://www.syncfusion.com/account/downloads
156-
- Create a community account for free license
151+
```bash
152+
cd src/SentenceStudio.AppHost
153+
154+
dotnet user-secrets set "Parameters:openaikey" "sk-proj-your-key-here"
155+
dotnet user-secrets set "Parameters:elevenlabskey" "sk_your-key-here"
156+
dotnet user-secrets set "Parameters:syncfusionkey" "your-license-key-here"
157+
```
157158

158-
#### Configuration Setup
159+
The AppHost passes these to Api, WebApp, Workers, and MAUI projects via `WithEnvironment()` at runtime. You do not need to configure secrets in each individual project when using Aspire.
159160

160-
Copy the template configuration file and add your API keys:
161+
#### Option B: Running individual projects without Aspire
161162

163+
If you run a project standalone (not through the AppHost), set its secrets directly:
164+
165+
**Api:**
162166
```bash
163-
# Copy the template file
164-
cp src/SentenceStudio/appsettings.template.json src/SentenceStudio/appsettings.json
167+
cd src/SentenceStudio.Api
168+
dotnet user-secrets set "AI:OpenAI:ApiKey" "sk-proj-your-key-here"
169+
dotnet user-secrets set "ElevenLabsKey" "sk_your-key-here"
170+
```
165171

166-
# Edit the appsettings.json file with your actual API keys
167-
# Note: This file is gitignored for security
172+
**WebApp:**
173+
```bash
174+
cd src/SentenceStudio.WebApp
175+
dotnet user-secrets set "Settings:OpenAIKey" "sk-proj-your-key-here"
176+
dotnet user-secrets set "Settings:ElevenLabsKey" "sk_your-key-here"
168177
```
169178

170-
Your `appsettings.json` should look like:
171-
172-
```json
173-
{
174-
"Settings": {
175-
"OpenAIKey": "sk-proj-your-actual-openai-key-here",
176-
"HuggingFaceToken": "hf_your-actual-huggingface-token-here",
177-
"ElevenLabsKey": "sk_your-actual-elevenlabs-key-here",
178-
"SyncfusionKey": "your-actual-syncfusion-key-here"
179-
}
180-
}
179+
#### Option C: MAUI mobile/desktop apps (without Aspire)
180+
181+
MAUI apps read from `appsettings.json` in the AppLib project. This file is gitignored:
182+
183+
```bash
184+
cp src/SentenceStudio.AppLib/appsettings.template.json src/SentenceStudio.AppLib/appsettings.json
185+
# Edit src/SentenceStudio.AppLib/appsettings.json with your actual keys
181186
```
182187

183-
**⚠️ Security Warning**: Never commit the `appsettings.json` file containing real API keys to version control. The file is included in `.gitignore` to prevent accidental exposure.
188+
**Security note:** The `appsettings.json` files containing secrets are gitignored. Never commit real API keys to version control. Use `dotnet user-secrets` for all server-side projects.
184189

185190
## Building and Running
186191

secrets.template.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"_comment": "This file documents all secrets required for local development. NO actual values here.",
3+
"_instructions": "Set values using: dotnet user-secrets set 'Key' 'value' --project src/<Project>",
4+
5+
"_section_apphost": "---- AppHost (Aspire orchestrator) ---- Run from src/SentenceStudio.AppHost",
6+
"Parameters:openaikey": "<your-openai-api-key>",
7+
"Parameters:elevenlabskey": "<your-elevenlabs-api-key>",
8+
"Parameters:syncfusionkey": "<your-syncfusion-license-key>",
9+
10+
"_section_api": "---- Api (standalone, without Aspire) ---- Run from src/SentenceStudio.Api",
11+
"AI:OpenAI:ApiKey": "<your-openai-api-key>",
12+
"ElevenLabsKey": "<your-elevenlabs-api-key>",
13+
14+
"_section_webapp": "---- WebApp (standalone, without Aspire) ---- Run from src/SentenceStudio.WebApp",
15+
"Settings:OpenAIKey": "<your-openai-api-key>",
16+
"Settings:ElevenLabsKey": "<your-elevenlabs-api-key>",
17+
18+
"_section_maui": "---- MAUI Apps (AppLib appsettings.json, gitignored) ---- Copy appsettings.template.json to appsettings.json in src/SentenceStudio.AppLib/",
19+
"Settings:SyncfusionKey": "<your-syncfusion-license-key>",
20+
"Settings:HuggingFaceToken": "<your-huggingface-token (optional)>"
21+
}

src/SentenceStudio.Api/SentenceStudio.Api.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
<Project Sdk="Microsoft.NET.Sdk.Web">
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
44
<TargetFramework>net10.0</TargetFramework>
55
<Nullable>enable</Nullable>
66
<ImplicitUsings>enable</ImplicitUsings>
7+
<UserSecretsId>9ae3953f-a490-41b3-a2b8-a8e2555b4615</UserSecretsId>
78
</PropertyGroup>
89

910
<ItemGroup>

src/SentenceStudio.WebApp/SentenceStudio.WebApp.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk.Web">
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<ItemGroup>
44
<ProjectReference Include="..\SentenceStudio.AppLib\SentenceStudio.AppLib.csproj" />
@@ -13,6 +13,7 @@
1313
<Nullable>enable</Nullable>
1414
<ImplicitUsings>enable</ImplicitUsings>
1515
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
16+
<UserSecretsId>33f95f89-d495-4311-b6cb-53a47b5c34e6</UserSecretsId>
1617
</PropertyGroup>
1718

1819
</Project>

0 commit comments

Comments
 (0)