Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,14 @@ public async Task<ActionResult> Status(BankIdUiApiStatusRequest request)
{
Validators.ThrowIfNullOrWhitespace(request.OrderRef, nameof(request.OrderRef));
Validators.ThrowIfNullOrWhitespace(request.ReturnUrl, nameof(request.ReturnUrl));
Validators.ThrowIfNullOrWhitespace(request.UiOptions, nameof(request.UiOptions));

if (!Url.IsLocalUrl(request.ReturnUrl))
{
throw new Exception(BankIdConstants.ErrorMessages.InvalidReturnUrl);
}

var orderRef = OrderRefProtector.Unprotect(request.OrderRef);
var uiOptions = ResolveProtectedUiOptions(request.UiOptions);
var uiOptions = ResolveProtectedUiOptions();

BankIdFlowCollectResult result;
try
Expand All @@ -92,8 +91,6 @@ public async Task<ActionResult> Status(BankIdUiApiStatusRequest request)
case BankIdFlowCollectResultComplete complete:
{
var uiResult = ConstructProtectedUiResult(orderRef.OrderRef, complete.CompletionData);
UiOptionsCookieManager.Delete(request.UiOptions);

return OkJsonResult(BankIdUiApiStatusResponse.Finished(request.ReturnUrl, uiResult));
}
case BankIdFlowCollectResultRetry retry:
Expand All @@ -102,8 +99,6 @@ public async Task<ActionResult> Status(BankIdUiApiStatusRequest request)
}
case BankIdFlowCollectResultFailure failure:
{
UiOptionsCookieManager.Delete(request.UiOptions);

return BadRequestJsonResult(new BankIdUiApiErrorResponse(failure.StatusMessage));
}
default:
Expand All @@ -130,10 +125,9 @@ public ActionResult QrCode(BankIdUiApiQrCodeRequest request)
public async Task<ActionResult> Cancel(BankIdUiApiCancelRequest request)
{
Validators.ThrowIfNullOrWhitespace(request.OrderRef, nameof(request.OrderRef));
Validators.ThrowIfNullOrWhitespace(request.UiOptions, nameof(request.UiOptions));

var orderRef = OrderRefProtector.Unprotect(request.OrderRef);
var uiOptions = ResolveProtectedUiOptions(request.UiOptions);
var uiOptions = ResolveProtectedUiOptions();

await BankIdFlowService.Cancel(orderRef.OrderRef, uiOptions.ToBankIdFlowOptions());

Expand Down Expand Up @@ -189,11 +183,10 @@ protected static ActionResult BadRequestJsonResult(object model)
}

