FAPI 2.0 security profile - supporting RFC 9207 OAuth 2.0 Authorization Server Issuer Identification

Closes #20584
This commit is contained in:
Takashi Norimatsu 2023-05-27 19:36:08 +09:00 committed by Marek Posolda
parent 821316a61a
commit 2efd79f982
28 changed files with 318 additions and 38 deletions

View file

@ -384,7 +384,8 @@ public class OAuthRequestAuthenticator {
KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(facade.getRequest().getURI()) KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(facade.getRequest().getURI())
.replaceQueryParam(OAuth2Constants.CODE, null) .replaceQueryParam(OAuth2Constants.CODE, null)
.replaceQueryParam(OAuth2Constants.STATE, null) .replaceQueryParam(OAuth2Constants.STATE, null)
.replaceQueryParam(OAuth2Constants.SESSION_STATE, null); .replaceQueryParam(OAuth2Constants.SESSION_STATE, null)
.replaceQueryParam(OAuth2Constants.ISSUER, null);
return builder.buildAsString(); return builder.buildAsString();
} }

View file

@ -290,7 +290,8 @@ public class ServerRequest {
protected static String stripOauthParametersFromRedirect(String uri) { protected static String stripOauthParametersFromRedirect(String uri) {
KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(uri) KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(uri)
.replaceQueryParam(OAuth2Constants.CODE, null) .replaceQueryParam(OAuth2Constants.CODE, null)
.replaceQueryParam(OAuth2Constants.STATE, null); .replaceQueryParam(OAuth2Constants.STATE, null)
.replaceQueryParam(OAuth2Constants.ISSUER, null);
return builder.buildAsString(); return builder.buildAsString();
} }

View file

@ -145,6 +145,9 @@ public interface OAuth2Constants {
// https://openid.net/specs/openid-financial-api-jarm-ID1.html // https://openid.net/specs/openid-financial-api-jarm-ID1.html
String RESPONSE = "response"; String RESPONSE = "response";
// https://www.rfc-editor.org/rfc/rfc9207.html
String ISSUER = "iss";
} }

View file

@ -193,6 +193,9 @@ public class OIDCConfigurationRepresentation {
@JsonProperty("mtls_endpoint_aliases") @JsonProperty("mtls_endpoint_aliases")
private MTLSEndpointAliases mtlsEndpointAliases; private MTLSEndpointAliases mtlsEndpointAliases;
@JsonProperty("authorization_response_iss_parameter_supported")
private Boolean authorizationResponseIssParameterSupported;
protected Map<String, Object> otherClaims = new HashMap<String, Object>(); protected Map<String, Object> otherClaims = new HashMap<String, Object>();
public String getIssuer() { public String getIssuer() {
@ -624,4 +627,13 @@ public class OIDCConfigurationRepresentation {
public Boolean getFrontChannelLogoutSupported() { public Boolean getFrontChannelLogoutSupported() {
return frontChannelLogoutSupported; return frontChannelLogoutSupported;
} }
public Boolean getAuthorizationResponseIssParameterSupported() {
return authorizationResponseIssParameterSupported;
}
public void setAuthorizationResponseIssParameterSupported(Boolean authorizationResponseIssParameterSupported) {
this.authorizationResponseIssParameterSupported = authorizationResponseIssParameterSupported;
}
} }

View file

@ -0,0 +1,13 @@
= Added iss parameter to OAuth 2.0/OpenID Connect Authentication Response
RFC 9207 OAuth 2.0 Authorization Server Issuer Identification specification adds the parameter `iss` in the OAuth 2.0/OpenID Connect Authentication Response for realizing secure authorization responses.
In past releases, we did not have this parameter, but now {project_name} adds this parameter by default, as required by the specification.
However, some OpenID Connect / OAuth2 adapters, and especially older {project_name} adapters, may have issues with this new parameter.
For example, the parameter will be always present in the browser URL after successful authentication to the client application.
In these cases, it may be useful to disable adding the `iss` parameter to the authentication response. This can be done
for the particular client in the {project_name} Admin console, in client details in the section with `OpenID Connect Compatibility Modes`,
described in <<_compatibility_with_older_adapters>>. Dedicated `Exclude Issuer From Authentication Response` switch exists,
which can be turned on to prevent adding the `iss` parameter to the authentication response.

View file

@ -1,5 +1,9 @@
== Migration Changes == Migration Changes
=== Migrating to 23.0.0
include::changes-23_0_0.adoc[leveloffset=3]
=== Migrating to 22.0.0 === Migrating to 22.0.0
include::changes-22_0_0.adoc[leveloffset=3] include::changes-22_0_0.adoc[leveloffset=3]

View file

@ -96,6 +96,7 @@
"authorizationEncryptedResponseEnc": "JWA Algorithm used for content encryption in encrypting the authorization response when the response mode is jwt. This option is needed if you want encrypted authorization response. If left empty, the authorization response is just signed, but not encrypted.", "authorizationEncryptedResponseEnc": "JWA Algorithm used for content encryption in encrypting the authorization response when the response mode is jwt. This option is needed if you want encrypted authorization response. If left empty, the authorization response is just signed, but not encrypted.",
"openIdConnectCompatibilityModes": "This section is used to configure settings for backward compatibility with older OpenID Connect / OAuth 2 adaptors. It's useful especially if your client uses older version of Keycloak / RH-SSO adapter.", "openIdConnectCompatibilityModes": "This section is used to configure settings for backward compatibility with older OpenID Connect / OAuth 2 adaptors. It's useful especially if your client uses older version of Keycloak / RH-SSO adapter.",
"excludeSessionStateFromAuthenticationResponse": "If this is on, the parameter 'session_state' will not be included in OpenID Connect Authentication Response. It is useful if your client uses older OIDC / OAuth2 adapter, which does not support 'session_state' parameter.", "excludeSessionStateFromAuthenticationResponse": "If this is on, the parameter 'session_state' will not be included in OpenID Connect Authentication Response. It is useful if your client uses older OIDC / OAuth2 adapter, which does not support 'session_state' parameter.",
"excludeIssuerFromAuthenticationResponse": "If this is on, the parameter 'iss' will not be included in OpenID Connect Authentication Response. It is useful if your client uses older OIDC / OAuth2 adapter, which does not support 'session_state' parameter.",
"useRefreshTokens": "If this is on, a refresh_token will be created and added to the token response. If this is off then no refresh_token will be generated.", "useRefreshTokens": "If this is on, a refresh_token will be created and added to the token response. If this is off then no refresh_token will be generated.",
"useRefreshTokenForClientCredentialsGrant": "If this is on, a refresh_token will be created and added to the token response if the client_credentials grant is used. The OAuth 2.0 RFC6749 Section 4.4.3 states that a refresh_token should not be generated when client_credentials grant is used. If this is off then no refresh_token will be generated and the associated user session will be removed.", "useRefreshTokenForClientCredentialsGrant": "If this is on, a refresh_token will be created and added to the token response if the client_credentials grant is used. The OAuth 2.0 RFC6749 Section 4.4.3 states that a refresh_token should not be generated when client_credentials grant is used. If this is off then no refresh_token will be generated and the associated user session will be removed.",
"useLowerCaseBearerType": "If this is on, token responses will be set the with the type \"bearer\" in lower-case. By default, the server sets the type as \"Bearer\" as defined by RFC6750.", "useLowerCaseBearerType": "If this is on, token responses will be set the with the type \"bearer\" in lower-case. By default, the server sets the type as \"Bearer\" as defined by RFC6750.",

View file

@ -492,6 +492,7 @@
"authorizationEncryptedResponseEnc": "Authorization response encryption content encryption algorithm", "authorizationEncryptedResponseEnc": "Authorization response encryption content encryption algorithm",
"openIdConnectCompatibilityModes": "Open ID Connect Compatibility Modes", "openIdConnectCompatibilityModes": "Open ID Connect Compatibility Modes",
"excludeSessionStateFromAuthenticationResponse": "Exclude Session State From Authentication Response", "excludeSessionStateFromAuthenticationResponse": "Exclude Session State From Authentication Response",
"excludeIssuerFromAuthenticationResponse": "Exclude Issuer From Authentication Response",
"useRefreshTokens": "Use refresh tokens", "useRefreshTokens": "Use refresh tokens",
"useRefreshTokenForClientCredentialsGrant": "Use refresh tokens for client credentials grant", "useRefreshTokenForClientCredentialsGrant": "Use refresh tokens for client credentials grant",
"useLowerCaseBearerType": "Use lower-case bearer type in token responses", "useLowerCaseBearerType": "Use lower-case bearer type in token responses",

View file

@ -57,6 +57,35 @@ export const OpenIdConnectCompatibilityModes = ({
)} )}
/> />
</FormGroup> </FormGroup>
<FormGroup
label={t("excludeIssuerFromAuthenticationResponse")}
fieldId="excludeIssuerFromAuthenticationResponse"
hasNoPaddingTop
labelIcon={
<HelpItem
helpText={t("clients-help:excludeIssuerFromAuthenticationResponse")}
fieldLabelId="clients:excludeIssuerFromAuthenticationResponse"
/>
}
>
<Controller
name={convertAttributeNameToForm<FormFields>(
"attributes.exclude.issuer.from.auth.response",
)}
defaultValue=""
control={control}
render={({ field }) => (
<Switch
id="excludeIssuerFromAuthenticationResponse-switch"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={field.value === "true"}
onChange={(value) => field.onChange(value.toString())}
aria-label={t("excludeIssuerFromAuthenticationResponse")}
/>
)}
/>
</FormGroup>
<FormGroup <FormGroup
label={t("useRefreshTokens")} label={t("useRefreshTokens")}
fieldId="useRefreshTokens" fieldId="useRefreshTokens"

