{"id":21477594,"url":"https://github.com/adamfoneil/apiauthdemo","last_synced_at":"2026-03-19T20:33:19.459Z","repository":{"id":234794233,"uuid":"789471070","full_name":"adamfoneil/ApiAuthDemo","owner":"adamfoneil","description":"minimal Blazor app showing auth API interactions","archived":false,"fork":false,"pushed_at":"2024-05-25T11:08:18.000Z","size":693,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-09T05:02:01.917Z","etag":null,"topics":["authentication","authorization","blazor-webassembly","radzen-blazor-components"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adamfoneil.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-04-20T16:37:15.000Z","updated_at":"2024-05-25T11:08:21.000Z","dependencies_parsed_at":"2024-04-20T20:25:23.668Z","dependency_job_id":"2188a9b0-7251-44e4-9bc3-41b38599bc3d","html_url":"https://github.com/adamfoneil/ApiAuthDemo","commit_stats":null,"previous_names":["adamfoneil/apiauthdemo"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adamfoneil%2FApiAuthDemo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adamfoneil%2FApiAuthDemo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adamfoneil%2FApiAuthDemo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adamfoneil%2FApiAuthDemo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adamfoneil","download_url":"https://codeload.github.com/adamfoneil/ApiAuthDemo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243997127,"owners_count":20380981,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","authorization","blazor-webassembly","radzen-blazor-components"],"created_at":"2024-11-23T11:14:28.631Z","updated_at":"2026-01-03T15:32:55.500Z","avatar_url":"https://github.com/adamfoneil.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"This was inspired by another Blazor project [sample-blazor8-auth-ms](https://github.com/ripteqdavid/sample-blazor8-auth-ms). I wanted to make something based on my opinions on certain things, and that gets closer to a production use case -- with some EF Core action and a minimal CRUD experience with a [Radzen](https://blazor.radzen.com/) grid. This wouldn't be possible without Dave's original project, as there were some critical Blazor WebAssembly parts I couldn't figure out on my own. I do find it surprising that there's not really a pit of success when implementing a Blazor API that requires authentication. The parts I was missing incidentally, were [CookieHandler](https://github.com/adamfoneil/ApiAuthDemo/blob/master/ApiAuthDemo/ApiAuthDemo.Client/CookieHandler.cs) and some special HttpClient initialization -- both in the [Client/Program.cs](https://github.com/adamfoneil/ApiAuthDemo/blob/master/ApiAuthDemo/ApiAuthDemo.Client/Program.cs#L14-L20) and the main app [Program.cs](https://github.com/adamfoneil/ApiAuthDemo/blob/master/ApiAuthDemo/ApiAuthDemo/Program.cs#L28-L35). I still don't understand why this code needs to be in both projects.\n\nPart of the issue is I was trying really hard at first to use [Refit](https://github.com/reactiveui/refit) for the API client. I love Refit's productivity, but it doesn't align with Blazor authentication because Refit wants to treat the HttpClient as a singleton. Singletons don't have access to ASP.NET authentication infrastructure, so I kept running into roadblocks there. I went my own way with [ApiClientBase](https://github.com/adamfoneil/ApiAuthDemo/blob/master/ApiClientBaseLibrary/ApiClientBase.cs), and its derived class [ApiClient](https://github.com/adamfoneil/ApiAuthDemo/blob/master/ApiAuthDemo/ApiAuthDemo.Client/ApiClient.cs). This is also a little different from Dave's project because I wanted my API calls to be exposed through an interface, not called directly on `HttpClient`. (In this case, when I say \"interface\" I mean a class that's encapsulating some stuff, not an actual `interface`, which I feel is not necessary here.)\n\nSpeaking of the API, the endpoints are here in [Program.Endpoints.cs](https://github.com/adamfoneil/ApiAuthDemo/blob/master/ApiAuthDemo/ApiAuthDemo/Program.Endpoints.cs). This is using minimal APIs and EF Core for the underlying data access. The one little oddity you might notice is this [Save](https://github.com/adamfoneil/ApiAuthDemo/blob/master/ApiAuthDemo/ApiAuthDemo/Extensions/DbSetExtensions.cs) extension method used [here](https://github.com/adamfoneil/ApiAuthDemo/blob/master/ApiAuthDemo/ApiAuthDemo/Program.Endpoints.cs#L27). I'd prefer to use a single POST to handle both inserts and updates.\n\nThis all comes together in [Widgets/Page.razor](https://github.com/adamfoneil/ApiAuthDemo/blob/master/ApiAuthDemo/ApiAuthDemo.Client/Pages/Widgets/Page.razor), which has the Radzen grid and its infrastructure. I do a couple unique things here to minimize boilerplate -- all centered around this [GridHelper](https://github.com/adamfoneil/ApiAuthDemo/blob/master/ApiAuthDemo/ApiAuthDemo.Client/Pages/Widgets/GridHelper.cs). This is ultimately based on some work of mine in another project that encapsulates some opinionated ways I use Radzen: [Radzen.Components](https://github.com/adamfoneil/LiteInvoice3/tree/master/Radzen.Components). This gives the \"are you sure\" dialog when deleting a row, and simplifies the core CRUD operations as much as possible IMO.\n\nNote, this project contains all the regular Blazor template stuff, which I am too impatient to remove.\n\n![image](widget-crud.gif)\n\n# Time Zone Support\nMy eventual use case for this in production is in a multi-tenant app where auditing and permissions depend on knowing custom properties of the user, in this case [ApplicationUser](https://github.com/adamfoneil/ApiAuthDemo/blob/master/ApiAuthDemo.Data/ApplicationUser.cs). One way to demo custom user properties (without adding true multi-tenant support to this app), was to add time zone support to the [auditing features](https://github.com/adamfoneil/ApiAuthDemo/blob/master/ApiAuthDemo.Data/ApplicationDbContext.cs#L29).\n\nI've accomplished this by:\n- adding a `TimeZoneId` property to `ApplicationUser`\n- adding a custom claims factory [DbClaimsPrincipalFactory](https://github.com/adamfoneil/ApiAuthDemo/blob/master/ApiAuthDemo/ApiAuthDemo/Services/DbClaimsPrincipalFactory.cs) and added it to the DI container in [Program.cs](https://github.com/adamfoneil/ApiAuthDemo/blob/master/ApiAuthDemo/ApiAuthDemo/Program.cs#L25).\n- adding a `TimeZoneId` property to my db context [ApplicationDbContext](https://github.com/adamfoneil/ApiAuthDemo/blob/master/ApiAuthDemo.Data/ApplicationDbContext.cs#L13). This is set within the endpoint handler [here](https://github.com/adamfoneil/ApiAuthDemo/blob/master/ApiAuthDemo/ApiAuthDemo/Program.Endpoints.cs#L26). The user's time zone is read when parsing [user info](https://github.com/adamfoneil/ApiAuthDemo/blob/master/ApiAuthDemo/ApiAuthDemo/Program.Endpoints.cs#L43) from incoming requests.\n- [adding time zone field](https://github.com/adamfoneil/ApiAuthDemo/commit/f1965eb316915ac79b85b73d59c8d5909b4c3f3e) to the Profile Manage page.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadamfoneil%2Fapiauthdemo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadamfoneil%2Fapiauthdemo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadamfoneil%2Fapiauthdemo/lists"}