/// <summary>
/// Resolves the UiOptions from either a GUID (stored in cookie) or the direct protected value.
/// Resolves the UiOptions stored in cookie and unprotects value.
/// </summary>
protected BankIdUiOptions ResolveProtectedUiOptions(string protectedUiOptionsOrGuid)
protected BankIdUiOptions ResolveProtectedUiOptions()
{
return UiOptionsCookieManager.Retrieve(protectedUiOptionsOrGuid)
?? throw new InvalidOperationException(BankIdConstants.ErrorMessages.InvalidUiOptions);
return UiOptionsCookieManager.Retrieve() ?? throw new InvalidOperationException(BankIdConstants.ErrorMessages.InvalidUiOptions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,15 @@ public BankIdUiAuthApiController(
public async Task<ActionResult<BankIdUiApiInitializeResponse>> Initialize(BankIdUiApiInitializeRequest request)
{
Validators.ThrowIfNullOrWhitespace(request.ReturnUrl, nameof(request.ReturnUrl));
Validators.ThrowIfNullOrWhitespace(request.UiOptions, nameof(request.UiOptions));

var uiOptions = ResolveProtectedUiOptions(request.UiOptions);
var uiOptions = ResolveProtectedUiOptions();

BankIdFlowInitializeResult bankIdFlowInitializeResult;
try
{
var returnRedirectUrl = Url.Action(BankIdConstants.Routes.BankIdAuthInitActionName, BankIdConstants.Routes.BankIdAuthControllerName, new
{
returnUrl = request.ReturnUrl,
uiOptions = request.UiOptions
returnUrl = request.ReturnUrl
}, protocol: Request.Scheme) ?? throw new Exception(BankIdConstants.ErrorMessages.CouldNotGetUrlFor(BankIdConstants.Routes.BankIdAuthControllerName, BankIdConstants.Routes.BankIdAuthInitActionName));

bankIdFlowInitializeResult = await BankIdFlowService.InitializeAuth(uiOptions.ToBankIdFlowOptions(), returnRedirectUrl);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ IBankIdUiOptionsCookieManager uiOptionsCookieManager

[HttpGet]
[Route($"/[area]/{BankIdConstants.Routes.BankIdPathName}/{BankIdConstants.Routes.BankIdAuthControllerPath}")]
public Task<ActionResult> Init(string returnUrl, [FromQuery(Name = BankIdConstants.QueryStringParameters.UiOptions)] string uiOptionsGuid)
public Task<ActionResult> Init(string returnUrl)
{
return Initialize(returnUrl, BankIdConstants.Routes.BankIdAuthApiControllerName, uiOptionsGuid, "Init");
return Initialize(returnUrl, BankIdConstants.Routes.BankIdAuthApiControllerName, "Init");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,20 @@ protected BankIdUiControllerBase(
_uiOptionsCookieManager = uiOptionsCookieManager;
}

protected async Task<ActionResult> Initialize(string returnUrl, string apiControllerName, string uiOptionsGuid, string viewName)
protected async Task<ActionResult> Initialize(string returnUrl, string apiControllerName, string viewName)
{
Validators.ThrowIfNullOrWhitespace(returnUrl);
Validators.ThrowIfNullOrWhitespace(uiOptionsGuid, BankIdConstants.QueryStringParameters.UiOptions);

if (!Url.IsLocalUrl(returnUrl))
{
throw new ArgumentException(BankIdConstants.ErrorMessages.InvalidReturnUrl);
}

var uiOptions = _uiOptionsCookieManager.Retrieve(uiOptionsGuid) ?? throw new InvalidOperationException(BankIdConstants.ErrorMessages.InvalidUiOptions);
if (!HasStateCookie(uiOptions))
var uiOptions = _uiOptionsCookieManager.Retrieve();
if (uiOptions == null || !HasStateCookie(uiOptions))
{
var invalidStateContext = new BankIdInvalidStateContext(uiOptions.CancelReturnUrl);
var cancelReturnUrl = uiOptions?.CancelReturnUrl ?? "/";
var invalidStateContext = new BankIdInvalidStateContext(cancelReturnUrl);
await _bankIdInvalidStateHandler.HandleAsync(invalidStateContext);

return new EmptyResult();
Expand All @@ -75,7 +75,7 @@ protected async Task<ActionResult> Initialize(string returnUrl, string apiContro
}
var state = _bankIdUiStateProtector.Unprotect(protectedState);

var viewModel = GetUiViewModel(returnUrl, apiControllerName, uiOptionsGuid, uiOptions, state, antiforgeryTokens);
var viewModel = GetUiViewModel(returnUrl, apiControllerName, uiOptions, state, antiforgeryTokens);

return View(viewName, viewModel);
}
Expand All @@ -91,7 +91,7 @@ private bool HasStateCookie(BankIdUiOptions uiOptions)
return !string.IsNullOrEmpty(HttpContext.Request.Cookies[uiOptions.StateCookieName]);
}

private BankIdUiViewModel GetUiViewModel(string returnUrl, string apiControllerName, string uiOptionsGuid, BankIdUiOptions unprotectedUiOptions, BankIdUiState uiState, AntiforgeryTokenSet antiforgeryTokens)
private BankIdUiViewModel GetUiViewModel(string returnUrl, string apiControllerName, BankIdUiOptions unprotectedUiOptions, BankIdUiState uiState, AntiforgeryTokenSet antiforgeryTokens)
{
Validators.ThrowIfNullOrWhitespace(antiforgeryTokens.RequestToken, nameof(antiforgeryTokens.RequestToken));

Expand Down Expand Up @@ -119,8 +119,6 @@ private BankIdUiViewModel GetUiViewModel(string returnUrl, string apiControllerN

ReturnUrl = returnUrl,
CancelReturnUrl = Url.Content(unprotectedUiOptions.CancelReturnUrl),

UiOptionsGuid = uiOptionsGuid
};

var localizedStartAppButtonText = _localizer["StartApp_Button"];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,8 @@ public BankIdUiPaymentApiController(
public async Task<ActionResult<BankIdUiApiInitializeResponse>> Initialize(BankIdUiApiInitializeRequest request)
{
Validators.ThrowIfNullOrWhitespace(request.ReturnUrl, nameof(request.ReturnUrl));
Validators.ThrowIfNullOrWhitespace(request.UiOptions, nameof(request.UiOptions));

var uiOptions = ResolveProtectedUiOptions(request.UiOptions);
var uiOptions = ResolveProtectedUiOptions();

var state = GetStateFromCookie(uiOptions);
if(state == null)
Expand All @@ -59,8 +58,7 @@ public async Task<ActionResult<BankIdUiApiInitializeResponse>> Initialize(BankId
{
var returnRedirectUrl = Url.Action(BankIdConstants.Routes.BankIdPaymentInitActionName, BankIdConstants.Routes.BankIdPaymentControllerName, new
{
returnUrl = request.ReturnUrl,
uiOptions = request.UiOptions
returnUrl = request.ReturnUrl
}, protocol: Request.Scheme) ?? throw new Exception(BankIdConstants.ErrorMessages.CouldNotGetUrlFor(BankIdConstants.Routes.BankIdPaymentControllerName, BankIdConstants.Routes.BankIdPaymentInitActionName));

bankIdFlowInitializeResult = await BankIdFlowService.InitializePayment(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ IBankIdUiOptionsCookieManager uiOptionsCookieManager
[HttpGet]
[AllowAnonymous]
[Route($"/[area]/{BankIdConstants.Routes.BankIdPathName}/{BankIdConstants.Routes.BankIdPaymentControllerPath}")]
public Task<ActionResult> Init(string returnUrl, [FromQuery(Name = BankIdConstants.QueryStringParameters.UiOptions)] string protectedUiOptions)
public Task<ActionResult> Init(string returnUrl)
{
return Initialize(returnUrl, BankIdConstants.Routes.BankIdPaymentApiControllerName, protectedUiOptions, "Init");
return Initialize(returnUrl, BankIdConstants.Routes.BankIdPaymentApiControllerName, "Init");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,11 @@ public BankIdUiSignApiController(
public async Task<ActionResult<BankIdUiApiInitializeResponse>> Initialize(BankIdUiApiInitializeRequest request)
{
Validators.ThrowIfNullOrWhitespace(request.ReturnUrl, nameof(request.ReturnUrl));
Validators.ThrowIfNullOrWhitespace(request.UiOptions, nameof(request.UiOptions));

var uiOptions = ResolveProtectedUiOptions(request.UiOptions);
var uiOptions = ResolveProtectedUiOptions();

var state = GetStateFromCookie(uiOptions);
if(state == null)
if (state == null)
{
throw new InvalidOperationException(BankIdConstants.ErrorMessages.InvalidStateCookie);
}
Expand All @@ -59,8 +58,7 @@ public async Task<ActionResult<BankIdUiApiInitializeResponse>> Initialize(BankId
{
var returnRedirectUrl = Url.Action(BankIdConstants.Routes.BankIdSignInitActionName, BankIdConstants.Routes.BankIdSignControllerName, new
{
returnUrl = request.ReturnUrl,
uiOptions = request.UiOptions
returnUrl = request.ReturnUrl
}, protocol: Request.Scheme) ?? throw new Exception(BankIdConstants.ErrorMessages.CouldNotGetUrlFor(BankIdConstants.Routes.BankIdSignControllerName, BankIdConstants.Routes.BankIdSignInitActionName));

bankIdFlowInitializeResult = await BankIdFlowService.InitializeSign(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ IBankIdUiOptionsCookieManager uiOptionsCookieManager
[HttpGet]
[AllowAnonymous]
[Route($"/[area]/{BankIdConstants.Routes.BankIdPathName}/{BankIdConstants.Routes.BankIdSignControllerPath}")]
public Task<ActionResult> Init(string returnUrl, [FromQuery(Name = BankIdConstants.QueryStringParameters.UiOptions)] string protectedUiOptions)
public Task<ActionResult> Init(string returnUrl)
{
return Initialize(returnUrl, BankIdConstants.Routes.BankIdSignApiControllerName, protectedUiOptions, "Init");
return Initialize(returnUrl, BankIdConstants.Routes.BankIdSignApiControllerName, "Init");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ public BankIdUiApiCancelRequest()

}

[Required]
public string? UiOptions { get; set; }

[Required]
public string? OrderRef { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,4 @@ public BankIdUiApiInitializeRequest()

[Required]
public string? ReturnUrl { get; set; }

[Required]
public string? UiOptions { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,5 @@ public BankIdUiApiStatusRequest()
[Required]
public string? ReturnUrl { get; set; }

[Required]
public string? UiOptions { get; set; }

public int AutoStartAttempts { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,4 @@ internal BankIdUiScriptInitState()

[JsonPropertyName("cancelReturnUrl")]
public string CancelReturnUrl { get; init; } = string.Empty;

[JsonPropertyName("uiOptionsGuid")]
public string UiOptionsGuid { get; init; } = string.Empty;
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ namespace ActiveLogin.Authentication.BankId.AspNetCore.Auth;
public class BankIdAuthHandler : RemoteAuthenticationHandler<BankIdAuthOptions>
{
private const string StateCookieNameParameterName = "StateCookie.Name";

private readonly PathString _authPath = new($"/{BankIdConstants.Routes.ActiveLoginAreaName}/{BankIdConstants.Routes.BankIdPathName}/{BankIdConstants.Routes.BankIdAuthControllerPath}");

private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IAntiforgery _antiforgery;
private readonly IBankIdUiStateProtector _uiStateProtector;
private readonly IBankIdUiOptionsProtector _uiOptionsProtector;
private readonly IBankIdUiResultProtector _uiResultProtector;
private readonly IBankIdEventTrigger _bankIdEventTrigger;
private readonly IBankIdSupportedDeviceDetector _bankIdSupportedDeviceDetector;
Expand All @@ -44,7 +44,6 @@ public BankIdAuthHandler(
ILoggerFactory loggerFactory,
UrlEncoder encoder,
IBankIdUiStateProtector uiStateProtector,
IBankIdUiOptionsProtector uiOptionsProtector,
IBankIdUiResultProtector uiResultProtector,
IBankIdEventTrigger bankIdEventTrigger,
IBankIdSupportedDeviceDetector bankIdSupportedDeviceDetector,
Expand All @@ -55,7 +54,6 @@ public BankIdAuthHandler(
_httpContextAccessor = httpContextAccessor;
_antiforgery = antiforgery;
_uiStateProtector = uiStateProtector;
_uiOptionsProtector = uiOptionsProtector;
_uiResultProtector = uiResultProtector;
_bankIdEventTrigger = bankIdEventTrigger;
_bankIdSupportedDeviceDetector = bankIdSupportedDeviceDetector;
Expand All @@ -74,6 +72,7 @@ protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync
}

DeleteStateCookie();
DeleteUiOptionsCookie();

if (!Request.HasFormContentType)
{
Expand Down Expand Up @@ -172,6 +171,8 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop
Options.CardReader
);

AppendUiOptionsCookie(uiOptions);

var detectedDevice = _bankIdSupportedDeviceDetector.Detect();
await _bankIdEventTrigger.TriggerAsync(new BankIdAspNetChallengeSuccessEvent(detectedDevice, uiOptions.ToBankIdFlowOptions()));

Expand All @@ -185,13 +186,9 @@ private string GetInitUiUrl(BankIdUiOptions uiOptions)
var authUrl = pathBase.Add(_authPath);
var returnUrl = pathBase.Add(Options.CallbackPath);

// Store UiOptions in cookie and use GUID in URL to reduce URL length
var uiOptionsGuid = _uiOptionsCookieManager.Store(uiOptions);

var queryBuilder = new QueryBuilder(new Dictionary<string, string>
{
{ BankIdConstants.QueryStringParameters.ReturnUrl, returnUrl },
{ BankIdConstants.QueryStringParameters.UiOptions, uiOptionsGuid }
{ BankIdConstants.QueryStringParameters.ReturnUrl, returnUrl }
});

return $"{authUrl}{queryBuilder.ToQueryString()}";
Expand All @@ -213,6 +210,16 @@ private void AppendStateCookie(AuthenticationProperties properties)
Response.Cookies.Append(Options.StateCookie.Name, cookieValue, cookieOptions);
}

private void AppendUiOptionsCookie(BankIdUiOptions uiOptions)
{
if (Options.TimeProvider == null)
{
throw new InvalidOperationException(BankIdConstants.ErrorMessages.TimeProviderNotSet);
}

_uiOptionsCookieManager.Store(uiOptions, Options.TimeProvider.GetUtcNow());
}

private BankIdUiAuthState? GetStateFromCookie()
{
Validators.ThrowIfNullOrWhitespace(Options.StateCookie.Name, StateCookieNameParameterName);
Expand All @@ -238,4 +245,9 @@ private void DeleteStateCookie()
var cookieOptions = Options.StateCookie.Build(Context, Options.TimeProvider.GetUtcNow());
Response.Cookies.Delete(Options.StateCookie.Name, cookieOptions);
}

private void DeleteUiOptionsCookie()
{
_uiOptionsCookieManager.Delete();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@ internal static class BankIdConstants
public static readonly TimeSpan QrCodeRefreshInterval = TimeSpan.FromSeconds(1);

public static readonly TimeSpan DeviceDataRefreshInterval = TimeSpan.FromDays(365);
public static readonly TimeSpan UiOptionsCookieLifeTime = TimeSpan.FromMinutes(10);

public const string DefaultCancelUrl = "/";
public const string DefaultStateCookieName = "__ActiveLogin.BankIdUiState";
public const string DefaultDeviceDataCookieName = "__ActiveLogin.BankIdDeviceData";
public const string DefaultUiOptionsCookieNamePrefix = "__ActiveLogin.BankId.UiOptions_";
public const string DefaultUiOptionsCookieName = "__ActiveLogin.BankId.UiOptions";

public const string LocalizationResourcesPath = "Resources";

Expand Down
Loading