Altinn / Altinn/altinn-authentication
Enable appconfiguration
- Dominant language
- C#
- Stars
- 8
- Forks
- 5
- Avg merge
- 3d 10h
- Merged PRs (30d)
- 17
Description
## Goal
Enable **Azure App Configuration** (centralized settings + feature flags with runtime refresh) for altinn-authentication, following the pattern AccessManagement uses in [altinn-authorization-tmp](https://github.com/Altinn/altinn-authorization-tmp).
## Key insight: the app-side support already exists
The host boots via `AltinnHost.CreateWebApplicationBuilder("authentication", args)` (`AuthenticationHost.cs`) from **`Altinn.Authorization.ServiceDefaults`** (currently 5.5.0), and that library already ships the full App Configuration bootstrap ([`AltinnServiceDefaultsExtensions.AddAltinnAppConfiguration`](https://github.com/Altinn/altinn-authorization-utils/blob/main/src/Altinn.Authorization.ServiceDefaults/src/ServiceDefaults/Microsoft.Extensions.Hosting/AltinnServiceDefaultsExtensions.cs) + [`RefreshAppConfigurationHostedService`](https://github.com/Altinn/altinn-authorization-utils/blob/main/src/Altinn.Authorization.ServiceDefaults/src/ServiceDefaults/AppConfiguration/RefreshAppConfigurationHostedService.cs)). `Microsoft.Azure.AppConfiguration.AspNetCore` 8.5.0 is already in our dependency closure via ServiceDefaults. **No application code changes are needed** — activation is purely configuration-driven:
| Config key | Meaning | Default |
|---|---|---|
| `Altinn:AppConfiguration:Endpoint` | Store URI (e.g. `https://appconf.azconfig.io`). Absent ⇒ feature off (today's state, logs "skipping") | *(unset)* |
| `Altinn:AppConfiguration:Label` | Label to select. **Required once Endpoint is set — startup throws without it** | *(unset)* |
| `Altinn:AppConfiguration:FeatureFlags:Enable` | Also load feature flags from the store | `false` |
| `Altinn:AppConfiguration:Credentials:Environment:Enable` | Use `EnvironmentCredential` | `false` |
| `Altinn:AppConfiguration:Credentials:WorkloadIdentity:Enable` | Use `WorkloadIdentityCredential` | `true` |
| `Altinn:AppConfiguration:Credentials:ManagedIdentity:Enable` | Use `ManagedIdentityCredential` (system-assigned) | `true` |
What the bootstrap then does:
- Selects keys with filters: the configured label, the service name (**`authentication`**), the environment (e.g. `at22`), and `{env}-{service}` (e.g. `at22-authentication`). Same filters for feature flags.
- Resolves Key Vault references in the store with the same credential chain.
- Registers refresh for **all** keys and polls every **5 minutes** (`RefreshAppConfigurationHostedService`) — feature flags and config toggle at runtime without redeploy (note: only `IOptionsMonitor`/`IFeatureManager` consumers see changes; values captured at startup or via plain `IOptions` singletons do not).
## What AccessManagement does (reference implementation)
- **App**: nothing special — same ServiceDefaults bootstrap (`AltinnHost.CreateWebApplicationBuilder("access-management", ...)` in [`AccessManagementHost.cs`](https://github.com/Altinn/altinn-authorization-tmp/blob/main/src/apps/Altinn.AccessManagement/src/Altinn.AccessManagement/AccessManagementHost.cs)) + `services.AddFeatureManagement()` (reads from `IConfiguration`, so flags from the store materialize automatically — we already have this call too).
- **Store**: a single shared store in the authorization *hub* (`appconf{hub_suffix}`), values separated per environment by **label** (`at22`, `tt02`, `prod`, …).
- **RBAC** ([`infra/modules/rbac/main.tf`](https://github.com/Altinn/altinn-authorization-tmp/blob/main/infra/modules/rbac/main.tf)): the app's user-assigned managed identity gets **`App Configuration Data Reader`** on the store (`use_app_configuration = true` from [`src/apps/Altinn.AccessManagement/infra/main.tf`](https://github.com/Altinn/altinn-authorization-tmp/blob/main/src/apps/Altinn.AccessManagement/infra/main.tf)), with workload-identity federation for the pod's service account.
- **Seeding** ([`infra/modules/appsettings/main.tf`](https://github.com/Altinn/altinn-authorization-tmp/blob/main/infra/modules/appsettings/main.tf) + [`infra/deploy/spoke/appsettings.tf`](https://github.com/Altinn/altinn-authorization-tmp/blob/main/infra/deploy/spoke/appsettings.tf)): terraform manages `azurerm_app_configuration_key` (kv + Key Vault references) and `azurerm_app_configuration_feature` entries with env labels. Feature flags use `lifecycle { ignore_changes = [enabled] }` so toggling in the portal isn't reverted by the next apply.
## Tasks for altinn-authentication
- [ ] **Decide store**: reuse the authorization hub store vs. an App Configuration store in the authentication subscription. (The reader code doesn't care; RBAC + terraform ownership does.)
- [ ] **Infra/RBAC**: grant the authentication workload identity `App Configuration Data Reader` on the chosen store; ensure workload-identity federation (or system-assigned MI) is in place for the pods.
- [ ] **Deployment config** (lives in the ops/deploy setup, not this repo): set `Altinn__AppConfiguration__Endpoint` and `Altinn__AppConfiguration__Label` per environment. ⚠️ Setting Endpoint without Label crash-loops the pod at startup.
- [ ] **Seed settings/flags** with env labels + `authentication` key filters (terraform, following the appsettings-module pattern).
- [ ] **Feature flags**: set `Altinn:AppConfiguration:FeatureFlags:Enable = true` and migrate the local `FeatureManagement` section (`AuditLog`, `SystemUser` in `appsettings.json`) to feature flags in the store. `AddFeatureManagement()` needs no change.
- [ ] **Verify refresh**: flip a flag in the store and confirm it takes effect within ~5 min without redeploy; audit that flag consumers use `IFeatureManager`/`IOptionsMonitor` (not captured-at-startup values).
- [ ] **Local dev/tests**: no changes needed — without Endpoint the bootstrap logs "Missing Altinn:AppConfiguration:Endpoint - skipping" and everything behaves as today.
## Gotchas
- **Label is mandatory** once Endpoint is set — the ServiceDefaults bootstrap throws `InvalidOperationException` at startup otherwise.
- Refresh is `RegisterAll()` every 5 min — there is no sentinel key; every changed key refreshes.
- Key Vault **references** inside App Configuration resolve with the same credential chain — the identity also needs KV read access if we use them (separate from the existing `kvSetting` KeyVault config source, which stays as-is).
- The endpoint/label env-var injection for AccessManagement is not in the public repo (it sits in the deployment layer), so the exact injection mechanism for our pods needs to be confirmed with the ops setup.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with AuthenticationHost.cs and the Altinn.Authorization.ServiceDefaults App Configuration bootstrap, then compare the referenced AccessManagement Terraform files and confirm the deployment-layer injection setup. Done means choosing the store, configuring identity and RBAC, setting endpoint and label values, seeding authentication settings and flags, and verifying refresh without redeployment.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- azure, csharp, terraform
- Domain
- backend, cloud, infrastructure
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100