View file

@ -1077,13 +1077,13 @@ function Keycloak (config) {
var supportedParams; var supportedParams;
switch (kc.flow) { switch (kc.flow) {
case 'standard': case 'standard':
supportedParams = ['code', 'state', 'session_state', 'kc_action_status']; supportedParams = ['code', 'state', 'session_state', 'kc_action_status', 'iss'];
break; break;
case 'implicit': case 'implicit':
supportedParams = ['access_token', 'token_type', 'id_token', 'state', 'session_state', 'expires_in', 'kc_action_status']; supportedParams = ['access_token', 'token_type', 'id_token', 'state', 'session_state', 'expires_in', 'kc_action_status', 'iss'];
break; break;
case 'hybrid': case 'hybrid':
supportedParams = ['access_token', 'token_type', 'id_token', 'code', 'state', 'session_state', 'expires_in', 'kc_action_status']; supportedParams = ['access_token', 'token_type', 'id_token', 'code', 'state', 'session_state', 'expires_in', 'kc_action_status', 'iss'];
break; break;
} }

View file

@ -42,6 +42,7 @@ public final class OIDCConfigAttributes {
public static final String USE_JWKS_STRING = "use.jwks.string"; public static final String USE_JWKS_STRING = "use.jwks.string";
public static final String EXCLUDE_SESSION_STATE_FROM_AUTH_RESPONSE = "exclude.session.state.from.auth.response"; public static final String EXCLUDE_SESSION_STATE_FROM_AUTH_RESPONSE = "exclude.session.state.from.auth.response";
public static final String EXCLUDE_ISSUER_FROM_AUTH_RESPONSE = "exclude.issuer.from.auth.response";
public static final String USE_MTLS_HOK_TOKEN = "tls.client.certificate.bound.access.tokens"; public static final String USE_MTLS_HOK_TOKEN = "tls.client.certificate.bound.access.tokens";

View file

@ -163,6 +163,16 @@ public class OIDCAdvancedConfigWrapper extends AbstractClientConfigWrapper {
setAttribute(OIDCConfigAttributes.EXCLUDE_SESSION_STATE_FROM_AUTH_RESPONSE, val); setAttribute(OIDCConfigAttributes.EXCLUDE_SESSION_STATE_FROM_AUTH_RESPONSE, val);
} }
public boolean isExcludeIssuerFromAuthResponse() {
String excludeIssuerFromAuthResponse = getAttribute(OIDCConfigAttributes.EXCLUDE_ISSUER_FROM_AUTH_RESPONSE);
return Boolean.parseBoolean(excludeIssuerFromAuthResponse);
}
public void setExcludeIssuerFromAuthResponse(boolean excludeIssuerFromAuthResponse) {
String val = String.valueOf(excludeIssuerFromAuthResponse);
setAttribute(OIDCConfigAttributes.EXCLUDE_ISSUER_FROM_AUTH_RESPONSE, val);
}
// KEYCLOAK-6771 Certificate Bound Token // KEYCLOAK-6771 Certificate Bound Token
// https://tools.ietf.org/html/draft-ietf-oauth-mtls-08#section-6.5 // https://tools.ietf.org/html/draft-ietf-oauth-mtls-08#section-6.5
public boolean isUseMtlsHokToken() { public boolean isUseMtlsHokToken() {

View file

@ -223,6 +223,9 @@ public class OIDCLoginProtocol implements LoginProtocol {
if (!clientConfig.isExcludeSessionStateFromAuthResponse()) { if (!clientConfig.isExcludeSessionStateFromAuthResponse()) {
redirectUri.addParam(OAuth2Constants.SESSION_STATE, userSession.getId()); redirectUri.addParam(OAuth2Constants.SESSION_STATE, userSession.getId());
} }
if (!clientConfig.isExcludeIssuerFromAuthResponse()) {
redirectUri.addParam(OAuth2Constants.ISSUER, clientSession.getNote(OIDCLoginProtocol.ISSUER));
}
String nonce = authSession.getClientNote(OIDCLoginProtocol.NONCE_PARAM); String nonce = authSession.getClientNote(OIDCLoginProtocol.NONCE_PARAM);
clientSessionCtx.setAttribute(OIDCLoginProtocol.NONCE_PARAM, nonce); clientSessionCtx.setAttribute(OIDCLoginProtocol.NONCE_PARAM, nonce);
@ -278,6 +281,9 @@ public class OIDCLoginProtocol implements LoginProtocol {
event.error(cpe.getError()); event.error(cpe.getError());
new AuthenticationSessionManager(session).removeAuthenticationSession(realm, authSession, true); new AuthenticationSessionManager(session).removeAuthenticationSession(realm, authSession, true);
redirectUri.addParam(OAuth2Constants.ERROR_DESCRIPTION, cpe.getError()); redirectUri.addParam(OAuth2Constants.ERROR_DESCRIPTION, cpe.getError());
if (!clientConfig.isExcludeIssuerFromAuthResponse()) {
redirectUri.addParam(OAuth2Constants.ISSUER, clientSession.getNote(OIDCLoginProtocol.ISSUER));
}
return redirectUri.build(); return redirectUri.build();
} }

View file

@ -212,6 +212,8 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
MTLSEndpointAliases mtlsEndpointAliases = getMtlsEndpointAliases(config); MTLSEndpointAliases mtlsEndpointAliases = getMtlsEndpointAliases(config);
config.setMtlsEndpointAliases(mtlsEndpointAliases); config.setMtlsEndpointAliases(mtlsEndpointAliases);
config.setAuthorizationResponseIssParameterSupported(true);
config = checkConfigOverride(config); config = checkConfigOverride(config);
return config; return config;
} }

View file

@ -34,6 +34,7 @@ import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.AuthorizationEndpointBase; import org.keycloak.protocol.AuthorizationEndpointBase;
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest; import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest;
import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequestParserProcessor; import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequestParserProcessor;
@ -300,6 +301,11 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
errorResponseBuilder.addParam(OAuth2Constants.STATE, request.getState()); errorResponseBuilder.addParam(OAuth2Constants.STATE, request.getState());
} }
OIDCAdvancedConfigWrapper clientConfig = OIDCAdvancedConfigWrapper.fromClientModel(client);
if (!clientConfig.isExcludeIssuerFromAuthResponse()) {
errorResponseBuilder.addParam(OAuth2Constants.ISSUER, Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
}
return errorResponseBuilder.build(); return errorResponseBuilder.build();
} }

