213 lines
7.9 KiB
C#
213 lines
7.9 KiB
C#
using System;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Xml;
|
|
using System.Xml.Linq;
|
|
using System.Security.Authentication;
|
|
using Serilog;
|
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using OnlineBankingAuth.Tokens;
|
|
using Landmark.OLA.Models.OnlineBanking;
|
|
using Landmark.OLA.Application.Common.Security;
|
|
using Landmark.OLA.Models.Core;
|
|
using OnlineBankingAuth.Parsers;
|
|
using OnlineBankingAuth.Services;
|
|
|
|
namespace Landmark.OLA.Application.Controllers
|
|
{
|
|
[Route("api/[controller]")]
|
|
[ApiController]
|
|
public class SamlAssertionConsumerController : ControllerBase
|
|
{
|
|
private readonly IDigitalInsightVendorClient _diVendor;
|
|
private readonly ITokenService _tokenService;
|
|
|
|
public SamlAssertionConsumerController
|
|
(
|
|
IDigitalInsightVendorClient diVendor,
|
|
ITokenService tokenService
|
|
)
|
|
{
|
|
_diVendor = diVendor;
|
|
_tokenService = tokenService;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use to refer a user to the application with a valid SAML assertion
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Returns an application redirect, adding in a SAML assertion via a JWT in a secure http-only cookie.
|
|
/// Cookies: Set a standard pair of authentication and authorization cookies.
|
|
/// Security: Passed document must include a valid, signed SAML assertion, provides member authentication.
|
|
/// </remarks>
|
|
// POST: api/SAMLAssertionConsumer
|
|
[HttpPost]
|
|
[ProducesResponseType(403)]
|
|
[ProducesResponseType(401)]
|
|
[ProducesResponseType(201)]
|
|
[ProducesResponseType(typeof(ModelStateDictionary), 401)]
|
|
public async Task Post([FromForm] SAMLMessaging document)
|
|
{
|
|
try
|
|
{
|
|
var decodedString = Encoding.UTF8.GetString(Convert.FromBase64String(document.SAMLResponse));
|
|
|
|
var parsedXml = XElement.Parse(decodedString);
|
|
var xmlDocument = new XmlDocument
|
|
{
|
|
PreserveWhitespace = true
|
|
};
|
|
xmlDocument.Load(parsedXml.CreateReader());
|
|
|
|
// @TODO: Should catch error here, move element query to static service #1400b
|
|
var x509Payload = (XmlElement)xmlDocument.GetElementsByTagName("ds:X509Certificate")[0];
|
|
var vendorCertMatches = _diVendor.ValidateDIPublicKey(x509Payload);
|
|
if (!vendorCertMatches)
|
|
{
|
|
throw new AuthenticationException("The vendor's public certificate did not match! ");
|
|
}
|
|
|
|
var assertion = _diVendor.GetSAMLPayload(xmlDocument);
|
|
|
|
// Check that assertion is still valid
|
|
var datesValid = _diVendor.ValidateTimeStamp(assertion);
|
|
if (!datesValid)
|
|
{
|
|
throw new AuthenticationException("The Authentication has expired!");
|
|
}
|
|
|
|
var obToken = new OnlineBankingToken(assertion, null, true)
|
|
{
|
|
IsTwoFactor = true
|
|
};
|
|
obToken.IndividualID = obToken.MemberID;
|
|
|
|
AssignTokens(obToken);
|
|
HttpContext.Response.Redirect(_diVendor.GetSuccessRedirectUrl());
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, $"Failure to validate SAML in {GetType().Name}");
|
|
HttpContext.Response.Redirect(_diVendor.GetFailureRedirectUrl(ex.Message));
|
|
}
|
|
}
|
|
|
|
private void AssignTokens(OnlineBankingToken obToken)
|
|
{
|
|
var meta = new MemberMeta
|
|
{
|
|
IsTwoFactor = obToken.IsTwoFactor,
|
|
IsMember = (obToken.MemberID != null),
|
|
IsIndividual = (obToken.IndividualID != null)
|
|
};
|
|
|
|
var privateToken = _tokenService.CreateOnlineBankingJWT(obToken);
|
|
var publicToken = _tokenService.CreateAuthMetaToken(meta);
|
|
CookieService.SetAuthenticationCookie(HttpContext.Response.Cookies, privateToken);
|
|
CookieService.SetAuthorizationCookie(HttpContext.Response.Cookies, publicToken);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use to create an empty cookie session
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Returns a pair of empty authorization and authentication cookies.
|
|
/// Cookies: Sets empty Authentication and Authorization cookies.
|
|
/// Security: None required or provided.
|
|
/// </remarks>
|
|
[HttpGet("InitSession")]
|
|
[ProducesResponseType(200)]
|
|
public IActionResult Get()
|
|
{
|
|
var obToken = new OnlineBankingToken
|
|
{
|
|
MemberID = null, IndividualID = null, FI_ID = "0", IsTwoFactor = false
|
|
};
|
|
|
|
AssignTokens(obToken);
|
|
return Ok();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use to create a cookie session with credentials
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Returns a pair of authorization and authentication cookies when provided with valid credentials.
|
|
/// Cookies: Sets Authentication and Authorization cookies.
|
|
/// Security: Require.
|
|
/// </remarks>
|
|
[HttpPost("Authenticate")]
|
|
[ProducesResponseType(403)]
|
|
[ProducesResponseType(201)]
|
|
[ProducesResponseType(typeof(ModelStateDictionary), 401)]
|
|
public async Task<object> PostAsync([FromBody] OnlineBankingFormData document)
|
|
{
|
|
try
|
|
{
|
|
var diResponse = await _diVendor.AuthenticateOnlineBankingAccount(document);
|
|
|
|
if (diResponse.Token == null)
|
|
{
|
|
Log.Error($"Member failed non-two factor login, {@document.Username}");
|
|
return Ok(diResponse.Status);
|
|
}
|
|
|
|
var assertion = new DISAMLAssertion
|
|
{
|
|
MemberID = diResponse.Token.DiMemberNumber,
|
|
FI_ID = diResponse.Token.DiFiid
|
|
};
|
|
|
|
var obToken = new OnlineBankingToken(assertion, null, false)
|
|
{
|
|
IsTwoFactor = false
|
|
};
|
|
obToken.IndividualID = obToken.MemberID;
|
|
|
|
AssignTokens(obToken);
|
|
return Ok(diResponse.Status);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, $"Failure to authenticate user in {GetType().Name}");
|
|
return StatusCode((int)System.Net.HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use to identify a unique individual on the member account.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Returns OK if an individual ID was added to the cookie.
|
|
/// Cookies: Sets Individual ID in cookie.
|
|
/// Security: Requires existing cookie, with member.
|
|
/// </remarks>
|
|
[HttpPost("Identify")]
|
|
[ProducesResponseType(403)]
|
|
[ProducesResponseType(400)]
|
|
[ProducesResponseType(200)]
|
|
[ProducesResponseType(typeof(ModelStateDictionary), 401)]
|
|
public async Task<object> PostIdentify([FromBody] OnlineIdentityFormData document)
|
|
{
|
|
try
|
|
{
|
|
var token = _tokenService.GetAuthenticatedDataFromCookie(HttpContext);
|
|
if (token == null)
|
|
{
|
|
Log.Error($"Non-authenticated attempt at individual confirmation! Using info {@document}, Refusing.");
|
|
return Unauthorized();
|
|
}
|
|
|
|
token.IndividualID = token.MemberID;
|
|
AssignTokens(token);
|
|
return Ok();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, $"Failure to identify user in {GetType().Name}");
|
|
return StatusCode((int)System.Net.HttpStatusCode.InternalServerError);
|
|
}
|
|
}
|
|
}
|
|
} |