Guide: Integrate Tide in a C# WebForm Application
This guide will walk you through integrating Tide into a C# WebForm application to manage user authentication. Follow the steps below to set up the integration and customize the example for your project.
Prerequisites
Before starting, ensure you have:
- A Tide instance running locally or on a server.
- A realm and a client configured in Tide.
- Basic knowledge of C# and WebForms.
- .NET Framework SDK installed on your machine.
0: Set Up Tidecloak and Tide IDP
Before starting the integration, make sure you have both Tidecloak and the TIDE Identity Provider (IDP) set up and running. This is crucial for managing user authentication in your application. You can refer to the following setup guides:
- How to Set Up Tidecloak – In this guide, you will configure Tidecloak with the same values used in this tutorial (e.g.,
localhost:8000
for the Core app). - How to Set Up the TIDE IDP – This guide helps you configure the TIDE Identity Provider, which will serve as your authentication server.
By following these setup guides, you'll ensure your environment is ready to integrate with your WebForms app using the values provided in this tutorial. Please continue with the steps below to integrate Tide with your WebForm application.
1. Set Up the Development Environment
You need an IDE and tools to develop a WebForm project. You can use:
- Visual Studio : The recommended IDE for .NET development.
- .NET Framework SDK : Make sure to have the latest .NET Framework installed. You can download and install the required tools from theMicrosoft .NET website.
2. Create a New WebForm Application
This app will act as the UI and interact with the Core app for login and logout and account details
- Open Visual Studio .
- Create a New Project : Navigate to
File > New > Project
. - Select ASP.NET Web Forms Application .
- Name the project (e.g.,
Webforms
) and choose a location for the project files.
3. ASP.NET WebForms Application Setup
In the Default.aspx
page, create buttons for login and logout, which will redirect to the Core authentication app
Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebForms._Default" Async="true" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Welcome to WebFormsApp</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<h2>Welcome to the WebForms Application</h2>
<a href="/CoreApp/login">Login to Core App</a>
<br />
<a href="/CoreApp/logout">Logout from Core App</a>
<br />
<!-- Panel to display user info, hidden if cookies are not found -->
<asp:Panel ID="UserInfoPanel" runat="server" Visible="false">
<asp:Literal ID="UserInfo" runat="server" />
</asp:Panel>
<br />
</div>
</form>
</body>
</html>
In the code-behind of Default.aspx.cs
, implement the redirection to the Core app for login and logout.
Default.aspx.cs
using System;
using System.Web;
using System.Web.UI;
namespace WebForms
{
public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
if (Request.Cookies["UserName"] != null && Request.Cookies["Subject"] != null)
{
// Retrieve the values from the cookies
string userName = Request.Cookies["UserName"].Value;
string subject = Request.Cookies["Subject"].Value;
// Set the Literal content
UserInfo.Text = $"Welcome, {userName}! Your Session ID is {subject}.";
// Make the panel visible
UserInfoPanel.Visible = true;
}
else
{
// If cookies are not found, hide the panel
UserInfoPanel.Visible = false;
}
}
}
}
}
You should now have all the required files for the WebForms app, enabling it to handle redirection to the Core app for login, logout, and retrieving user details. These files ensure that the WebForms app can communicate effectively with the Core app
4. Create a New ASP.NET Core Authentication Application
This app will interact with the existing WebFrom application for managing authentication and user session details.
- Open Visual Studio .
- Create a New Project : Navigate to
File > Add > New Project
. - Select ASP.NET Core Web API .
- Name the project (e.g.,
CoreApp
) and choose a location for the project files. The location should be the same as the WebForm application
5. Install Required NuGet Packages
Install the following packages for OpenID Connect and Keycloak interaction:
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
Install-Package Microsoft.AspNetCore.Authentication.OpenIdConnect
Install-Package Microsoft.IdentityModel.Protocols.OpenIdConnect
6. ASP.NET Application Setup
In the Core project, configure Keycloak authentication using OpenID Connect.
Program.cs
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using System.Security.Claims;
var builder = WebApplication.CreateBuilder(args);
// Set the application to run on port 8000
builder.WebHost.UseUrls("http://localhost:8000");
// Configure logging
builder.Logging.ClearProviders();
builder.Logging.AddConsole(); // Ensure console logging is enabled
builder.Logging.SetMinimumLevel(LogLevel.Information); // Set minimum log level
// Register IHttpContextAccessor
builder.Services.AddHttpContextAccessor(); // <-- Add this line
builder.Services.AddRazorPages();
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowSpecificOrigins", policy =>
{
// Allow WebForms app or other clients
policy.WithOrigins("http://localhost:8000") // Your WebForms app URL
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials(); // Allow cookies/auth tokens
});
});
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.Authority = "http://localhost:8080/realms/myrealm"; // Keycloak Realm URL
options.ClientId = "myclient"; // Client ID from Keycloak
options.RequireHttpsMetadata = false; // Set to true in production with HTTPS
options.ResponseType = OpenIdConnectResponseType.Code; // Use authorization code flow
options.SaveTokens = true; // Save tokens in the authentication session
options.GetClaimsFromUserInfoEndpoint = true; // Fetch claims from the user info endpoint
// Define the scope of the access request
options.Scope.Add("profile");
options.Scope.Add("email");
// Token validation parameters
options.GetClaimsFromUserInfoEndpoint = true;
options.ClaimActions.MapJsonKey(ClaimTypes.Name, "preferred_username");
options.ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
options.ClaimActions.MapJsonKey("sub", "sub");
});
var app = builder.Build();
app.UseCors("AllowSpecificOrigins"); // Add CORS middleware here
app.UseStaticFiles();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
This Razor Page displays the UI for user login, usually containing a button or link that triggers the authentication flow. The user will be redirected to the identity provider for login when they interact with this page.
login.cshtml
@page
@model YourNamespace.Pages.LoginModel
@{
ViewData["Title"] = "Login";
}
<h2>Login</h2>
<p>You are being redirected to the login page...</p>
@section Scripts {
<script>
window.location.href = "/Account/Login";
</script>
}
The code-behind for Login.cshtml
handles the authentication logic. It redirects the user to the authentication provider (e.g., OpenID Connect) to initiate the login process and manages any redirection URLs or state required for the login flow.
login.cshtml.cs
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace YourNamespace.Pages
{
public class LoginModel : PageModel
{
public IActionResult OnGet()
{
return Challenge(new AuthenticationProperties
{
RedirectUri = "/CoreApp/Callback"
}, OpenIdConnectDefaults.AuthenticationScheme);
}
}
}
This page presents the UI for the logout process. It may contain a confirmation button or message indicating that the user will be logged out and redirected from the application.
Logout.cshtml
@page
@model YourNamespace.Pages.LogoutModel
@{
ViewData["Title"] = "Logout";
}
<h2>Logout</h2>
<p>You are being logged out...</p>
@section Scripts {
<script>
window.location.href = "/Account/Logout";
</script>
}
The code-behind for Logout.cshtml
is responsible for managing the logout process. It clears the user's authentication cookies and signs them out from the identity provider, redirecting them to a post-logout URL, often the home page.
Logout.cshtml.cs
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace YourNamespace.Pages
{
public class LogoutModel : PageModel
{
public IActionResult OnGet()
{
// Remove both cookies
Response.Cookies.Delete("UserName");
Response.Cookies.Delete("Subject");
// Sign the user out from both Cookie and OpenID Connect schemes
return SignOut(new AuthenticationProperties
{
RedirectUri = "http://localhost:8000"
}, CookieAuthenticationDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme);
}
}
}
This page serves as the landing point after a successful login from the identity provider. It may show a loading message or a redirect notice, indicating the user is being authenticated.
Callback.cshtml
@page
@model YourNamespace.Pages.CallbackModel
@{
}
The code-behind for Callback.cshtml
processes the response from the identity provider after login. It retrieves tokens or user claims, validates the response, and then redirects the user to their intended page within the application.
Callback.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Security.Claims;
using System.Text.Json;
namespace YourNamespace.Pages
{
public class CallbackModel : PageModel
{
public IActionResult OnGet()
{
if (User.Identity.IsAuthenticated)
{
var claimsIdentity = User.Identity as ClaimsIdentity;
if (claimsIdentity != null)
{
// Retrieve user claims
string userName = User.Identity.Name ?? "No Username Claim";
string subject = User.FindFirst("sub")?.Value ?? "No Subject Claim";
// Store UserName in a cookie
Response.Cookies.Append("UserName", userName, new CookieOptions
{
Expires = DateTimeOffset.Now.AddMinutes(30), // Cookie expires in 30 minutes
HttpOnly = false, // Allow access from client-side if needed
IsEssential = true // Mark cookie as essential
});
// Store Subject in a separate cookie
Response.Cookies.Append("Subject", subject, new CookieOptions
{
Expires = DateTimeOffset.Now.AddMinutes(30), // Cookie expires in 30 minutes
HttpOnly = false, // Allow access from client-side if needed
IsEssential = true // Mark cookie as essential
});
// Redirect to Default.aspx in the WebForms parent app
string redirectUrl = "http://localhost:8000/Default.aspx";
return Redirect(redirectUrl);
}
}
// If not authenticated, return an error message
return new JsonResult(new { Error = "User is not authenticated" });
}
}
}
This page displays the logged-in user's details, such as their username, email, and any other relevant information fetched from the authentication system. It serves as a user profile page.
UserDetails.cshtml
@page
@model CoreApp.Pages.UserDetailsModel
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>User Details</title>
</head>
<body>
<h2>User Details</h2>
@if (User.Identity.IsAuthenticated)
{
<p><strong>Username:</strong> @Model.UserName</p>
<p><strong>Subject (sub):</strong> @User.FindFirst("sub")?.Value</p>
}
else
{
<p>You are not logged in.</p>
}
<a href="/">Back</a>
</body>
</html>
The code-behind for UserDetails.cshtml
retrieves the user's information from cookies or the authentication provider and populates the UI with the user's details. It may also handle interactions like updating or refreshing user data.
UserDetails.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Security.Claims;
using System.Text.Json;
namespace CoreApp.Pages
{
public class UserDetailsModel : PageModel
{
public string UserName { get; set; }
public string Subject { get; set; }
public IActionResult OnGet()
{
if (User.Identity.IsAuthenticated)
{
var claimsIdentity = User.Identity as ClaimsIdentity;
if (claimsIdentity != null)
{
// Retrieve claims
UserName = User.Identity.Name ?? "No Username Claim";
Subject = User.FindFirst("sub")?.Value ?? "No Subject Claim";
// Create a JSON object
var userDetails = new
{
UserName,
Subject
};
// Serialize the JSON object to string
var userDetailsJson = JsonSerializer.Serialize(userDetails);
// Store the JSON response in a cookie
Response.Cookies.Append("UserDetails", userDetailsJson, new CookieOptions
{
Expires = DateTimeOffset.Now.AddDays(1) // Cookie expires in 1 day
});
// Return the same JSON response
return new JsonResult(userDetails);
}
}
return new JsonResult(new { Error = "User is not authenticated or claims not available" });
}
}
}
You should now have all the required files for the Core app to manage the login, logout, callback, and user details functionalities. These pages handle the complete authentication flow, from initiating login and logout to processing user details and redirecting users. With this setup in place, your Core app is ready to become a sub-application of the WebForms app, seamlessly integrating with it to handle authentication and user management.
7. Modify applicationhost.config
to Set Up the CoreApp as a Sub-Application
The next step is to configure the applicationhost.config
file for IIS Express. This file tells IIS how to handle the relationship between the WebForms main app and the CoreApp sub-app.
7.1 Find and Edit the applicationhost.config
- Navigate to the
.vs
folder in the root of your solution directory:- Example:
projectRoot/.vs/config/applicationhost.config
.
- Example:
- Open
applicationhost.config
with a text editor likeNotepad++ .
7.2 Add an Application Pool for CoreApp
- Locate the
<applicationPools>
section in theapplicationhost.config
file. - Add a new application pool for AuthServer. Ensure that the
managedRuntimeVersion
is empty since AuthServer is unmanaged.
<applicationPools>
<add name="TideAuthAppPool" managedRuntimeVersion="" managedPipelineMode="Classic" autoStart="true" />
</applicationPools>
7.3 Add CoreApp as a Sub-Application
- Under the
<sites>
section, locate your WebFormsApp configuration. - Add the AuthServer as a sub-application by creating a new
<application>
entry inside the WebFormsApp app's<site>
.
<sites>
<site name="WebFormsApp" id="1">
<application path="/" applicationPool="Clr4IntegratedAppPool">
<virtualDirectory path="/" physicalPath="C:\Users\test\Desktop\WebFormsApp" />
</application>
<!-- ASP.NET Core Application as Sub-Application -->
<application path="/coreapp" applicationPool="TideAuthAppPool">
<virtualDirectory path="/" physicalPath="C:\Users\test\Desktop\CoreApp\bin\Release\net8.0\publish" />
</application>
<bindings>
<binding protocol="http" bindingInformation="*:8000:localhost" />
</bindings>
</site>
</sites>
8. Publish CoreApp to the Publish Folder
Before running the WebForms app with CoreApp as a sub-application, publish the CoreApp project:
- Right-click the AuthServer project inSolution Explorer .
- ChoosePublish .
- SelectFolder as the target, and choose the folder you set in the
applicationhost.config
(e.g.,C:\Users\test\Desktop\CoreApp\bin\Release\net8.0\publish
). - Publish the application.
9. Run and Test the Applications
- Run theWebFormsApp project using IIS Express in Visual Studio.
- Navigate to
http://localhost:8000
to access the WebForms app. - Navigate to
http://localhost:8000/coreapp
to test the CoreApp running as a sub-application.
You should see the AuthServer being served as a sub-app under the WebFormsApp .
🎉 Congratulations! You've successfully integrated Tide with your C# WebForm application! With this setup, your WebForm app now has secure authentication powered by Tide's identity management.