Skip to main content

Discussion: Understanding TideCloak in CSharp

This page explains what happens end‑to‑end when you wire ASP.NET Core to TideCloak via OpenID Connect.


1) Browser sign‑in (Authorization Code flow)

  1. Your app calls Challenge(...) with the OpenID Connect scheme.
  2. The user is redirected to TideCloak (Keycloak) to authenticate.
  3. TideCloak redirects back to /signin-oidc with an authorization code.
  4. The OpenID Connect handler exchanges the code for ID/Access tokens and issues an auth cookie for your site.
  5. You now have claims such as preferred_username, email, and an ID token stored (if SaveTokens = true).

APIs: for APIs, prefer AddJwtBearer so requests carry a bearer token instead of cookies.


2) Claims, roles, and policies

  • Realm roles arrive under realm_access.roles in the access token.
  • Client roles arrive under resource_access.<clientId>.roles.
  • Map roles to ClaimTypes.Role and set TokenValidationParameters.RoleClaimType = "role" (or "roles") so [Authorize(Roles="admin")] works.

You can add an event to copy roles from the token JSON into role claims during authentication.

.AddOpenIdConnect(options =>
{
// ... authority, clientId, etc.
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = ctx =>
{
var identity = (System.Security.Claims.ClaimsIdentity)ctx.Principal!.Identity!;
// Example: ensure Name is set
identity.AddClaim(new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Name,
identity.FindFirst("preferred_username")?.Value ?? identity.Name ?? "user"));
return System.Threading.Tasks.Task.CompletedTask;
}
};
});

3) Cookies vs JWT (when to use which)

  • Cookie auth (shown in tutorials) is perfect for MVC/Razor Pages sites.
  • JWT bearer is ideal for APIs and SPAs that call your API:
builder.Services.AddAuthentication().AddJwtBearer("bearer", o =>
{
o.Authority = "http://localhost:8080/realms/myrealm";
o.Audience = "myclient";
o.RequireHttpsMetadata = false; // dev only
});
app.MapGet("/api/me", [Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes="bearer")] (System.Security.Claims.ClaimsPrincipal user) => Results.Ok(new { sub = user.FindFirst("sub")?.Value }));

You can run both in the same app: cookie for site pages, bearer for API endpoints.


4) Redirect URIs & common pitfalls

  • Add your app's callback URL in TideCloak client (e.g. http://localhost:8000/signin-oidc).
  • If you change the site base URL or run behind IIS/Reverse proxy, update the client settings & ForwardedHeadersOptions so OIDC callback URIs match.
  • In production: set RequireHttpsMetadata = true and use HTTPS everywhere.

5) Protecting pages and APIs

  • Use [Authorize] on controllers/pages.
  • Gate Razor UI fragments with User.Identity.IsAuthenticated.
  • Use policies to check specific roles/claims:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdmin", p => p.RequireRole("admin"));
});
// [Authorize(Policy = "RequireAdmin")]

6) Logout

SignOut with both cookie and OIDC schemes. For full single‑sign‑out, configure Keycloak's end‑session endpoint in OpenIdConnectOptions (optional).


7) Hosting on IIS

  • Install the ASP.NET Core Hosting Bundle on the server.
  • Publish with dotnet publish -c Release and point IIS site/app to the publish folder.
  • If pairing WebForms + Core, treat Core as a sub‑application under the same site (see WebForms tutorial).

8) Security checklist (prod)

  • RequireHttpsMetadata = true
  • Enforce HTTPS redirection and HSTS
  • Validate issuer/audience if you add JwtBearer
  • Rotate client secrets; limit scopes to what you need