.NET MVC C# Integration Guide
Complete guide for integrating .NET MVC applications with CAS SSO
Setup time: 10 minutes
Difficulty: Intermediate
.NET 6+
1. Project Setup
NuGet Packages
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
Package Manager Console
Install-Package System.IdentityModel.Tokens.Jwt
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
Install-Package Newtonsoft.Json
2. Configuration
appsettings.json
{
"CasSettings": {
"ServerUrl": "http://localhost:5000",
"ClientId": "your_client_id",
"ClientUsername": "your_client_username",
"ClientPassword": "your_client_password",
"SignatureSecret": "your_signature_secret",
"CallbackUrl": "http://yourapp.com/cas/callback",
"TokenExpiration": 120
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
Configuration Model
// Models/CasSettings.cs
public class CasSettings
{
public string ServerUrl { get; set; }
public string ClientId { get; set; }
public string ClientUsername { get; set; }
public string ClientPassword { get; set; }
public string SignatureSecret { get; set; }
public string CallbackUrl { get; set; }
public int TokenExpiration { get; set; } = 120;
}
3. CAS Client Service
// Services/CasClient.cs
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
public class CasClient
{
private readonly CasSettings _settings;
private readonly HttpClient _httpClient;
private readonly ILogger<CasClient> _logger;
public CasClient(CasSettings settings, HttpClient httpClient, ILogger<CasClient> logger)
{
_settings = settings;
_httpClient = httpClient;
_logger = logger;
}
public string GetLoginUrl(string returnUrl)
{
var loginUrl = $"{_settings.ServerUrl}/auth/login";
var callbackUrl = $"{_settings.CallbackUrl}?return_url={Uri.EscapeDataString(returnUrl)}";
return $"{loginUrl}?callback_url={Uri.EscapeDataString(callbackUrl)}";
}
public async Task<CasUser> ValidateTokenAsync(string token)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_settings.SignatureSecret);
var validationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
var principal = tokenHandler.ValidateToken(token, validationParameters, out var validatedToken);
// Extract user information from claims
var user = new CasUser
{
Username = principal.FindFirst(ClaimTypes.Name)?.Value,
Email = principal.FindFirst(ClaimTypes.Email)?.Value,
Role = principal.FindFirst(ClaimTypes.Role)?.Value,
FirstName = principal.FindFirst("first_name")?.Value,
LastName = principal.FindFirst("last_name")?.Value
};
return user;
}
catch (Exception ex)
{
_logger.LogError(ex, "Token validation failed");
throw new UnauthorizedAccessException("Invalid token");
}
}
public async Task<AuthResult> AuthenticateAsync(string username, string password)
{
var loginData = new
{
username,
password,
client_id = _settings.ClientId,
client_username = _settings.ClientUsername,
client_password = _settings.ClientPassword
};
var json = JsonConvert.SerializeObject(loginData);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync($"{_settings.ServerUrl}/api/sso/token", content);
if (response.IsSuccessStatusCode)
{
var responseContent = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<AuthResult>(responseContent);
return result;
}
throw new UnauthorizedAccessException("Authentication failed");
}
}
4. Models
// Models/CasUser.cs
public class CasUser
{
public string Username { get; set; }
public string Email { get; set; }
public string Role { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName => $"{FirstName} {LastName}";
public bool IsAdmin => Role == "admin";
public bool HasRole(string role) => Role == role;
}
// Models/AuthResult.cs
public class AuthResult
{
public string Token { get; set; }
public CasUser User { get; set; }
public DateTime ExpiresAt { get; set; }
}
5. Authentication Attribute
// Attributes/CasAuthAttribute.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
public class CasAuthAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var casUser = context.HttpContext.Session.GetString("CasUser");
var casToken = context.HttpContext.Session.GetString("CasToken");
if (string.IsNullOrEmpty(casUser) || string.IsNullOrEmpty(casToken))
{
var returnUrl = context.HttpContext.Request.Path + context.HttpContext.Request.QueryString;
var casClient = context.HttpContext.RequestServices.GetService<CasClient>();
var loginUrl = casClient.GetLoginUrl(returnUrl);
context.Result = new RedirectResult(loginUrl);
return;
}
// Validate token if needed
var user = Newtonsoft.Json.JsonConvert.DeserializeObject<CasUser>(casUser);
context.HttpContext.Items["CasUser"] = user;
base.OnActionExecuting(context);
}
}
// Attributes/CasRoleAttribute.cs
public class CasRoleAttribute : CasAuthAttribute
{
private readonly string[] _roles;
public CasRoleAttribute(params string[] roles)
{
_roles = roles;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
if (context.Result != null) return; // Already redirected
var user = context.HttpContext.Items["CasUser"] as CasUser;
if (user == null || !_roles.Contains(user.Role))
{
context.Result = new ForbidResult();
}
}
}
6. Controller Examples
Home Controller
// Controllers/HomeController.cs
using Microsoft.AspNetCore.Mvc;
public class HomeController : Controller
{
private readonly CasClient _casClient;
public HomeController(CasClient casClient)
{
_casClient = casClient;
}
[CasAuth]
public IActionResult Index()
{
var user = HttpContext.Items["CasUser"] as CasUser;
return View(user);
}
[CasRole("admin")]
public IActionResult Admin()
{
var user = HttpContext.Items["CasUser"] as CasUser;
return View(user);
}
public IActionResult Login(string returnUrl = "/")
{
var loginUrl = _casClient.GetLoginUrl(returnUrl);
return Redirect(loginUrl);
}
public async Task<IActionResult> CasCallback(string token, string return_url = "/")
{
try
{
var user = await _casClient.ValidateTokenAsync(token);
HttpContext.Session.SetString("CasUser", JsonConvert.SerializeObject(user));
HttpContext.Session.SetString("CasToken", token);
return Redirect(return_url);
}
catch
{
return RedirectToAction("Login");
}
}
public IActionResult Logout()
{
HttpContext.Session.Clear();
return RedirectToAction("Index");
}
}
7. Startup Configuration
// Program.cs (.NET 6+)
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Add services
builder.Services.AddControllersWithViews();
// Configure CAS settings
var casSettings = new CasSettings();
builder.Configuration.GetSection("CasSettings").Bind(casSettings);
builder.Services.AddSingleton(casSettings);
// Add HTTP client
builder.Services.AddHttpClient<CasClient>();
// Add session
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(120);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
// Add JWT authentication (optional, for API endpoints)
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(casSettings.SignatureSecret)),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
});
var app = builder.Build();
// Configure pipeline
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseSession();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
8. View Examples
Dashboard View
@@* Views/Home/Index.cshtml *@@
@model CasUser
@@{
ViewData["Title"] = "Dashboard";
}
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>Welcome, @Model.FullName!</h1>
<div class="card">
<div class="card-header">
<h3>User Information</h3>
</div>
<div class="card-body">
<p><strong>Username:</strong> @Model.Username</p>
<p><strong>Email:</strong> @Model.Email</p>
<p><strong>Role:</strong> @Model.Role</p>
<p><strong>First Name:</strong> @Model.FirstName</p>
<p><strong>Last Name:</strong> @Model.LastName</p>
</div>
</div>
<div class="mt-4">
@if(Model.IsAdmin)
{
<a href="@Url.Action("Admin")" class="btn btn-primary">Admin Panel</a>
}
<a href="@Url.Action("Logout")" class="btn btn-secondary">Logout</a>
</div>
</div>
</div>
</div>
Layout with Authentication
@@* Views/Shared/_Layout.cshtml *@@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - CAS Demo</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">CAS Demo</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
</ul>
<ul class="navbar-nav">
@@{
var casUser = Context.Session.GetString("CasUser");
if (!string.IsNullOrEmpty(casUser))
{
var user = Newtonsoft.Json.JsonConvert.DeserializeObject<CasUser>(casUser);
<li class="nav-item">
<span class="nav-link">Welcome, @user.FirstName!</span>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Logout">Logout</a>
</li>
}
else
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Login">Login</a>
</li>
}
}
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>