0
# Webflow Utilities
1
2
Static utility methods for managing MFA-related data in webflow scopes, device registration, provider selection, and token management. The `MultifactorAuthenticationWebflowUtils` class provides a comprehensive set of utilities for storing and retrieving MFA-related information across webflow transitions.
3
4
## Capabilities
5
6
### MultifactorAuthenticationWebflowUtils
7
8
Utility class providing static methods for managing MFA-related data in webflow scopes.
9
10
```java { .api }
11
/**
12
* Utility class for managing MFA-related data in webflow scopes
13
*/
14
@UtilityClass
15
public class MultifactorAuthenticationWebflowUtils {
16
17
// Webflow Customizer Management
18
19
/**
20
* Get multifactor authentication webflow customizers from application context
21
* @param applicationContext Spring application context
22
* @return List of sorted webflow customizers
23
*/
24
public static List<CasMultifactorWebflowCustomizer> getMultifactorAuthenticationWebflowCustomizers(
25
ConfigurableApplicationContext applicationContext);
26
27
// Device Registration Management
28
29
/**
30
* Check if multifactor device registration is enabled
31
* @param requestContext The webflow request context
32
* @return true if device registration is enabled
33
*/
34
public static boolean isMultifactorDeviceRegistrationEnabled(RequestContext requestContext);
35
36
/**
37
* Set multifactor device registration enabled flag
38
* @param requestContext The webflow request context
39
* @param enabled Whether device registration is enabled
40
*/
41
public static void putMultifactorDeviceRegistrationEnabled(RequestContext requestContext, boolean enabled);
42
43
// Provider Management
44
45
/**
46
* Store resolved multifactor authentication providers in conversation scope
47
* @param context The webflow request context
48
* @param value Collection of resolved MFA providers
49
*/
50
public static void putResolvedMultifactorAuthenticationProviders(
51
RequestContext context,
52
Collection<MultifactorAuthenticationProvider> value);
53
54
/**
55
* Get resolved multifactor authentication providers from conversation scope
56
* @param context The webflow request context
57
* @return Collection of resolved provider IDs
58
*/
59
public static Collection<String> getResolvedMultifactorAuthenticationProviders(RequestContext context);
60
61
/**
62
* Store MFA provider ID in flow scope
63
* @param context The webflow request context
64
* @param provider The MFA provider
65
*/
66
public static void putMultifactorAuthenticationProvider(RequestContext context, MultifactorAuthenticationProvider provider);
67
68
/**
69
* Get MFA provider ID from flow scope
70
* @param context The webflow request context
71
* @return Provider ID string
72
*/
73
public static String getMultifactorAuthenticationProvider(RequestContext context);
74
75
// Provider Selection Management
76
77
/**
78
* Store selectable multifactor authentication providers in view scope
79
* @param requestContext The webflow request context
80
* @param mfaProviders List of selectable provider IDs
81
*/
82
public static void putSelectableMultifactorAuthenticationProviders(RequestContext requestContext, List<String> mfaProviders);
83
84
/**
85
* Get selectable multifactor authentication providers from view scope
86
* @param requestContext The webflow request context
87
* @return List of selectable provider IDs
88
*/
89
public static List<String> getSelectableMultifactorAuthenticationProviders(RequestContext requestContext);
90
91
// Google Authenticator Specific
92
93
/**
94
* Set Google Authenticator multiple device registration enabled flag
95
* @param requestContext The webflow request context
96
* @param enabled Whether multiple device registration is enabled
97
*/
98
public static void putGoogleAuthenticatorMultipleDeviceRegistrationEnabled(RequestContext requestContext, boolean enabled);
99
100
/**
101
* Check if Google Authenticator multiple device registration is enabled
102
* @param requestContext The webflow request context
103
* @return Boolean indicating if multiple device registration is enabled
104
*/
105
public static Boolean isGoogleAuthenticatorMultipleDeviceRegistrationEnabled(RequestContext requestContext);
106
107
// YubiKey Specific
108
109
/**
110
* Set YubiKey multiple device registration enabled flag
111
* @param requestContext The webflow request context
112
* @param enabled Whether multiple device registration is enabled
113
*/
114
public static void putYubiKeyMultipleDeviceRegistrationEnabled(RequestContext requestContext, boolean enabled);
115
116
// Simple MFA Token Management
117
118
/**
119
* Store simple multifactor authentication token in flow scope
120
* @param requestContext The webflow request context
121
* @param token The ticket token
122
*/
123
public static void putSimpleMultifactorAuthenticationToken(RequestContext requestContext, Ticket token);
124
125
/**
126
* Remove simple multifactor authentication token from flow scope
127
* @param requestContext The webflow request context
128
*/
129
public static void removeSimpleMultifactorAuthenticationToken(RequestContext requestContext);
130
131
/**
132
* Get simple multifactor authentication token from flow scope
133
* @param <T> Type of ticket extending Ticket
134
* @param requestContext The webflow request context
135
* @param clazz Class type of the ticket
136
* @return Typed ticket instance or null
137
*/
138
public static <T extends Ticket> T getSimpleMultifactorAuthenticationToken(RequestContext requestContext, Class<T> clazz);
139
140
// Parent Credential Management
141
142
/**
143
* Get multifactor authentication parent credential from flow scope
144
* @param requestContext The webflow request context
145
* @return Parent credential or null
146
*/
147
public static Credential getMultifactorAuthenticationParentCredential(RequestContext requestContext);
148
149
// Registered Device Management
150
151
/**
152
* Store multifactor authentication registered devices in flow scope
153
* @param requestContext The webflow request context
154
* @param accounts Set of registered devices/accounts
155
*/
156
public static void putMultifactorAuthenticationRegisteredDevices(RequestContext requestContext, Set accounts);
157
158
/**
159
* Get multifactor authentication registered devices from flow scope
160
* Note: This method is NOT static in the source, which appears to be an inconsistency
161
* @param requestContext The webflow request context
162
* @return Set of registered devices
163
*/
164
public Set<MultifactorAuthenticationRegisteredDevice> getMultifactorAuthenticationRegisteredDevices(RequestContext requestContext);
165
166
// One-Time Token Account Management
167
168
/**
169
* Store one-time token account in flow scope
170
* @param requestContext The webflow request context
171
* @param account The one-time token account
172
*/
173
public static void putOneTimeTokenAccount(RequestContext requestContext, OneTimeTokenAccount account);
174
175
/**
176
* Store collection of one-time token accounts in flow scope
177
* @param requestContext The webflow request context
178
* @param accounts Collection of one-time token accounts
179
*/
180
public static void putOneTimeTokenAccounts(RequestContext requestContext, Collection accounts);
181
182
/**
183
* Get one-time token accounts from flow scope
184
* @param requestContext The webflow request context
185
* @return Collection of one-time token accounts
186
*/
187
public static Collection getOneTimeTokenAccounts(RequestContext requestContext);
188
189
/**
190
* Get typed one-time token account from flow scope
191
* @param <T> Type extending OneTimeTokenAccount
192
* @param requestContext The webflow request context
193
* @param clazz Class type of the account
194
* @return Typed account instance or null
195
*/
196
public static <T extends OneTimeTokenAccount> T getOneTimeTokenAccount(RequestContext requestContext, Class<T> clazz);
197
}
198
```
199
200
## Usage Examples
201
202
### Basic Provider Management
203
204
```java
205
// In a webflow action
206
public class MyMfaAction extends BaseCasWebflowAction {
207
208
@Override
209
protected Event doExecuteInternal(RequestContext requestContext) throws Exception {
210
// Get current MFA provider
211
val providerId = MultifactorAuthenticationWebflowUtils
212
.getMultifactorAuthenticationProvider(requestContext);
213
214
if (providerId == null) {
215
// No provider set, determine and store one
216
val selectedProvider = determineAppropriateProvider(requestContext);
217
MultifactorAuthenticationWebflowUtils
218
.putMultifactorAuthenticationProvider(requestContext, selectedProvider);
219
}
220
221
return success();
222
}
223
}
224
```
225
226
### Device Registration Management
227
228
```java
229
// Enable/disable device registration based on policy
230
public class DeviceRegistrationPolicyAction extends BaseCasWebflowAction {
231
232
@Override
233
protected Event doExecuteInternal(RequestContext requestContext) throws Exception {
234
val authentication = WebUtils.getAuthentication(requestContext);
235
val service = WebUtils.getService(requestContext);
236
237
// Check policy for device registration
238
val deviceRegistrationEnabled = shouldAllowDeviceRegistration(authentication, service);
239
240
// Store the decision in webflow scope
241
MultifactorAuthenticationWebflowUtils
242
.putMultifactorDeviceRegistrationEnabled(requestContext, deviceRegistrationEnabled);
243
244
// Provider-specific settings
245
if (isGoogleAuthenticatorProvider()) {
246
MultifactorAuthenticationWebflowUtils
247
.putGoogleAuthenticatorMultipleDeviceRegistrationEnabled(requestContext,
248
shouldAllowMultipleDevices(authentication));
249
}
250
251
return success();
252
}
253
}
254
```
255
256
### Provider Selection Workflow
257
258
```java
259
// Prepare providers for user selection
260
public class PrepareProviderSelectionAction extends BaseCasWebflowAction {
261
262
@Override
263
protected Event doExecuteInternal(RequestContext requestContext) throws Exception {
264
val authentication = WebUtils.getAuthentication(requestContext);
265
val availableProviders = getAvailableProvidersForUser(authentication);
266
267
// Filter providers based on availability and bypass rules
268
val selectableProviders = availableProviders.stream()
269
.filter(provider -> isProviderAvailable(provider, authentication))
270
.filter(provider -> !shouldBypassProvider(provider, authentication))
271
.map(MultifactorAuthenticationProvider::getId)
272
.collect(Collectors.toList());
273
274
// Store selectable providers for the view
275
MultifactorAuthenticationWebflowUtils
276
.putSelectableMultifactorAuthenticationProviders(requestContext, selectableProviders);
277
278
if (selectableProviders.isEmpty()) {
279
return error();
280
} else if (selectableProviders.size() == 1) {
281
// Only one provider available, set it directly
282
val singleProvider = getProviderById(selectableProviders.get(0));
283
MultifactorAuthenticationWebflowUtils
284
.putMultifactorAuthenticationProvider(requestContext, singleProvider);
285
return new EventFactorySupport().event(this, "single");
286
} else {
287
// Multiple providers available, proceed to selection
288
return success();
289
}
290
}
291
}
292
```
293
294
### Token and Credential Management
295
296
```java
297
// Handle simple MFA token lifecycle
298
public class SimpleMfaTokenAction extends BaseCasWebflowAction {
299
300
private final TicketRegistry ticketRegistry;
301
302
@Override
303
protected Event doExecuteInternal(RequestContext requestContext) throws Exception {
304
val authentication = WebUtils.getAuthentication(requestContext);
305
306
// Generate and store token
307
val token = createSimpleMfaToken(authentication);
308
ticketRegistry.addTicket(token);
309
310
MultifactorAuthenticationWebflowUtils
311
.putSimpleMultifactorAuthenticationToken(requestContext, token);
312
313
// Send token to user (SMS, email, etc.)
314
sendTokenToUser(token, authentication.getPrincipal());
315
316
return success();
317
}
318
319
// In verification action
320
protected Event verifyToken(RequestContext requestContext) throws Exception {
321
val submittedToken = requestContext.getRequestParameters().get("token");
322
val storedToken = MultifactorAuthenticationWebflowUtils
323
.getSimpleMultifactorAuthenticationToken(requestContext, SimpleMfaToken.class);
324
325
if (storedToken != null && storedToken.getId().equals(submittedToken)) {
326
// Token verified, clean up
327
MultifactorAuthenticationWebflowUtils
328
.removeSimpleMultifactorAuthenticationToken(requestContext);
329
ticketRegistry.deleteTicket(storedToken.getId());
330
return success();
331
}
332
333
return error();
334
}
335
}
336
```
337
338
### Registered Device Management
339
340
```java
341
// Load and manage registered devices
342
public class LoadRegisteredDevicesAction extends BaseCasWebflowAction {
343
344
private final MultifactorAuthenticationDeviceManager deviceManager;
345
346
@Override
347
protected Event doExecuteInternal(RequestContext requestContext) throws Exception {
348
val authentication = WebUtils.getAuthentication(requestContext);
349
val principal = authentication.getPrincipal();
350
351
// Load registered devices for the user
352
val registeredDevices = deviceManager.findRegisteredDevices(principal);
353
354
// Store in webflow scope for view access
355
MultifactorAuthenticationWebflowUtils
356
.putMultifactorAuthenticationRegisteredDevices(requestContext, registeredDevices);
357
358
// Also store as one-time token accounts if applicable
359
val tokenAccounts = registeredDevices.stream()
360
.filter(device -> device instanceof OneTimeTokenAccount)
361
.map(device -> (OneTimeTokenAccount) device)
362
.collect(Collectors.toSet());
363
364
if (!tokenAccounts.isEmpty()) {
365
MultifactorAuthenticationWebflowUtils
366
.putOneTimeTokenAccounts(requestContext, tokenAccounts);
367
}
368
369
return success();
370
}
371
}
372
```
373
374
### Custom Webflow Scope Integration
375
376
```java
377
// Custom utility methods extending the base utilities
378
@UtilityClass
379
public class CustomMfaWebflowUtils {
380
381
/**
382
* Store custom MFA context data
383
*/
384
public static void putCustomMfaContext(RequestContext context, String key, Object value) {
385
context.getFlowScope().put("customMfa_" + key, value);
386
}
387
388
/**
389
* Get custom MFA context data
390
*/
391
public static <T> T getCustomMfaContext(RequestContext context, String key, Class<T> type) {
392
return context.getFlowScope().get("customMfa_" + key, type);
393
}
394
395
/**
396
* Check if user has completed MFA recently (using conversation scope for session-wide storage)
397
*/
398
public static boolean hasRecentMfaCompletion(RequestContext context, String providerId) {
399
val completions = context.getConversationScope()
400
.get("recentMfaCompletions", Map.class);
401
if (completions != null) {
402
val lastCompletion = (Long) completions.get(providerId);
403
return lastCompletion != null &&
404
(System.currentTimeMillis() - lastCompletion) < Duration.ofMinutes(5).toMillis();
405
}
406
return false;
407
}
408
409
/**
410
* Record MFA completion timestamp
411
*/
412
public static void recordMfaCompletion(RequestContext context, String providerId) {
413
val completions = context.getConversationScope()
414
.get("recentMfaCompletions", Map.class);
415
val completionMap = completions != null ? completions : new HashMap<String, Long>();
416
completionMap.put(providerId, System.currentTimeMillis());
417
context.getConversationScope().put("recentMfaCompletions", completionMap);
418
}
419
}
420
```
421
422
## Scope Usage Guidelines
423
424
- **Flow Scope**: Use for data that needs to persist across the entire webflow execution (e.g., selected provider, tokens)
425
- **Conversation Scope**: Use for data that should persist across multiple webflow executions in the same session (e.g., resolved providers)
426
- **View Scope**: Use for data that's only needed for rendering the current view (e.g., selectable providers list)
427
428
The utility class automatically manages the appropriate scopes for different types of MFA data, ensuring optimal performance and proper cleanup.