diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index 0ea6ad9..184df85 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -57,6 +57,7 @@ public class AuthenticationProcessor {
protected String flowPath;
protected boolean browserFlow;
protected BruteForceProtector protector;
+ protected boolean oneActionWasSuccessful;
/**
* This could be an error message forwarded from another authenticator
*/
@@ -761,6 +762,28 @@ public class AuthenticationProcessor {
return challenge;
}
+ /**
+ * Marks that at least one action was successful
+ *
+ */
+ public void setActionSuccessful() {
+ oneActionWasSuccessful = true;
+ }
+
+ public Response checkWasSuccessfulBrowserAction() {
+ if (oneActionWasSuccessful && isBrowserFlow()) {
+ // redirect to non-action url so browser refresh button works without reposting past data
+ String code = generateCode();
+
+ URI redirect = LoginActionsService.loginActionsBaseUrl(getUriInfo())
+ .path(flowPath)
+ .queryParam(OAuth2Constants.CODE, code).build(getRealm().getName());
+ return Response.status(302).location(redirect).build();
+ } else {
+ return null;
+ }
+ }
+
public void attachSession() {
String username = clientSession.getAuthenticatedUser().getUsername();
String attemptedUsername = clientSession.getNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME);
diff --git a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
index ff140b6..46e651f 100755
--- a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
+++ b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
@@ -68,6 +68,10 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
Response response = processResult(result);
if (response == null) {
processor.getClientSession().removeNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
+ if (result.status == FlowStatus.SUCCESS) {
+ // we do this so that flow can redirect to a non-action URL
+ processor.setActionSuccessful();
+ }
return processFlow();
} else return response;
}
@@ -153,6 +157,10 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
}
}
}
+ // skip if action as successful already
+ Response redirect = processor.checkWasSuccessfulBrowserAction();
+ if (redirect != null) return redirect;
+
AuthenticationProcessor.Result context = processor.createAuthenticatorContext(model, authenticator, executions);
logger.debug("invoke authenticator.authenticate");
authenticator.authenticate(context);
diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
index 2967b67..e46cbca 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -812,15 +812,16 @@ public class LoginActionsService {
final ClientSessionCode clientCode = checks.clientCode;
final ClientSessionModel clientSession = clientCode.getClientSession();
- if (clientSession.getUserSession() == null) {
+ final UserSessionModel userSession = clientSession.getUserSession();
+ if (userSession == null) {
logger.userSessionNull();
event.error(Errors.USER_SESSION_NOT_FOUND);
throw new WebApplicationException(ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE));
}
- if (action == null && clientSession.getUserSession() != null) { // do next required action only if user is already authenticated
+ if (action == null && userSession != null) { // do next required action only if user is already authenticated
initEvent(clientSession);
event.event(EventType.LOGIN);
- return AuthenticationManager.nextActionAfterAuthentication(session, clientSession.getUserSession(), clientSession, clientConnection, request, uriInfo, event);
+ return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
}
if (!action.equals(clientSession.getNote(AuthenticationManager.CURRENT_REQUIRED_ACTION))) {
@@ -841,7 +842,7 @@ public class LoginActionsService {
event.event(EventType.CUSTOM_REQUIRED_ACTION);
- RequiredActionContextResult context = new RequiredActionContextResult(clientSession.getUserSession(), clientSession, realm, event, session, request, clientSession.getUserSession().getUser(), factory) {
+ RequiredActionContextResult context = new RequiredActionContextResult(userSession, clientSession, realm, event, session, request, userSession.getUser(), factory) {
@Override
public void ignore() {
throw new RuntimeException("Cannot call ignore within processAction()");
@@ -850,16 +851,19 @@ public class LoginActionsService {
provider.processAction(context);
if (context.getStatus() == RequiredActionContext.Status.SUCCESS) {
event.clone().success();
- // do both
- clientSession.removeRequiredAction(factory.getId());
- clientSession.getUserSession().getUser().removeRequiredAction(factory.getId());
- clientSession.removeNote(AuthenticationManager.CURRENT_REQUIRED_ACTION);
- // redirect to a generic code URI so that browser refresh will work
- //return redirectToRequiredActions(code);
- event.removeDetail(Details.CUSTOM_REQUIRED_ACTION);
initEvent(clientSession);
event.event(EventType.LOGIN);
- return AuthenticationManager.nextActionAfterAuthentication(session, clientSession.getUserSession(), clientSession, clientConnection, request, uriInfo, event);
+ clientSession.removeRequiredAction(factory.getId());
+ userSession.getUser().removeRequiredAction(factory.getId());
+ clientSession.removeNote(AuthenticationManager.CURRENT_REQUIRED_ACTION);
+
+ if (AuthenticationManager.isActionRequired(session, userSession, clientSession, clientConnection, request, uriInfo, event)) {
+ // redirect to a generic code URI so that browser refresh will work
+ return redirectToRequiredActions(code);
+ } else {
+ return AuthenticationManager.finishedRequiredActions(session, userSession, clientSession, clientConnection, request, uriInfo, event);
+
+ }
}
if (context.getStatus() == RequiredActionContext.Status.CHALLENGE) {
return context.getChallenge();