mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
Compliant with RFC8414, return server metadata at /.well-known/oauth-authorization-server/realms/{realm}
closes #40923 Signed-off-by: Takashi Norimatsu <takashi.norimatsu.ws@hitachi.com>
This commit is contained in:
parent
4fec0a8630
commit
ea63cdc97a
@ -180,6 +180,10 @@ public class RealmsResource {
|
||||
}
|
||||
|
||||
private void resolveRealmAndUpdateSession(String realmName) {
|
||||
resolveRealmAndUpdateSession(session, realmName);
|
||||
}
|
||||
|
||||
private static void resolveRealmAndUpdateSession(KeycloakSession session, String realmName) {
|
||||
RealmManager realmManager = new RealmManager(session);
|
||||
RealmModel realm = realmManager.getRealmByName(realmName);
|
||||
if (realm == null) {
|
||||
@ -225,8 +229,12 @@ public class RealmsResource {
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getWellKnown(final @PathParam("realm") String name,
|
||||
final @PathParam("alias") String alias) {
|
||||
resolveRealmAndUpdateSession(name);
|
||||
checkSsl(session.getContext().getRealm());
|
||||
return getWellKnownResponse(session, name, alias, logger);
|
||||
}
|
||||
|
||||
public static Response getWellKnownResponse(KeycloakSession session, String name, String alias, Logger logger) throws NotFoundException {
|
||||
resolveRealmAndUpdateSession(session, name);
|
||||
checkSsl(session, session.getContext().getRealm());
|
||||
|
||||
WellKnownProviderFactory wellKnownProviderFactoryFound = session.getKeycloakSessionFactory().getProviderFactoriesStream(WellKnownProvider.class)
|
||||
.map(providerFactory -> (WellKnownProviderFactory) providerFactory)
|
||||
@ -276,6 +284,10 @@ public class RealmsResource {
|
||||
}
|
||||
|
||||
private void checkSsl(RealmModel realm) {
|
||||
checkSsl(session, realm);
|
||||
}
|
||||
|
||||
private static void checkSsl(KeycloakSession session, RealmModel realm) {
|
||||
if (!"https".equals(session.getContext().getUri().getBaseUri().getScheme())
|
||||
&& realm.getSslRequired().isRequired(session.getContext().getConnection())) {
|
||||
HttpRequest request = session.getContext().getHttpRequest();
|
||||
|
||||
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2025 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.services.resources;
|
||||
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import jakarta.ws.rs.OPTIONS;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.UriBuilder;
|
||||
import jakarta.ws.rs.ext.Provider;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.protocol.oauth2.OAuth2WellKnownProviderFactory;
|
||||
import org.keycloak.services.cors.Cors;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Provider
|
||||
@Path("/.well-known")
|
||||
public class ServerMetadataResource {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(ServerMetadataResource.class);
|
||||
|
||||
@Context
|
||||
protected KeycloakSession session;
|
||||
|
||||
@OPTIONS
|
||||
@Path("{provider}/realms/{realm}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getOAuth2AuthorizationServerWellKnownVersionPreflight(final @PathParam("provider") String providerName,
|
||||
final @PathParam("realm") String name) {
|
||||
if (!isValidProvider(providerName)) throw new NotFoundException();
|
||||
return Cors.builder().allowedMethods("GET").preflight().auth().add(Response.ok());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{provider}/realms/{realm}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getOAuth2AuthorizationServerWellKnown(final @PathParam("provider") String providerName,
|
||||
final @PathParam("realm") String name) {
|
||||
if (!isValidProvider(providerName)) throw new NotFoundException();
|
||||
return RealmsResource.getWellKnownResponse(session, name, providerName, logger);
|
||||
}
|
||||
|
||||
public static UriBuilder wellKnownOAuthProviderUrl(UriBuilder builder) {
|
||||
return builder.path(ServerMetadataResource.class).path("{provider}/realms/{realm}");
|
||||
}
|
||||
|
||||
private boolean isValidProvider(String providerName) {
|
||||
// you can add codes here considering the current status of the implementation (preview, experimental).
|
||||
if (OAuth2WellKnownProviderFactory.PROVIDER_ID.equals(providerName)) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -28,6 +28,7 @@ import org.keycloak.services.filters.KeycloakSecurityHeadersFilter;
|
||||
import org.keycloak.services.resources.KeycloakApplication;
|
||||
import org.keycloak.services.resources.LoadBalancerResource;
|
||||
import org.keycloak.services.resources.RealmsResource;
|
||||
import org.keycloak.services.resources.ServerMetadataResource;
|
||||
import org.keycloak.services.resources.ThemeResource;
|
||||
import org.keycloak.services.resources.WelcomeResource;
|
||||
import org.keycloak.services.resources.admin.AdminRoot;
|
||||
@ -54,6 +55,7 @@ public class ResteasyKeycloakApplication extends KeycloakApplication {
|
||||
|
||||
singletons.add(new ObjectMapperResolver());
|
||||
classes.add(WelcomeResource.class);
|
||||
classes.add(ServerMetadataResource.class);
|
||||
|
||||
if (MultiSiteUtils.isMultiSiteEnabled()) {
|
||||
// If we are running in multi-site mode, we need to add a resource which to expose
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2025 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.testsuite.oauth;
|
||||
|
||||
import jakarta.ws.rs.core.UriBuilder;
|
||||
import org.keycloak.protocol.oauth2.OAuth2WellKnownProviderFactory;
|
||||
import org.keycloak.services.resources.ServerMetadataResource;
|
||||
import org.keycloak.testsuite.oidc.AbstractWellKnownProviderTest;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
public class RFC8414CompliantOAuth2WellKnownProviderTest extends AbstractWellKnownProviderTest {
|
||||
|
||||
protected String getWellKnownProviderId() {
|
||||
return OAuth2WellKnownProviderFactory.PROVIDER_ID;
|
||||
}
|
||||
|
||||
protected URI getOIDCDiscoveryUri(UriBuilder builder) {
|
||||
return ServerMetadataResource.wellKnownOAuthProviderUrl(builder).build(this.getWellKnownProviderId(), "test");
|
||||
}
|
||||
|
||||
}
|
||||
@ -410,9 +410,13 @@ public abstract class AbstractWellKnownProviderTest extends AbstractKeycloakTest
|
||||
}
|
||||
}
|
||||
|
||||
protected URI getOIDCDiscoveryUri(UriBuilder builder) {
|
||||
return RealmsResource.wellKnownProviderUrl(builder).build("test", this.getWellKnownProviderId());
|
||||
}
|
||||
|
||||
private String getOIDCDiscoveryConfiguration(Client client, String uriTemplate) {
|
||||
UriBuilder builder = UriBuilder.fromUri(uriTemplate);
|
||||
URI oidcDiscoveryUri = RealmsResource.wellKnownProviderUrl(builder).build("test", this.getWellKnownProviderId());
|
||||
URI oidcDiscoveryUri = getOIDCDiscoveryUri(builder);
|
||||
WebTarget oidcDiscoveryTarget = client.target(oidcDiscoveryUri);
|
||||
|
||||
Response response = oidcDiscoveryTarget.request().get();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user