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)
- Your app calls
Challenge(...)
with the OpenID Connect scheme. - The user is redirected to TideCloak (Keycloak) to authenticate.
- TideCloak redirects back to
/signin-oidc
with an authorization code. - The OpenID Connect handler exchanges the code for ID/Access tokens and issues an auth cookie for your site.
- You now have claims such as
preferred_username
,email
, and an ID token stored (ifSaveTokens = 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 setTokenValidationParameters.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