I have an asp net core application developed with .NET 5.
In this application, there is a controller (ACSController) that manages responses from an Identity Provider. The controller is the follow:
public class ACSController : Controller
{
private readonly IRequestRepository _requestRepository;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IIdpRepository _idpRepository;
private readonly IResponseRepository _responseRepository;
private readonly AppSessionViewModel _session;
public ACSController(IRequestRepository requestRepository, IHttpClientFactory httpClientFactory,
IIdpRepository idpRepository, IResponseRepository responseRepository, AppSessionViewModel session)
{
_requestRepository = requestRepository;
_httpClientFactory = httpClientFactory;
_idpRepository = idpRepository;
_responseRepository = responseRepository;
_session = session;
}
[HttpPost]
public async Task<IActionResult> IndexAsync(IFormCollection form)
{
var base64Response = form["SAMLResponse"].ToString();
var response = SAMLHelper.GetAuthnResponse(base64Response);
var cachedRequest = _requestRepository.Read();
var idpMetadata = await SamlHandler.DownloadIdPMetadata(_httpClientFactory,
_idpRepository.Read().OrganizationUrlMetadata);
var validationResult = ResponseValidator.ValidateAuthnResponse(response, cachedRequest, idpMetadata);
if (validationResult.IsSuccess)
{
_responseRepository.Write(response);
_session.Logged = true;
ViewData["UserInfo"] = CreateUserInfo(response);
return View();
}
else
{
ViewData["Message"] = validationResult.Error;
return View("Error");
}
}
[HttpPost]
public IActionResult Logout(IFormCollection form)
{
var base64Response = form["SAMLResponse"].ToString();
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
#region Utilities
private Dictionary<string, string> CreateUserInfo(AuthnResponse response)
{
if (response == null) throw new ArgumentException(nameof(response));
var attributes = response.GetAssertion().GetAttributeStatement().Items;
var userDictionary = new Dictionary<string, string>();
foreach (var attribute in attributes)
{
var attr = (AttributeType)attribute;
userDictionary.Add(attr.Name, (string)attr.AttributeValue.First());
}
return userDictionary;
}
#endregion
}
In my case, there is no problem when acs/index method is called by a POST request.
When I invoke the Logout procedure (from another controller) the request is sent correctly by my application but when IdP responds to me I obtain this response on my browser: https:///acs/logout?SAMLResponse=fVLBSsNAEL0L%2FYeS%2Bya72b … with a 405 error.
This is my Startup.cs :
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddSingleton<IRequestRepository, RequestRepository>();
services.AddSingleton<IIdpRepository, IdpRepository>();
services.AddSingleton<IResponseRepository, ResponseRepository>();
services.AddSingleton<AppSessionViewModel>();
services.AddHttpClient();
services.Configure<Spid>(Configuration.GetSection("Spid"));
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
//app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
//app.UseSession();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
And this is my launchSettings.json (I use SPID_Test as web browser):
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:15378",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
"SPID_Test": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Production"
},
"dotnetRunMessages": "true",
"applicationUrl": "http://localhost:5000"
}
Try to change [HttpPost]
to [HttpGet]
and then change IFormCollection form
to string SAMLResponse
.Model binding gets data from Query string parameters by default,so it can bind string SAMLResponse
with SAMLResponse=xxx
by default.Here is a demo:
[HttpGet]
public IActionResult Logout(string SAMLResponse)
{
var base64Response = SAMLResponse;
return View();
}
Update:
If you want to decode a base64string,try to use:
byte[] data = Convert.FromBase64String(SAMLResponse);
string decodedString = Encoding.UTF8.GetString(data);