ASP.NET Core 10 provides Google sign-in, server-managed cookie sessions, and durable learner progress. The Next.js app forwards /api/platform/* to this service through its server-only BFF. Anonymous browser practice stays available when the backend or Google credentials are not configured.
Install a supported .NET 10 SDK and keep its runtime on the latest servicing patch. From the repository root:
dotnet restore backend/ProCoderJi.slnx --locked-mode
dotnet run --project backend/src/ProCoderJi.ApiThe development profile listens on http://localhost:5100. Set DOTNET_API_URL=http://localhost:5100 in the frontend's .env.local and run Next.js separately. This URL is server configuration and must never use a NEXT_PUBLIC_ prefix.
SQLite is the default development provider. The API creates and migrates backend/src/ProCoderJi.Api/Data/procoderji.db automatically in Development. Users, progress, and ASP.NET Data Protection keys survive process restarts in that database. Generated databases and build output are ignored by Git.
Create a Google OAuth Web application client and register this exact local authorized redirect URI:
http://localhost:3000/api/platform/signin-google
The callback belongs to the frontend origin, which forwards it to ASP.NET Core. The backend's PublicOrigin configuration defaults to http://localhost:3000 in the checked-in development settings. It determines OAuth callback URLs, allowed return paths, mutation-origin checks, and cookie security. Set it to the actual HTTPS frontend origin in production.
Keep credentials in .NET user secrets locally:
dotnet user-secrets set --project backend/src/ProCoderJi.Api "Authentication:Google:ClientId" "YOUR_CLIENT_ID"
dotnet user-secrets set --project backend/src/ProCoderJi.Api "Authentication:Google:ClientSecret" "YOUR_CLIENT_SECRET"Deployment environments can instead supply Authentication__Google__ClientId and Authentication__Google__ClientSecret. These are backend secrets; never commit them or expose them through frontend environment variables. Without both values, the session endpoint reports configured: false and the Google challenge returns 503 while guest practice remains available.
Users are linked by Google's stable provider subject, never by matching email addresses. The app stores its own internal UUID plus the provider subject and current email/display name. Cookies contain the internal account identity; provider access/refresh tokens are not retained. Sessions are HttpOnly, SameSite=Lax, and Secure on HTTPS deployments.
| Endpoint | Behavior |
|---|---|
GET /api/platform/auth/session |
Returns { configured, user, csrfToken } and issues the antiforgery cookie. |
GET /api/platform/auth/google?returnTo=/learn |
Begins Google OAuth; return URLs stay under approved learning/account paths on PublicOrigin. |
/api/platform/signin-google |
OAuth middleware callback; requires valid state/correlation. |
POST /api/platform/auth/logout |
Clears the session cookie; requires current antiforgery token and same-origin request. |
GET /api/platform/progress |
Returns { attempts, generation } for the authenticated account. |
POST /api/platform/progress |
Accepts { attempts, generation }; validates questions/answers and inserts immutable attempts idempotently. |
DELETE /api/platform/progress |
Deletes the account's attempts and rotates its generation atomically; returns { generation }. |
Progress requests include x-account-id matching the signed-in user to reject account-switch races. Mutations also require Origin matching PublicOrigin and X-CSRF-Token obtained from the current session response. A token issued before sign-in/account change must be refreshed. Private responses use private, no-store; the BFF preserves cookie and redirect headers.
Attempts store raw question IDs and selected answers, not trusted scores. The backend validates against shared/practice/questions.json, which is copied into the API output as Data/questions.json. Grading remains reproducible from the canonical bank. This introductory practice is not a proctored or hiring assessment.
Serializable transactions and a state concurrency token serialize sync/reset races. A reset rotates the generation, so old devices cannot silently restore deleted cloud history. Attempts are keyed by account plus session UUID, and the newest 100 are retained per account. Requests are limited to 100 attempts and 256 KiB.
Neon PostgreSQL is the selected hosted database. Follow Neon setup for the runtime and migration connections. The other providers remain available for contributors.
Choose one provider per deployment. These are operator settings; students use the same account and progress API with every provider. SQLite remains the default for development without Docker.
Database__Provider |
Storage | Required configuration |
|---|---|---|
Sqlite |
Local SQLite file through EF Core | Optional ConnectionStrings__Platform; defaults to Data/procoderji.db. |
Postgres |
PostgreSQL through EF Core/Npgsql | ConnectionStrings__Platform with a PostgreSQL connection string. |
SqlServer |
Microsoft SQL Server through EF Core | ConnectionStrings__Platform with a SQL Server connection string. |
MongoDb |
MongoDB through the official .NET driver | MongoDB URI in ConnectionStrings__Platform, plus Database__Name. |
The API, migrations, and selected database can run together with Docker Compose, in separate containers with persistent database volumes. Follow Container setup. Vercel container deployments need an external durable database; Compose volumes are for a Docker host. Changing providers does not copy existing data or sessions between databases.
Example backend-only PostgreSQL configuration:
Database__Provider=Postgres
ConnectionStrings__Platform=Host=YOUR_HOST;Database=YOUR_DATABASE;Username=YOUR_USER;Password=YOUR_PASSWORD
PublicOrigin=https://YOUR_FRONTEND_ORIGIN
For SQL Server, use Database__Provider=SqlServer and a connection string such as
Server=YOUR_HOST;Database=procoderji;User Id=YOUR_USER;Password=YOUR_PASSWORD;Encrypt=True;TrustServerCertificate=False.
For MongoDB, use Database__Provider=MongoDb, Database__Name=procoderji, and
ConnectionStrings__Platform=mongodb+srv://YOUR_USER:YOUR_PASSWORD@YOUR_HOST/.
Keep actual credentials in user-secrets or the hosting secret store.
Run migrations deliberately before starting production replicas:
dotnet run --project backend/src/ProCoderJi.Api --no-launch-profile -- --migrateThis command uses the current environment's database/provider settings and exits after
provisioning. It applies EF migrations for relational databases and creates MongoDB's unique
Google-subject index for MongoDB. Development and Testing provision automatically; production
does so only for --migrate or explicit Database__MigrateOnStartup=true. Back up the database
before schema changes. Shared relational migrations contain provider-specific SQL where needed;
keep both the default and SQL Server model snapshots current and run the provider checks.
Data Protection keys are stored in the selected application database with a stable application name, so replicas using the same database can decrypt sessions. Relational storage uses EF Core; MongoDB uses a key collection and an XML repository. Protect database access, backups, and key material at rest, and isolate key rings between deployments. Restrict direct network access to the backend and use the frontend BFF as the public entry point. Configure infrastructure rate limits, HTTPS, operational monitoring, and backups before public account launch.
MongoDB stores each account's progress generation, revision and 100 retained attempts in one document. Conditional atomic writes preserve reset and immutable-attempt behavior without a replica set or multi-document transactions. See ADR 0003.
Razorpay or Paytm integration is planned for a later billing slice. No payment, subscription activation, or live checkout is implemented in this API yet.
dotnet test backend/ProCoderJi.slnx --no-restoreThe default suite creates isolated SQLite databases and uses test-only authentication registered by WebApplicationFactory in the test assembly. The API has no test-header login path. Tests cover anonymous behavior, Google challenge URL construction, CSRF/origin checks, account isolation, immutable sync, generation resets, stale requests, question validation, and retention.
To run the same API contract tests against a disposable server, set exactly one of
PROCODERJI_TEST_POSTGRES, PROCODERJI_TEST_SQLSERVER, or PROCODERJI_TEST_MONGODB to its
administrative test connection string. Each factory creates and drops only its randomly named
test schema/database. The MongoDB setting also enables its persistence and Data Protection
integration tests, which otherwise report skipped. Never use a production database for these
tests. The backend-container workflow exercises all three servers and the packaged API.
A real Google login still needs operator-provided credentials and a live callback check.