View file

@ -40,7 +40,10 @@
"redirectUris" : [ "http://localhost:8080/hello-world-authz-service/*" ], "redirectUris" : [ "http://localhost:8080/hello-world-authz-service/*" ],
"baseUrl": "http://localhost:8080/hello-world-authz-service", "baseUrl": "http://localhost:8080/hello-world-authz-service",
"adminUrl": "http://localhost:8080/hello-world-authz-service", "adminUrl": "http://localhost:8080/hello-world-authz-service",
"directAccessGrantsEnabled" : true "directAccessGrantsEnabled" : true,
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
} }
] ]
} }

View file

@ -88,7 +88,10 @@
"redirectUris": [ "redirectUris": [
"/servlet-authz-app/*" "/servlet-authz-app/*"
], ],
"secret": "secret" "secret": "secret",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
} }
] ]
} }

View file

@ -338,6 +338,9 @@
} }
], ],
"scopes": [] "scopes": []
},
"attributes" : {
"exclude.issuer.from.auth.response": "true"
} }
} }
] ]

View file

@ -1798,6 +1798,8 @@ public class OAuthClient {
// Just during FAPI JARM response mode JWT // Just during FAPI JARM response mode JWT
private String response; private String response;
private String issuer;
public AuthorizationEndpointResponse(OAuthClient client) { public AuthorizationEndpointResponse(OAuthClient client) {
boolean fragment; boolean fragment;
if (client.responseMode == null || "jwt".equals(client.responseMode)) { if (client.responseMode == null || "jwt".equals(client.responseMode)) {
@ -1830,6 +1832,7 @@ public class OAuthClient {
tokenType = params.get(OAuth2Constants.TOKEN_TYPE); tokenType = params.get(OAuth2Constants.TOKEN_TYPE);
expiresIn = params.get(OAuth2Constants.EXPIRES_IN); expiresIn = params.get(OAuth2Constants.EXPIRES_IN);
response = params.get(OAuth2Constants.RESPONSE); response = params.get(OAuth2Constants.RESPONSE);
issuer = params.get(OAuth2Constants.ISSUER);
} }
public boolean isRedirected() { public boolean isRedirected() {
@ -1875,6 +1878,9 @@ public class OAuthClient {
public String getResponse() { public String getResponse() {
return response; return response;
} }
public String getIssuer() {
return issuer;
}
} }
public static class AuthenticationRequestAcknowledgement { public static class AuthenticationRequestAcknowledgement {

View file

@ -38,6 +38,7 @@ import org.keycloak.models.Constants;
import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService; import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
@ -77,8 +78,11 @@ import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriBuilder; import jakarta.ws.rs.core.UriBuilder;
import java.io.File; import java.io.File;
import java.net.URL; import java.net.URL;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional;
import static org.keycloak.testsuite.admin.ApiUtil.createUserAndResetPasswordWithAdminClient; import static org.keycloak.testsuite.admin.ApiUtil.createUserAndResetPasswordWithAdminClient;
@ -159,6 +163,11 @@ public class BrokerLinkAndTokenExchangeTest extends AbstractServletsAdapterTest
servlet.getRedirectUris().add(uri + "/*"); servlet.getRedirectUris().add(uri + "/*");
servlet.setSecret("password"); servlet.setSecret("password");
servlet.setFullScopeAllowed(true); servlet.setFullScopeAllowed(true);
Map<String, String> attributes = Optional.ofNullable(servlet.getAttributes()).orElse(new HashMap<>());
attributes.put(OIDCConfigAttributes.EXCLUDE_ISSUER_FROM_AUTH_RESPONSE, Boolean.TRUE.toString());
servlet.setAttributes(attributes);
realm.setClients(new LinkedList<>()); realm.setClients(new LinkedList<>());
realm.getClients().add(servlet); realm.getClients().add(servlet);

View file

@ -33,6 +33,7 @@ import org.keycloak.common.util.Base64Url;
import org.keycloak.models.Constants; import org.keycloak.models.Constants;
import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderMapperSyncMode; import org.keycloak.models.IdentityProviderMapperSyncMode;
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
@ -61,6 +62,7 @@ import org.keycloak.util.JsonSerialization;
import jakarta.ws.rs.client.Client; import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.core.UriBuilder; import jakarta.ws.rs.core.UriBuilder;
import java.net.URL; import java.net.URL;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -138,6 +140,11 @@ public class ClientInitiatedAccountLinkTest extends AbstractServletsAdapterTest
servlet.getRedirectUris().add(uri + "/*"); servlet.getRedirectUris().add(uri + "/*");
servlet.setSecret("password"); servlet.setSecret("password");
servlet.setFullScopeAllowed(true); servlet.setFullScopeAllowed(true);
Map<String, String> attributes = Optional.ofNullable(servlet.getAttributes()).orElse(new HashMap<>());
attributes.put(OIDCConfigAttributes.EXCLUDE_ISSUER_FROM_AUTH_RESPONSE, Boolean.TRUE.toString());
servlet.setAttributes(attributes);
realm.setClients(new LinkedList<>()); realm.setClients(new LinkedList<>());
realm.getClients().add(servlet); realm.getClients().add(servlet);
testRealms.add(realm); testRealms.add(realm);

View file

@ -86,6 +86,7 @@ public class AuthorizationCodeTest extends AbstractKeycloakTest {
Assert.assertNotNull(response.getCode()); Assert.assertNotNull(response.getCode());
assertEquals("OpenIdConnect.AuthenticationProperties=2302984sdlk", response.getState()); assertEquals("OpenIdConnect.AuthenticationProperties=2302984sdlk", response.getState());
Assert.assertNull(response.getError()); Assert.assertNull(response.getError());
assertEquals(oauth.AUTH_SERVER_ROOT + "/realms/test", response.getIssuer());
String codeId = events.expectLogin().assertEvent().getDetails().get(Details.CODE_ID); String codeId = events.expectLogin().assertEvent().getDetails().get(Details.CODE_ID);
} }
@ -160,6 +161,7 @@ public class AuthorizationCodeTest extends AbstractKeycloakTest {
Assert.assertNotNull(response.getCode()); Assert.assertNotNull(response.getCode());
Assert.assertNull(response.getState()); Assert.assertNull(response.getState());
Assert.assertNull(response.getError()); Assert.assertNull(response.getError());
assertEquals(oauth.AUTH_SERVER_ROOT + "/realms/test", response.getIssuer());
String codeId = events.expectLogin().assertEvent().getDetails().get(Details.CODE_ID); String codeId = events.expectLogin().assertEvent().getDetails().get(Details.CODE_ID);
} }
@ -173,6 +175,7 @@ public class AuthorizationCodeTest extends AbstractKeycloakTest {
OAuthClient.AuthorizationEndpointResponse errorResponse = new OAuthClient.AuthorizationEndpointResponse(oauth); OAuthClient.AuthorizationEndpointResponse errorResponse = new OAuthClient.AuthorizationEndpointResponse(oauth);
assertTrue(errorResponse.isRedirected()); assertTrue(errorResponse.isRedirected());
Assert.assertEquals(errorResponse.getError(), OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE); Assert.assertEquals(errorResponse.getError(), OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE);
Assert.assertEquals(oauth.AUTH_SERVER_ROOT + "/realms/test", errorResponse.getIssuer());
events.expectLogin().error(Errors.INVALID_REQUEST).user((String) null).session((String) null).clearDetails().detail(Details.RESPONSE_TYPE, "tokenn").assertEvent(); events.expectLogin().error(Errors.INVALID_REQUEST).user((String) null).session((String) null).clearDetails().detail(Details.RESPONSE_TYPE, "tokenn").assertEvent();
} }
@ -284,6 +287,7 @@ public class AuthorizationCodeTest extends AbstractKeycloakTest {
Assert.assertNotNull(response.getCode()); Assert.assertNotNull(response.getCode());
Assert.assertNotNull(response.getState()); Assert.assertNotNull(response.getState());
Assert.assertEquals(oauth.AUTH_SERVER_ROOT + "/realms/test", response.getIssuer());
currentUri = new URI(driver.getCurrentUrl()); currentUri = new URI(driver.getCurrentUrl());
Assert.assertNotNull(currentUri.getRawQuery()); Assert.assertNotNull(currentUri.getRawQuery());

View file

@ -501,6 +501,7 @@ public class OAuthRedirectUriTest extends AbstractKeycloakTest {
.replaceQueryParam(OAuth2Constants.CODE, null) .replaceQueryParam(OAuth2Constants.CODE, null)
.replaceQueryParam(OAuth2Constants.STATE, null) .replaceQueryParam(OAuth2Constants.STATE, null)
.replaceQueryParam(OAuth2Constants.SESSION_STATE, null) .replaceQueryParam(OAuth2Constants.SESSION_STATE, null)
.replaceQueryParam(OAuth2Constants.ISSUER, null)
.build().toString(); .build().toString();
if (browserUrlAfterRedirectFromKeycloak.endsWith("/")) browserUrlAfterRedirectFromKeycloak = browserUrlAfterRedirectFromKeycloak.substring(0, browserUrlAfterRedirectFromKeycloak.length() - 1); if (browserUrlAfterRedirectFromKeycloak.endsWith("/")) browserUrlAfterRedirectFromKeycloak = browserUrlAfterRedirectFromKeycloak.substring(0, browserUrlAfterRedirectFromKeycloak.length() - 1);
if (Constants.INSTALLED_APP_URN.equals(redirectUri)) { if (Constants.INSTALLED_APP_URN.equals(redirectUri)) {

View file

@ -17,12 +17,18 @@
package org.keycloak.testsuite.oidc; package org.keycloak.testsuite.oidc;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import org.jboss.arquillian.graphene.page.Page; import org.jboss.arquillian.graphene.page.Page;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.OAuthErrorException;
import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper; import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.representations.idm.EventRepresentation;
@ -38,6 +44,8 @@ import org.keycloak.testsuite.pages.OAuthGrantPage;
import org.keycloak.testsuite.util.ClientManager; import org.keycloak.testsuite.util.ClientManager;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
import jakarta.ws.rs.core.UriBuilder;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -106,5 +114,64 @@ public class OIDCBackwardsCompatibilityTest extends AbstractTestRealmKeycloakTes
client.update(clientRep); client.update(clientRep);
} }
@Test
public void testExcludeIssuerParameter() {
// Open login form and login successfully. Assert iss parameter is present
OAuthClient.AuthorizationEndpointResponse authzResponse = oauth.doLogin("test-user@localhost", "password");
events.expectLogin().assertEvent();
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertEquals(oauth.AUTH_SERVER_ROOT + "/realms/test", authzResponse.getIssuer());
// Switch "exclude iss" to on
ClientResource client = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
ClientRepresentation clientRep = client.toRepresentation();
OIDCAdvancedConfigWrapper config = OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep);
config.setExcludeIssuerFromAuthResponse(true);
client.update(clientRep);
// Open login again and assert iss parameter is not present
driver.navigate().to(oauth.getLoginFormUrl());
org.keycloak.testsuite.Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
events.expectLogin().detail(Details.USERNAME, "test-user@localhost").assertEvent();
authzResponse = new OAuthClient.AuthorizationEndpointResponse(oauth);
Assert.assertNull(authzResponse.getIssuer());
// Revert
config.setExcludeIssuerFromAuthResponse(false);
client.update(clientRep);
}
@Test
public void testExcludeIssuerParameterOnError() throws IOException {
// Open login form and login fails. Assert iss parameter is present
oauth.responseType("tokenn");
UriBuilder b = UriBuilder.fromUri(oauth.getLoginFormUrl());
driver.navigate().to(b.build().toURL());
OAuthClient.AuthorizationEndpointResponse errorResponse = new OAuthClient.AuthorizationEndpointResponse(oauth);
assertTrue(errorResponse.isRedirected());
Assert.assertEquals(errorResponse.getError(), OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE);
Assert.assertEquals(oauth.AUTH_SERVER_ROOT + "/realms/test", errorResponse.getIssuer());
events.expectLogin().error(Errors.INVALID_REQUEST).user((String) null).session((String) null).clearDetails().detail(Details.RESPONSE_TYPE, "tokenn").assertEvent();
// Switch "exclude iss" to on
ClientResource client = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
ClientRepresentation clientRep = client.toRepresentation();
OIDCAdvancedConfigWrapper config = OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep);
config.setExcludeIssuerFromAuthResponse(true);
client.update(clientRep);
// Open login again and assert iss parameter is not present
driver.navigate().to(b.build().toURL());
errorResponse = new OAuthClient.AuthorizationEndpointResponse(oauth);
assertTrue(errorResponse.isRedirected());
Assert.assertEquals(errorResponse.getError(), OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE);
Assert.assertNull(errorResponse.getIssuer());
events.expectLogin().error(Errors.INVALID_REQUEST).user((String) null).session((String) null).clearDetails().detail(Details.RESPONSE_TYPE, "tokenn").assertEvent();
}
} }

View file

@ -131,14 +131,20 @@
"enabled": true, "enabled": true,
"adminUrl": "/customer-db", "adminUrl": "/customer-db",
"baseUrl": "/customer-db", "baseUrl": "/customer-db",
"bearerOnly": true "bearerOnly": true,
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "customer-db-audience-required", "clientId": "customer-db-audience-required",
"enabled": true, "enabled": true,
"adminUrl": "/customer-db-audience-required", "adminUrl": "/customer-db-audience-required",
"baseUrl": "/customer-db-audience-required", "baseUrl": "/customer-db-audience-required",
"bearerOnly": true "bearerOnly": true,
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "customer-portal", "clientId": "customer-portal",
@ -149,7 +155,10 @@
"/customer-portal/*" "/customer-portal/*"
], ],
"secret": "password", "secret": "password",
"directAccessGrantsEnabled": true "directAccessGrantsEnabled": true,
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "serialization-servlet", "clientId": "serialization-servlet",
@ -160,7 +169,10 @@
"/serialization-servlet/*" "/serialization-servlet/*"
], ],
"secret": "password", "secret": "password",
"directAccessGrantsEnabled": true "directAccessGrantsEnabled": true,
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "customer-portal-subsystem", "clientId": "customer-portal-subsystem",
@ -170,7 +182,10 @@
"redirectUris": [ "redirectUris": [
"/customer-portal-subsystem/*" "/customer-portal-subsystem/*"
], ],
"secret": "password" "secret": "password",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "customer-cookie-portal", "clientId": "customer-cookie-portal",
@ -179,7 +194,10 @@
"redirectUris": [ "redirectUris": [
"/customer-cookie-portal/*" "/customer-cookie-portal/*"
], ],
"secret": "password" "secret": "password",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "customer-cookie-portal-root", "clientId": "customer-cookie-portal-root",
@ -188,7 +206,10 @@
"redirectUris": [ "redirectUris": [
"http://localhost:8080/*" "http://localhost:8080/*"
], ],
"secret": "password" "secret": "password",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "customer-portal-js", "clientId": "customer-portal-js",
@ -198,7 +219,10 @@
"baseUrl": "/customer-portal-js", "baseUrl": "/customer-portal-js",
"redirectUris": [ "redirectUris": [
"/customer-portal-js/*" "/customer-portal-js/*"
] ],
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "customer-portal-cli", "clientId": "customer-portal-cli",
@ -207,13 +231,19 @@
"redirectUris": [ "redirectUris": [
"urn:ietf:wg:oauth:2.0:oob", "urn:ietf:wg:oauth:2.0:oob",
"http://localhost" "http://localhost"
] ],
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "customer-portal-public", "clientId": "customer-portal-public",
"enabled": true, "enabled": true,
"publicClient": true, "publicClient": true,
"directAccessGrantsEnabled": true "directAccessGrantsEnabled": true,
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "product-portal", "clientId": "product-portal",
@ -223,7 +253,10 @@
"redirectUris": [ "redirectUris": [
"/product-portal/*" "/product-portal/*"
], ],
"secret": "password" "secret": "password",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "product-portal-subsystem", "clientId": "product-portal-subsystem",
@ -233,7 +266,10 @@
"redirectUris": [ "redirectUris": [
"/product-portal-subsystem/*" "/product-portal-subsystem/*"
], ],
"secret": "password" "secret": "password",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "product-portal-autodetect-bearer-only", "clientId": "product-portal-autodetect-bearer-only",
@ -243,7 +279,10 @@
"redirectUris": [ "redirectUris": [
"/product-portal-autodetect-bearer-only/*" "/product-portal-autodetect-bearer-only/*"
], ],
"secret": "password" "secret": "password",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "secure-portal", "clientId": "secure-portal",
@ -255,7 +294,8 @@
"/secure-portal/*" "/secure-portal/*"
], ],
"attributes" : { "attributes" : {
"jwt.credential.certificate" : "MIICqTCCAZECBgFT0Ngs/DANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1zZWN1cmUtcG9ydGFsMB4XDTE2MDQwMTA4MDA0MVoXDTI2MDQwMTA4MDIyMVowGDEWMBQGA1UEAwwNc2VjdXJlLXBvcnRhbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJa4GixpmzP511AmI0eLPLORyJwXS8908MUvdG3hmh8jMOIhe28XjIFeZSY09vFxh22F2SUMjxU/B2Hw4PDJUkebuNR7rXhOIYCJAo6eEZzjSBY/wngFtfm74zJ/eLCobBtDvIld7jobdHTfE1Oz9+GzvtG0k7cm7ubrLT0J4I1UsFZj3b//3wa+O0vNaTwHC1Jz/m59VbtXqyO4xEzIdl416cnGCmEmk5qd5h1de2UoLi/CTad8HftIJhzN1qhlySzW/9Ha70aYlDH2hiibDsXDTrNaMdaaLik7I8Rv/nIbggysG863PKZo8wknDe62QctH5VYSSktiy4gjSJkGh7ECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAZnnx+AHQ8txugGcFK8gWjildDgk+v31fBHBDvmLQaSzsUaIOJaK4wnlwUI+VfR46HmBXhjlDCobFLUptd+kz0G7xapcIn3b5jLrySUUD7L+LAp1vNOQU4mKhTGS3IEvNB73D3GH9rQ+M3KEcoN3f99fNKqKsUdxbmZqGf4VOQ57PUfLBw4PJJGlROPosBc7ivPRyeYnKekhoCTynq30BAD1FA1BA8ppcY4ZVGADPTAgMJxpglpFY9LiqCwdLAGW1ttnsyIJ7DpT+kybhhk7c+MU7gyQdv8xPnMR0bSCB9hndowgBn5oZ393aMscwMNCzwJ0aWBs1sUyn3X0RIsu9Jg==" "jwt.credential.certificate" : "MIICqTCCAZECBgFT0Ngs/DANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1zZWN1cmUtcG9ydGFsMB4XDTE2MDQwMTA4MDA0MVoXDTI2MDQwMTA4MDIyMVowGDEWMBQGA1UEAwwNc2VjdXJlLXBvcnRhbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJa4GixpmzP511AmI0eLPLORyJwXS8908MUvdG3hmh8jMOIhe28XjIFeZSY09vFxh22F2SUMjxU/B2Hw4PDJUkebuNR7rXhOIYCJAo6eEZzjSBY/wngFtfm74zJ/eLCobBtDvIld7jobdHTfE1Oz9+GzvtG0k7cm7ubrLT0J4I1UsFZj3b//3wa+O0vNaTwHC1Jz/m59VbtXqyO4xEzIdl416cnGCmEmk5qd5h1de2UoLi/CTad8HftIJhzN1qhlySzW/9Ha70aYlDH2hiibDsXDTrNaMdaaLik7I8Rv/nIbggysG863PKZo8wknDe62QctH5VYSSktiy4gjSJkGh7ECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAZnnx+AHQ8txugGcFK8gWjildDgk+v31fBHBDvmLQaSzsUaIOJaK4wnlwUI+VfR46HmBXhjlDCobFLUptd+kz0G7xapcIn3b5jLrySUUD7L+LAp1vNOQU4mKhTGS3IEvNB73D3GH9rQ+M3KEcoN3f99fNKqKsUdxbmZqGf4VOQ57PUfLBw4PJJGlROPosBc7ivPRyeYnKekhoCTynq30BAD1FA1BA8ppcY4ZVGADPTAgMJxpglpFY9LiqCwdLAGW1ttnsyIJ7DpT+kybhhk7c+MU7gyQdv8xPnMR0bSCB9hndowgBn5oZ393aMscwMNCzwJ0aWBs1sUyn3X0RIsu9Jg==",
"exclude.issuer.from.auth.response": "true"
} }
}, },
{ {
@ -268,7 +308,8 @@
"/rewritten/*" "/rewritten/*"
], ],
"attributes" : { "attributes" : {
"jwt.credential.certificate" : "MIICqTCCAZECBgFT0Ngs/DANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1zZWN1cmUtcG9ydGFsMB4XDTE2MDQwMTA4MDA0MVoXDTI2MDQwMTA4MDIyMVowGDEWMBQGA1UEAwwNc2VjdXJlLXBvcnRhbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJa4GixpmzP511AmI0eLPLORyJwXS8908MUvdG3hmh8jMOIhe28XjIFeZSY09vFxh22F2SUMjxU/B2Hw4PDJUkebuNR7rXhOIYCJAo6eEZzjSBY/wngFtfm74zJ/eLCobBtDvIld7jobdHTfE1Oz9+GzvtG0k7cm7ubrLT0J4I1UsFZj3b//3wa+O0vNaTwHC1Jz/m59VbtXqyO4xEzIdl416cnGCmEmk5qd5h1de2UoLi/CTad8HftIJhzN1qhlySzW/9Ha70aYlDH2hiibDsXDTrNaMdaaLik7I8Rv/nIbggysG863PKZo8wknDe62QctH5VYSSktiy4gjSJkGh7ECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAZnnx+AHQ8txugGcFK8gWjildDgk+v31fBHBDvmLQaSzsUaIOJaK4wnlwUI+VfR46HmBXhjlDCobFLUptd+kz0G7xapcIn3b5jLrySUUD7L+LAp1vNOQU4mKhTGS3IEvNB73D3GH9rQ+M3KEcoN3f99fNKqKsUdxbmZqGf4VOQ57PUfLBw4PJJGlROPosBc7ivPRyeYnKekhoCTynq30BAD1FA1BA8ppcY4ZVGADPTAgMJxpglpFY9LiqCwdLAGW1ttnsyIJ7DpT+kybhhk7c+MU7gyQdv8xPnMR0bSCB9hndowgBn5oZ393aMscwMNCzwJ0aWBs1sUyn3X0RIsu9Jg==" "jwt.credential.certificate" : "MIICqTCCAZECBgFT0Ngs/DANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1zZWN1cmUtcG9ydGFsMB4XDTE2MDQwMTA4MDA0MVoXDTI2MDQwMTA4MDIyMVowGDEWMBQGA1UEAwwNc2VjdXJlLXBvcnRhbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJa4GixpmzP511AmI0eLPLORyJwXS8908MUvdG3hmh8jMOIhe28XjIFeZSY09vFxh22F2SUMjxU/B2Hw4PDJUkebuNR7rXhOIYCJAo6eEZzjSBY/wngFtfm74zJ/eLCobBtDvIld7jobdHTfE1Oz9+GzvtG0k7cm7ubrLT0J4I1UsFZj3b//3wa+O0vNaTwHC1Jz/m59VbtXqyO4xEzIdl416cnGCmEmk5qd5h1de2UoLi/CTad8HftIJhzN1qhlySzW/9Ha70aYlDH2hiibDsXDTrNaMdaaLik7I8Rv/nIbggysG863PKZo8wknDe62QctH5VYSSktiy4gjSJkGh7ECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAZnnx+AHQ8txugGcFK8gWjildDgk+v31fBHBDvmLQaSzsUaIOJaK4wnlwUI+VfR46HmBXhjlDCobFLUptd+kz0G7xapcIn3b5jLrySUUD7L+LAp1vNOQU4mKhTGS3IEvNB73D3GH9rQ+M3KEcoN3f99fNKqKsUdxbmZqGf4VOQ57PUfLBw4PJJGlROPosBc7ivPRyeYnKekhoCTynq30BAD1FA1BA8ppcY4ZVGADPTAgMJxpglpFY9LiqCwdLAGW1ttnsyIJ7DpT+kybhhk7c+MU7gyQdv8xPnMR0bSCB9hndowgBn5oZ393aMscwMNCzwJ0aWBs1sUyn3X0RIsu9Jg==",
"exclude.issuer.from.auth.response": "true"
} }
}, },
{ {
@ -281,7 +322,8 @@
"/secure-portal-with-custom-session-config/*" "/secure-portal-with-custom-session-config/*"
], ],
"attributes" : { "attributes" : {
"jwt.credential.certificate" : "MIICqTCCAZECBgFT0Ngs/DANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1zZWN1cmUtcG9ydGFsMB4XDTE2MDQwMTA4MDA0MVoXDTI2MDQwMTA4MDIyMVowGDEWMBQGA1UEAwwNc2VjdXJlLXBvcnRhbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJa4GixpmzP511AmI0eLPLORyJwXS8908MUvdG3hmh8jMOIhe28XjIFeZSY09vFxh22F2SUMjxU/B2Hw4PDJUkebuNR7rXhOIYCJAo6eEZzjSBY/wngFtfm74zJ/eLCobBtDvIld7jobdHTfE1Oz9+GzvtG0k7cm7ubrLT0J4I1UsFZj3b//3wa+O0vNaTwHC1Jz/m59VbtXqyO4xEzIdl416cnGCmEmk5qd5h1de2UoLi/CTad8HftIJhzN1qhlySzW/9Ha70aYlDH2hiibDsXDTrNaMdaaLik7I8Rv/nIbggysG863PKZo8wknDe62QctH5VYSSktiy4gjSJkGh7ECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAZnnx+AHQ8txugGcFK8gWjildDgk+v31fBHBDvmLQaSzsUaIOJaK4wnlwUI+VfR46HmBXhjlDCobFLUptd+kz0G7xapcIn3b5jLrySUUD7L+LAp1vNOQU4mKhTGS3IEvNB73D3GH9rQ+M3KEcoN3f99fNKqKsUdxbmZqGf4VOQ57PUfLBw4PJJGlROPosBc7ivPRyeYnKekhoCTynq30BAD1FA1BA8ppcY4ZVGADPTAgMJxpglpFY9LiqCwdLAGW1ttnsyIJ7DpT+kybhhk7c+MU7gyQdv8xPnMR0bSCB9hndowgBn5oZ393aMscwMNCzwJ0aWBs1sUyn3X0RIsu9Jg==" "jwt.credential.certificate" : "MIICqTCCAZECBgFT0Ngs/DANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1zZWN1cmUtcG9ydGFsMB4XDTE2MDQwMTA4MDA0MVoXDTI2MDQwMTA4MDIyMVowGDEWMBQGA1UEAwwNc2VjdXJlLXBvcnRhbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJa4GixpmzP511AmI0eLPLORyJwXS8908MUvdG3hmh8jMOIhe28XjIFeZSY09vFxh22F2SUMjxU/B2Hw4PDJUkebuNR7rXhOIYCJAo6eEZzjSBY/wngFtfm74zJ/eLCobBtDvIld7jobdHTfE1Oz9+GzvtG0k7cm7ubrLT0J4I1UsFZj3b//3wa+O0vNaTwHC1Jz/m59VbtXqyO4xEzIdl416cnGCmEmk5qd5h1de2UoLi/CTad8HftIJhzN1qhlySzW/9Ha70aYlDH2hiibDsXDTrNaMdaaLik7I8Rv/nIbggysG863PKZo8wknDe62QctH5VYSSktiy4gjSJkGh7ECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAZnnx+AHQ8txugGcFK8gWjildDgk+v31fBHBDvmLQaSzsUaIOJaK4wnlwUI+VfR46HmBXhjlDCobFLUptd+kz0G7xapcIn3b5jLrySUUD7L+LAp1vNOQU4mKhTGS3IEvNB73D3GH9rQ+M3KEcoN3f99fNKqKsUdxbmZqGf4VOQ57PUfLBw4PJJGlROPosBc7ivPRyeYnKekhoCTynq30BAD1FA1BA8ppcY4ZVGADPTAgMJxpglpFY9LiqCwdLAGW1ttnsyIJ7DpT+kybhhk7c+MU7gyQdv8xPnMR0bSCB9hndowgBn5oZ393aMscwMNCzwJ0aWBs1sUyn3X0RIsu9Jg==",
"exclude.issuer.from.auth.response": "true"
} }
}, },
{ {
@ -292,7 +334,10 @@
"redirectUris": [ "redirectUris": [
"/session-portal/*" "/session-portal/*"
], ],
"secret": "password" "secret": "password",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "session-portal-distributable", "clientId": "session-portal-distributable",
@ -302,7 +347,10 @@
"redirectUris": [ "redirectUris": [
"http://localhost:8580/session-portal-distributable/*" "http://localhost:8580/session-portal-distributable/*"
], ],
"secret": "password" "secret": "password",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "input-portal", "clientId": "input-portal",
@ -312,7 +360,10 @@
"redirectUris": [ "redirectUris": [
"/input-portal/*" "/input-portal/*"
], ],
"secret": "password" "secret": "password",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "input-portal-no-access-token", "clientId": "input-portal-no-access-token",
@ -322,7 +373,10 @@
"redirectUris": [ "redirectUris": [
"/input-portal-no-access-token/*" "/input-portal-no-access-token/*"
], ],
"secret": "password" "secret": "password",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "token-min-ttl", "clientId": "token-min-ttl",
@ -332,7 +386,10 @@
"redirectUris": [ "redirectUris": [
"/token-min-ttl/*" "/token-min-ttl/*"
], ],
"secret": "password" "secret": "password",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "token-refresh", "clientId": "token-refresh",
@ -342,7 +399,10 @@
"redirectUris": [ "redirectUris": [
"/token-refresh/*" "/token-refresh/*"
], ],
"secret": "password" "secret": "password",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "third-party", "clientId": "third-party",
@ -351,7 +411,10 @@
"/oauth-client/*", "/oauth-client/*",
"/oauth-client-cdi/*" "/oauth-client-cdi/*"
], ],
"secret": "password" "secret": "password",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "basic-auth-service", "clientId": "basic-auth-service",
@ -360,7 +423,10 @@
"enabled": true, "enabled": true,
"adminUrl": "/basic-auth", "adminUrl": "/basic-auth",
"baseUrl": "/basic-auth", "baseUrl": "/basic-auth",
"secret": "password" "secret": "password",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "client-secret-jwt-secure-portal", "clientId": "client-secret-jwt-secure-portal",
@ -371,7 +437,10 @@
"redirectUris": [ "redirectUris": [
"/client-secret-jwt-secure-portal/*" "/client-secret-jwt-secure-portal/*"
], ],
"secret": "234234-234234-234234" "secret": "234234-234234-234234",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "client-secret-jwt-secure-portal-valid-alg", "clientId": "client-secret-jwt-secure-portal-valid-alg",
@ -382,7 +451,10 @@
"redirectUris": [ "redirectUris": [
"/client-secret-jwt-secure-portal-valid-alg/*" "/client-secret-jwt-secure-portal-valid-alg/*"
], ],
"secret": "234234-234234-234234" "secret": "234234-234234-234234",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
} }
] ]
} }

