Skip to content

Configuration ​

Configuration comes from appsettings.json, environment-specific overrides (appsettings.Development.json), user secrets, and environment variables, in the standard ASP.NET Core precedence (env vars override user secrets, which override appsettings.{Environment}.json, which overrides appsettings.json).

Secrets are deliberately not committed: the tracked appsettings*.json carry only logging levels and DevAuth. Locally that gap is filled with dotnet user-secrets set (see Development setup); in a container it is filled with environment variables, using __ as the section separator (ConnectionStrings__SchulyDatabase).

Database connection string ​

The SchulyDbContext reads the connection string named SchulyDatabase (ConnectionStrings:SchulyDatabase), wired up in DatabaseExtensions. Retry-on-failure is enabled for transient Postgres errors.

Provide it via configuration or an environment variable, e.g.:

sh
export ConnectionStrings__SchulyDatabase="Host=localhost;Port=2406;Database=schuly-dev;Username=postgres;Password=..."

OIDC authentication ​

JWT bearer authentication validates tokens against the configured OIDC authority (AddSchulyAuthentication):

KeyPurpose
Oidc:AuthorityOIDC authority (Keycloak) used to validate bearer tokens.
Oidc:RequireHttpsMetadataWhether HTTPS metadata is required (default true).
Oidc:AdminClientId / Oidc:AdminClientSecretOptional. Confidential client whose service account holds the realm-management manage-users role, used to delete the identity-provider user on account deletion. Unset means the backend deletes only its own data and logs a warning.

Token claim mapping:

  • name → display name (NameClaimType).
  • groups claim → role (RoleClaimType). Group values map to the application roles Student, Teacher, and Administrator.
  • Audience validation is disabled (ValidateAudience = false).

On the first valid token for an unknown user, the API syncs the user from the token (AddUserSync / IUserService).

Development auth (DevAuth) ​

In Development you can enable a local fake-OIDC path instead of a real IdP. With DevAuth:Enabled = true (see appsettings.Development.json), the API trusts tokens minted by /api/dev/token, signed with a symmetric key, using DevAuth:Issuer (default schuly-dev). No external identity provider is contacted. Do not enable DevAuth in production.

OpenAPI document ​

Oidc:Authority is also read when the OpenAPI document is generated, to advertise the OAuth2 authorization-code flow. It is required even when DevAuth replaces the real identity provider: without it /openapi/v1.json returns 500 (Oidc:Authority not configured) and the Scalar UI comes up empty, since it renders that document. Oidc:ClientId prefills the client id in the reference UI's authorization dialog.

Document storage (S3) ​

Document and avatar blobs go to an S3-compatible bucket (SeaweedFS in the bundled stacks), configured under S3::

KeyPurpose
S3:EndpointS3 endpoint, e.g. http://localhost:8333.
S3:BucketBucket name.
S3:AccessKey / S3:SecretKeyCredentials. Must match the SeaweedFS s3-config.json.
S3:UsePathStyletrue for SeaweedFS and most self-hosted S3 implementations.

Avatar URL signing ​

KeyPurpose
Avatar:SigningKeyHMAC key for short-lived signed avatar URLs. Generate with openssl rand -hex 32.

The database stores only a bare blob key; a signed capability URL is minted per access. The key is read lazily, so a missing value surfaces as Avatar:SigningKey is not configured. the first time an avatar URL is signed rather than at startup.

Retention ​

A hosted service sweeps expired data once a day, five minutes after startup and every 24 hours after that. It reuses the same purge code as DELETE /api/auth/me, so a swept row leaves nothing behind in Postgres or S3.

KeyPurpose
Retention:EnabledWhether the sweep runs at all (default true).
Retention:MonthsAfterLeaveCached school data is dropped for school users whose LeaveDate is older than this (default 6).
Retention:MonthsInactiveAccounts with no authenticated request for this long are deleted (default 12).

"Inactive" is measured with ApplicationUser.LastSeenAt, stamped from the JWT validation pipeline at most once per hour so a busy client does not write on every request. An account that has never been stamped falls back to its CreatedAt.

The sweep never deletes identity-provider users, only what this backend stores, so an account swept for inactivity can sign in again and resync from scratch.

Push notifications (Firebase) ​

The notification outbox (grades, absences, agenda changes) is drained by a background service and delivered through Firebase Cloud Messaging.

KeyPurpose
Firebase:ServiceAccountJsonBase64 of the Firebase service-account JSON.
Firebase:ServiceAccountPathPath to the service-account JSON file, as an alternative to the inline value.

With neither set, the backend logs once at startup that push notifications are disabled and no-ops from then on, so deployments without Firebase keep working. The outbox is still drained in that case, so the table stays bounded even with push turned off.

Authorization policy ​

The default (fallback) policy requires an authenticated user for every endpoint (AddSchulyAuthorization). Endpoints opt out with [AllowAnonymous]. The anonymous /api/app surface (e.g. the school-systems catalog and static logo assets under wwwroot) is reachable without auth so the app can load its login catalog. Admin plugin endpoints under /api/plugins require the Administrator role.

Plugins ​

Plugin host configuration (registry URL, desired-plugin file, directories) is described in Plugin management.

Logging ​

Log levels are set under Logging:LogLevel in appsettings.json. The Development override raises HTTP logging to Information for request tracing.