Merge pull request #3031 from mposolda/master
KEYCLOAK-3220 redirect to client with error if possible
This commit is contained in:
commit
eeabc0092b
9 changed files with 124 additions and 39 deletions
|
@ -22,11 +22,22 @@ package org.keycloak;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class OAuthErrorException extends Exception {
|
public class OAuthErrorException extends Exception {
|
||||||
|
// OAuth2
|
||||||
public static final String INVALID_REQUEST = "invalid_request";
|
public static final String INVALID_REQUEST = "invalid_request";
|
||||||
|
public static final String INVALID_SCOPE = "invalid_scope";
|
||||||
|
public static final String UNAUTHORIZED_CLIENT = "unauthorized_client";
|
||||||
|
public static final String ACCESS_DENIED = "access_denied";
|
||||||
|
public static final String UNSUPPORTED_RESPONSE_TYPE = "unsupported_response_type";
|
||||||
|
public static final String SERVER_ERROR = "server_error";
|
||||||
|
public static final String TEMPORARILY_UNAVAILABKE = "temporarily_unavailable";
|
||||||
|
|
||||||
|
// OpenID Connect 1
|
||||||
|
public static final String INTERACTION_REQUIRED = "interaction_required";
|
||||||
|
public static final String LOGIN_REQUIRED = "login_required";
|
||||||
|
|
||||||
|
// Others
|
||||||
public static final String INVALID_CLIENT = "invalid_client";
|
public static final String INVALID_CLIENT = "invalid_client";
|
||||||
public static final String INVALID_GRANT = "invalid_grant";
|
public static final String INVALID_GRANT = "invalid_grant";
|
||||||
public static final String INVALID_SCOPE = "invalid_grant";
|
|
||||||
public static final String UNAUTHORIZED_CLIENT = "unauthorized_client";
|
|
||||||
public static final String UNSUPPORTED_GRANT_TYPE = "unsupported_grant_type";
|
public static final String UNSUPPORTED_GRANT_TYPE = "unsupported_grant_type";
|
||||||
public static final String INVALID_TOKEN = "invalid_token";
|
public static final String INVALID_TOKEN = "invalid_token";
|
||||||
public static final String INSUFFICIENT_SCOPE = "insufficient_scope";
|
public static final String INSUFFICIENT_SCOPE = "insufficient_scope";
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.keycloak.protocol.oidc;
|
package org.keycloak.protocol.oidc;
|
||||||
|
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
|
import org.keycloak.OAuthErrorException;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.EventBuilder;
|
import org.keycloak.events.EventBuilder;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
|
@ -193,14 +194,14 @@ public class OIDCLoginProtocol implements LoginProtocol {
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case CANCELLED_BY_USER:
|
case CANCELLED_BY_USER:
|
||||||
case CONSENT_DENIED:
|
case CONSENT_DENIED:
|
||||||
return "access_denied";
|
return OAuthErrorException.ACCESS_DENIED;
|
||||||
case PASSIVE_INTERACTION_REQUIRED:
|
case PASSIVE_INTERACTION_REQUIRED:
|
||||||
return "interaction_required";
|
return OAuthErrorException.INTERACTION_REQUIRED;
|
||||||
case PASSIVE_LOGIN_REQUIRED:
|
case PASSIVE_LOGIN_REQUIRED:
|
||||||
return "login_required";
|
return OAuthErrorException.LOGIN_REQUIRED;
|
||||||
default:
|
default:
|
||||||
logger.untranslatedProtocol(error.name());
|
logger.untranslatedProtocol(error.name());
|
||||||
return "access_denied";
|
return OAuthErrorException.SERVER_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,8 @@ import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import org.keycloak.OAuth2Constants;
|
||||||
|
import org.keycloak.OAuthErrorException;
|
||||||
import org.keycloak.authentication.AuthenticationProcessor;
|
import org.keycloak.authentication.AuthenticationProcessor;
|
||||||
import org.keycloak.constants.AdapterConstants;
|
import org.keycloak.constants.AdapterConstants;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
|
@ -40,6 +42,7 @@ import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.protocol.AuthorizationEndpointBase;
|
import org.keycloak.protocol.AuthorizationEndpointBase;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
|
import org.keycloak.protocol.oidc.utils.OIDCRedirectUriBuilder;
|
||||||
import org.keycloak.protocol.oidc.utils.OIDCResponseMode;
|
import org.keycloak.protocol.oidc.utils.OIDCResponseMode;
|
||||||
import org.keycloak.protocol.oidc.utils.OIDCResponseType;
|
import org.keycloak.protocol.oidc.utils.OIDCResponseType;
|
||||||
import org.keycloak.protocol.oidc.utils.RedirectUtils;
|
import org.keycloak.protocol.oidc.utils.RedirectUtils;
|
||||||
|
@ -146,9 +149,12 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
|
||||||
|
|
||||||
checkSsl();
|
checkSsl();
|
||||||
checkRealm();
|
checkRealm();
|
||||||
checkResponseType();
|
|
||||||
checkClient();
|
checkClient();
|
||||||
checkRedirectUri();
|
checkRedirectUri();
|
||||||
|
Response errorResponse = checkResponseType();
|
||||||
|
if (errorResponse != null) {
|
||||||
|
return errorResponse;
|
||||||
|
}
|
||||||
|
|
||||||
createClientSession();
|
createClientSession();
|
||||||
// So back button doesn't work
|
// So back button doesn't work
|
||||||
|
@ -236,28 +242,25 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
|
||||||
throw new ErrorPageException(session, Messages.CLIENT_NOT_FOUND);
|
throw new ErrorPageException(session, Messages.CLIENT_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!client.isEnabled()) {
|
||||||
|
event.error(Errors.CLIENT_DISABLED);
|
||||||
|
throw new ErrorPageException(session, Messages.CLIENT_DISABLED);
|
||||||
|
}
|
||||||
|
|
||||||
if (client.isBearerOnly()) {
|
if (client.isBearerOnly()) {
|
||||||
event.error(Errors.NOT_ALLOWED);
|
event.error(Errors.NOT_ALLOWED);
|
||||||
throw new ErrorPageException(session, Messages.BEARER_ONLY);
|
throw new ErrorPageException(session, Messages.BEARER_ONLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((parsedResponseType.hasResponseType(OIDCResponseType.CODE) || parsedResponseType.hasResponseType(OIDCResponseType.NONE)) && !client.isStandardFlowEnabled()) {
|
|
||||||
event.error(Errors.NOT_ALLOWED);
|
|
||||||
throw new ErrorPageException(session, Messages.STANDARD_FLOW_DISABLED);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parsedResponseType.isImplicitOrHybridFlow() && !client.isImplicitFlowEnabled()) {
|
|
||||||
event.error(Errors.NOT_ALLOWED);
|
|
||||||
throw new ErrorPageException(session, Messages.IMPLICIT_FLOW_DISABLED);
|
|
||||||
}
|
|
||||||
|
|
||||||
session.getContext().setClient(client);
|
session.getContext().setClient(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkResponseType() {
|
private Response checkResponseType() {
|
||||||
|
OIDCResponseMode defaultResponseMode = client.isImplicitFlowEnabled() ? OIDCResponseMode.FRAGMENT : OIDCResponseMode.QUERY;
|
||||||
|
|
||||||
if (responseType == null) {
|
if (responseType == null) {
|
||||||
event.error(Errors.INVALID_REQUEST);
|
event.error(Errors.INVALID_REQUEST);
|
||||||
throw new ErrorPageException(session, Messages.MISSING_PARAMETER, OIDCLoginProtocol.RESPONSE_TYPE_PARAM);
|
return redirectErrorToClient(defaultResponseMode, OAuthErrorException.INVALID_REQUEST, "Missing parameter: response_type");
|
||||||
}
|
}
|
||||||
|
|
||||||
event.detail(Details.RESPONSE_TYPE, responseType);
|
event.detail(Details.RESPONSE_TYPE, responseType);
|
||||||
|
@ -270,24 +273,51 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
|
||||||
} catch (IllegalArgumentException iae) {
|
} catch (IllegalArgumentException iae) {
|
||||||
logger.error(iae);
|
logger.error(iae);
|
||||||
event.error(Errors.INVALID_REQUEST);
|
event.error(Errors.INVALID_REQUEST);
|
||||||
throw new ErrorPageException(session, Messages.INVALID_PARAMETER, OIDCLoginProtocol.RESPONSE_TYPE_PARAM);
|
return redirectErrorToClient(defaultResponseMode, OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OIDCResponseMode parsedResponseMode = null;
|
||||||
try {
|
try {
|
||||||
OIDCResponseMode parsedResponseMode = OIDCResponseMode.parse(responseMode, parsedResponseType);
|
parsedResponseMode = OIDCResponseMode.parse(responseMode, parsedResponseType);
|
||||||
event.detail(Details.RESPONSE_MODE, parsedResponseMode.toString().toLowerCase());
|
|
||||||
|
|
||||||
// Disallowed by OIDC specs
|
|
||||||
if (parsedResponseType.isImplicitOrHybridFlow() && parsedResponseMode == OIDCResponseMode.QUERY) {
|
|
||||||
logger.responseModeQueryNotAllowed();
|
|
||||||
event.error(Errors.INVALID_REQUEST);
|
|
||||||
throw new ErrorPageException(session, Messages.INVALID_PARAMETER, OIDCLoginProtocol.RESPONSE_MODE_PARAM);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IllegalArgumentException iae) {
|
} catch (IllegalArgumentException iae) {
|
||||||
event.error(Errors.INVALID_REQUEST);
|
event.error(Errors.INVALID_REQUEST);
|
||||||
throw new ErrorPageException(session, Messages.INVALID_PARAMETER, OIDCLoginProtocol.RESPONSE_MODE_PARAM);
|
return redirectErrorToClient(defaultResponseMode, OAuthErrorException.INVALID_REQUEST, "Invalid parameter: response_mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event.detail(Details.RESPONSE_MODE, parsedResponseMode.toString().toLowerCase());
|
||||||
|
|
||||||
|
// Disallowed by OIDC specs
|
||||||
|
if (parsedResponseType.isImplicitOrHybridFlow() && parsedResponseMode == OIDCResponseMode.QUERY) {
|
||||||
|
event.error(Errors.INVALID_REQUEST);
|
||||||
|
return redirectErrorToClient(defaultResponseMode, OAuthErrorException.INVALID_REQUEST, "Response_mode 'query' not allowed for implicit or hybrid flow");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((parsedResponseType.hasResponseType(OIDCResponseType.CODE) || parsedResponseType.hasResponseType(OIDCResponseType.NONE)) && !client.isStandardFlowEnabled()) {
|
||||||
|
event.error(Errors.NOT_ALLOWED);
|
||||||
|
return redirectErrorToClient(parsedResponseMode, OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE, "Client is not allowed to initiate browser login with given response_type. Standard flow is disabled for the client.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsedResponseType.isImplicitOrHybridFlow() && !client.isImplicitFlowEnabled()) {
|
||||||
|
event.error(Errors.NOT_ALLOWED);
|
||||||
|
return redirectErrorToClient(parsedResponseMode, OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE, "Client is not allowed to initiate browser login with given response_type. Implicit flow is disabled for the client.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Response redirectErrorToClient(OIDCResponseMode responseMode, String error, String errorDescription) {
|
||||||
|
OIDCRedirectUriBuilder errorResponseBuilder = OIDCRedirectUriBuilder.fromUri(redirectUri, responseMode)
|
||||||
|
.addParam(OAuth2Constants.ERROR, error);
|
||||||
|
|
||||||
|
if (errorDescription != null) {
|
||||||
|
errorResponseBuilder.addParam(OAuth2Constants.ERROR_DESCRIPTION, errorDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state != null) {
|
||||||
|
errorResponseBuilder.addParam(OAuth2Constants.STATE, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorResponseBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkRedirectUri() {
|
private void checkRedirectUri() {
|
||||||
|
|
|
@ -39,6 +39,7 @@ public abstract class OIDCRedirectUriBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract OIDCRedirectUriBuilder addParam(String paramName, String paramValue);
|
public abstract OIDCRedirectUriBuilder addParam(String paramName, String paramValue);
|
||||||
|
|
||||||
public abstract Response build();
|
public abstract Response build();
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ public class OIDCResponseType {
|
||||||
throw new IllegalStateException("No responseType provided");
|
throw new IllegalStateException("No responseType provided");
|
||||||
}
|
}
|
||||||
if (responseTypes.contains(NONE) && responseTypes.size() > 1) {
|
if (responseTypes.contains(NONE) && responseTypes.size() > 1) {
|
||||||
throw new IllegalArgumentException("None not allowed with some other response_type");
|
throw new IllegalArgumentException("'None' not allowed with some other response_type");
|
||||||
}
|
}
|
||||||
|
|
||||||
// response_type value "token" alone is not mentioned in OIDC specification, however it is supported by OAuth2. We allow it just to be compatible with pure OAuth2 clients like swagger.ui
|
// response_type value "token" alone is not mentioned in OIDC specification, however it is supported by OAuth2. We allow it just to be compatible with pure OAuth2 clients like swagger.ui
|
||||||
|
|
|
@ -188,6 +188,8 @@ public class Messages {
|
||||||
|
|
||||||
public static final String CLIENT_NOT_FOUND = "clientNotFoundMessage";
|
public static final String CLIENT_NOT_FOUND = "clientNotFoundMessage";
|
||||||
|
|
||||||
|
public static final String CLIENT_DISABLED = "clientDisabledMessage";
|
||||||
|
|
||||||
public static final String INVALID_PARAMETER = "invalidParameterMessage";
|
public static final String INVALID_PARAMETER = "invalidParameterMessage";
|
||||||
|
|
||||||
public static final String IDENTITY_PROVIDER_LOGIN_FAILURE = "identityProviderLoginFailure";
|
public static final String IDENTITY_PROVIDER_LOGIN_FAILURE = "identityProviderLoginFailure";
|
||||||
|
|
|
@ -57,7 +57,9 @@ import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -449,7 +451,11 @@ public class OAuthClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCurrentRequest() {
|
public String getCurrentRequest() {
|
||||||
return driver.getCurrentUrl().substring(0, driver.getCurrentUrl().indexOf('?'));
|
int index = driver.getCurrentUrl().indexOf('?');
|
||||||
|
if (index == -1) {
|
||||||
|
index = driver.getCurrentUrl().indexOf('#');
|
||||||
|
}
|
||||||
|
return driver.getCurrentUrl().substring(0, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public URI getCurrentUri() {
|
public URI getCurrentUri() {
|
||||||
|
@ -469,6 +475,18 @@ public class OAuthClient {
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getCurrentFragment() {
|
||||||
|
Map<String, String> m = new HashMap<String, String>();
|
||||||
|
|
||||||
|
String fragment = getCurrentUri().getRawFragment();
|
||||||
|
List<NameValuePair> pairs = (fragment == null || fragment.isEmpty()) ? Collections.emptyList() : URLEncodedUtils.parse(fragment, Charset.forName("UTF-8"));
|
||||||
|
|
||||||
|
for (NameValuePair p : pairs) {
|
||||||
|
m.put(p.getName(), p.getValue());
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
public void openLoginForm() {
|
public void openLoginForm() {
|
||||||
driver.navigate().to(getLoginFormUrl());
|
driver.navigate().to(getLoginFormUrl());
|
||||||
}
|
}
|
||||||
|
@ -624,12 +642,20 @@ public class OAuthClient {
|
||||||
private String code;
|
private String code;
|
||||||
private String state;
|
private String state;
|
||||||
private String error;
|
private String error;
|
||||||
|
private String errorDescription;
|
||||||
|
|
||||||
public AuthorizationCodeResponse(OAuthClient client) {
|
public AuthorizationCodeResponse(OAuthClient client) {
|
||||||
|
this(client, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthorizationCodeResponse(OAuthClient client, boolean fragment) {
|
||||||
isRedirected = client.getCurrentRequest().equals(client.getRedirectUri());
|
isRedirected = client.getCurrentRequest().equals(client.getRedirectUri());
|
||||||
code = client.getCurrentQuery().get(OAuth2Constants.CODE);
|
Map<String, String> params = fragment ? client.getCurrentFragment() : client.getCurrentQuery();
|
||||||
state = client.getCurrentQuery().get(OAuth2Constants.STATE);
|
|
||||||
error = client.getCurrentQuery().get(OAuth2Constants.ERROR);
|
code = params.get(OAuth2Constants.CODE);
|
||||||
|
state = params.get(OAuth2Constants.STATE);
|
||||||
|
error = params.get(OAuth2Constants.ERROR);
|
||||||
|
errorDescription = params.get(OAuth2Constants.ERROR_DESCRIPTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRedirected() {
|
public boolean isRedirected() {
|
||||||
|
@ -648,6 +674,9 @@ public class OAuthClient {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getErrorDescription() {
|
||||||
|
return errorDescription;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class AccessTokenResponse {
|
public static class AccessTokenResponse {
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
|
import org.keycloak.OAuthErrorException;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.Errors;
|
import org.keycloak.events.Errors;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
|
@ -148,7 +149,12 @@ public class AuthorizationCodeTest extends AbstractKeycloakTest {
|
||||||
oauth.responseType("token id_token");
|
oauth.responseType("token id_token");
|
||||||
UriBuilder b = UriBuilder.fromUri(oauth.getLoginFormUrl());
|
UriBuilder b = UriBuilder.fromUri(oauth.getLoginFormUrl());
|
||||||
driver.navigate().to(b.build().toURL());
|
driver.navigate().to(b.build().toURL());
|
||||||
assertEquals("Client is not allowed to initiate browser login with given response_type. Implicit flow is disabled for the client.", errorPage.getError());
|
|
||||||
|
OAuthClient.AuthorizationCodeResponse errorResponse = new OAuthClient.AuthorizationCodeResponse(oauth, true);
|
||||||
|
Assert.assertTrue(errorResponse.isRedirected());
|
||||||
|
Assert.assertEquals(errorResponse.getError(), OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE);
|
||||||
|
Assert.assertEquals(errorResponse.getErrorDescription(), "Client is not allowed to initiate browser login with given response_type. Implicit flow is disabled for the client.");
|
||||||
|
|
||||||
events.expectLogin().error(Errors.NOT_ALLOWED).user((String) null).session((String) null).clearDetails().detail(Details.RESPONSE_TYPE, "token id_token").assertEvent();
|
events.expectLogin().error(Errors.NOT_ALLOWED).user((String) null).session((String) null).clearDetails().detail(Details.RESPONSE_TYPE, "token id_token").assertEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,8 +163,12 @@ public class AuthorizationCodeTest extends AbstractKeycloakTest {
|
||||||
oauth.responseType("tokenn");
|
oauth.responseType("tokenn");
|
||||||
UriBuilder b = UriBuilder.fromUri(oauth.getLoginFormUrl());
|
UriBuilder b = UriBuilder.fromUri(oauth.getLoginFormUrl());
|
||||||
driver.navigate().to(b.build().toURL());
|
driver.navigate().to(b.build().toURL());
|
||||||
assertEquals("Invalid parameter: response_type", errorPage.getError());
|
|
||||||
events.expectLogin().error(Errors.INVALID_REQUEST).client((String) null).user((String) null).session((String) null).clearDetails().detail(Details.RESPONSE_TYPE, "tokenn").assertEvent();
|
OAuthClient.AuthorizationCodeResponse errorResponse = new OAuthClient.AuthorizationCodeResponse(oauth);
|
||||||
|
Assert.assertTrue(errorResponse.isRedirected());
|
||||||
|
Assert.assertEquals(errorResponse.getError(), OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE);
|
||||||
|
|
||||||
|
events.expectLogin().error(Errors.INVALID_REQUEST).user((String) null).session((String) null).clearDetails().detail(Details.RESPONSE_TYPE, "tokenn").assertEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
// KEYCLOAK-3281
|
// KEYCLOAK-3281
|
||||||
|
|
|
@ -221,6 +221,7 @@ locale_ru=\u0420\u0443\u0441\u0441\u043A\u0438\u0439
|
||||||
backToApplication=« Back to Application
|
backToApplication=« Back to Application
|
||||||
missingParameterMessage=Missing parameters\: {0}
|
missingParameterMessage=Missing parameters\: {0}
|
||||||
clientNotFoundMessage=Client not found.
|
clientNotFoundMessage=Client not found.
|
||||||
|
clientDisabledMessage=Client disabled.
|
||||||
invalidParameterMessage=Invalid parameter\: {0}
|
invalidParameterMessage=Invalid parameter\: {0}
|
||||||
alreadyLoggedIn=You are already logged in.
|
alreadyLoggedIn=You are already logged in.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue