-
-
Notifications
You must be signed in to change notification settings - Fork 77
Expand file tree
/
Copy pathBankIdUiApiControllerBase.cs
More file actions
201 lines (171 loc) · 8.13 KB
/
BankIdUiApiControllerBase.cs
File metadata and controls
201 lines (171 loc) · 8.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
using System.Net.Mime;
using System.Text.Json;
using ActiveLogin.Authentication.BankId.Api;
using ActiveLogin.Authentication.BankId.Api.Models;
using ActiveLogin.Authentication.BankId.Api.UserMessage;
using ActiveLogin.Authentication.BankId.AspNetCore.Areas.ActiveLogin.Models;
using ActiveLogin.Authentication.BankId.AspNetCore.Cookies;
using ActiveLogin.Authentication.BankId.AspNetCore.DataProtection;
using ActiveLogin.Authentication.BankId.AspNetCore.Helpers;
using ActiveLogin.Authentication.BankId.AspNetCore.Models;
using ActiveLogin.Authentication.BankId.Core;
using ActiveLogin.Authentication.BankId.Core.Flow;
using ActiveLogin.Authentication.BankId.Core.Models;
using ActiveLogin.Authentication.BankId.Core.UserMessage;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace ActiveLogin.Authentication.BankId.AspNetCore.Areas.ActiveLogin.Controllers;
[NonController]
public abstract class BankIdUiApiControllerBase(
IBankIdFlowService bankIdFlowService,
IBankIdDataStateProtector<BankIdUiOrderRef> orderRefProtector,
IBankIdDataStateProtector<BankIdQrStartState> qrStartStateProtector,
IBankIdUiOptionsCookieManager uiOptionsCookieManager,
IBankIdUserMessage bankIdUserMessage,
IBankIdUserMessageLocalizer bankIdUserMessageLocalizer,
IBankIdDataStateProtector<BankIdUiResult> uiAuthResultProtector,
IStateStorage stateStorage
) : ControllerBase
{
private static JsonSerializerOptions JsonSerializerOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
protected readonly IBankIdFlowService BankIdFlowService = bankIdFlowService;
protected readonly IBankIdDataStateProtector<BankIdUiOrderRef> OrderRefProtector = orderRefProtector;
protected readonly IBankIdDataStateProtector<BankIdQrStartState> QrStartStateProtector = qrStartStateProtector;
protected readonly IStateStorage _stateStorage = stateStorage;
protected readonly IBankIdUiOptionsCookieManager UiOptionsCookieManager = uiOptionsCookieManager;
private readonly IBankIdUserMessage _bankIdUserMessage = bankIdUserMessage;
private readonly IBankIdUserMessageLocalizer _bankIdUserMessageLocalizer = bankIdUserMessageLocalizer;
private readonly IBankIdDataStateProtector<BankIdUiResult> _uiAuthResultProtector = uiAuthResultProtector;
[ValidateAntiForgeryToken]
[HttpPost(BankIdConstants.Routes.BankIdApiStatusActionName)]
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);
BankIdFlowCollectResult result;
try
{
result = await BankIdFlowService.Collect(orderRef.OrderRef, request.AutoStartAttempts, uiOptions.ToBankIdFlowOptions());
}
catch (BankIdApiException bankIdApiException)
{
var errorStatusMessage = GetBankIdApiExceptionStatusMessage(bankIdApiException);
return BadRequestJsonResult(new BankIdUiApiErrorResponse(errorStatusMessage));
}
switch (result)
{
case BankIdFlowCollectResultPending pending:
{
return OkJsonResult(BankIdUiApiStatusResponse.Pending(pending.StatusMessage));
}
case BankIdFlowCollectResultComplete complete:
{
var uiResult = ConstructProtectedUiResult(orderRef.OrderRef, complete.CompletionData);
UiOptionsCookieManager.Delete(request.UiOptions);
return OkJsonResult(BankIdUiApiStatusResponse.Finished(request.ReturnUrl, uiResult));
}
case BankIdFlowCollectResultRetry retry:
{
return OkJsonResult(BankIdUiApiStatusResponse.Retry(retry.StatusMessage));
}
case BankIdFlowCollectResultFailure failure:
{
UiOptionsCookieManager.Delete(request.UiOptions);
return BadRequestJsonResult(new BankIdUiApiErrorResponse(failure.StatusMessage));
}
default:
{
throw new InvalidOperationException(BankIdConstants.ErrorMessages.UnknownFlowCollectResultType);
}
}
}
[ValidateAntiForgeryToken]
[HttpPost(BankIdConstants.Routes.BankIdApiQrCodeActionName)]
public ActionResult QrCode(BankIdUiApiQrCodeRequest request)
{
Validators.ThrowIfNullOrWhitespace(request.QrStartState, nameof(request.QrStartState));
var qrStartState = QrStartStateProtector.Unprotect(request.QrStartState);
var qrCodeAsBase64 = BankIdFlowService.GetQrCodeAsBase64(qrStartState);
return OkJsonResult(new BankIdUiApiQrCodeResponse(qrCodeAsBase64));
}
[ValidateAntiForgeryToken]
[HttpPost(BankIdConstants.Routes.BankIdApiCancelActionName)]
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);
await BankIdFlowService.Cancel(orderRef.OrderRef, uiOptions.ToBankIdFlowOptions());
return OkJsonResult(BankIdUiCancelResponse.Cancelled());
}
protected string GetBankIdApiExceptionStatusMessage(BankIdApiException bankIdApiException)
{
var messageShortName = _bankIdUserMessage.GetMessageShortNameForErrorResponse(bankIdApiException.ErrorCode);
var statusMessage = _bankIdUserMessageLocalizer.GetLocalizedString(messageShortName);
return statusMessage;
}
protected string ConstructProtectedUiResult(string orderRef, CompletionData completionData)
{
var user = completionData.User;
var uiResult = BankIdUiResult.Success(
orderRef,
user.PersonalIdentityNumber,
user.Name,
user.GivenName,
user.Surname,
completionData.BankIdIssueDate,
completionData.StepUp?.Mrtd ?? false,
completionData.Signature,
completionData.OcspResponse,
completionData.Device.IpAddress,
completionData.Device.Uhi,
completionData.Risk
);
return _uiAuthResultProtector.Protect(uiResult);
}
protected static ActionResult OkJsonResult(object model)
{
return new ContentResult
{
ContentType = MediaTypeNames.Application.Json,
StatusCode = StatusCodes.Status200OK,
Content = JsonSerializer.Serialize(model, JsonSerializerOptions)
};
}
protected static ActionResult BadRequestJsonResult(object model)
{
return new ContentResult
{
ContentType = MediaTypeNames.Application.Json,
StatusCode = StatusCodes.Status400BadRequest,
Content = JsonSerializer.Serialize(model, JsonSerializerOptions)
};
}
/// <summary>
/// Resolves the UiOptions from either a GUID (stored in cookie) or the direct protected value.
/// </summary>
protected BankIdUiOptions ResolveProtectedUiOptions(string protectedUiOptionsOrGuid)
{
return UiOptionsCookieManager.Retrieve(protectedUiOptionsOrGuid)
?? throw new InvalidOperationException(BankIdConstants.ErrorMessages.InvalidUiOptions);
}
protected async ValueTask<T?> GetState<T>(BankIdUiOptions uiOptions)
where T : BankIdUiState
{
var cookie = Request.Cookies[uiOptions.StateKeyCookieName];
if (cookie == null)
{
return null;
}
var stateKey = new StateKey(cookie);
return await _stateStorage.GetAsync<T>(stateKey);
}
}