View file

@ -113,7 +113,10 @@
"http://localhost:8180/auth/realms/master/app/*" "http://localhost:8180/auth/realms/master/app/*"
], ],
"adminUrl": "http://localhost:8180/auth/realms/master/app/logout", "adminUrl": "http://localhost:8180/auth/realms/master/app/logout",
"secret": "password" "secret": "password",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId" : "third-party", "clientId" : "third-party",
@ -123,7 +126,10 @@
"redirectUris": [ "redirectUris": [
"http://localhost:8180/app/*" "http://localhost:8180/app/*"
], ],
"secret": "password" "secret": "password",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
}, },
{ {
"clientId": "offline-client", "clientId": "offline-client",
@ -134,7 +140,10 @@
"redirectUris": [ "redirectUris": [
"/offline-client/*" "/offline-client/*"
], ],
"secret": "secret1" "secret": "secret1",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
} }
], ],

View file

@ -66,7 +66,10 @@
"redirectUris": [ "redirectUris": [
"/multi-tenant/tenant1/*" "/multi-tenant/tenant1/*"
], ],
"secret": "password" "secret": "password",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
} }
] ]
} }

View file

@ -64,7 +64,10 @@
"redirectUris": [ "redirectUris": [
"/multi-tenant/tenant2/*" "/multi-tenant/tenant2/*"
], ],
"secret": "password" "secret": "password",
"attributes" : {
"exclude.issuer.from.auth.response": "true"
}
} }
] ]
} }