From 896e27004122fbbb92432f6bc2a76873979943c2 Mon Sep 17 00:00:00 2001 From: Bernard Budano Date: Wed, 3 Sep 2025 10:49:28 +0200 Subject: [PATCH 1/3] Support @ClientRegistrationId at Class Level Closes gh-17806 Signed-off-by: Bernard Budano --- .../annotation/ClientRegistrationId.java | 2 +- .../client/ClientRegistrationIdProcessor.java | 12 ++++-- .../ClientRegistrationIdProcessorTests.java | 37 ++++++++++++++----- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/annotation/ClientRegistrationId.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/annotation/ClientRegistrationId.java index c81dd92d3ff..d3ecf5045eb 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/annotation/ClientRegistrationId.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/annotation/ClientRegistrationId.java @@ -33,7 +33,7 @@ * @since 7.0 * @see org.springframework.security.oauth2.client.web.client.ClientRegistrationIdProcessor */ -@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) +@Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ClientRegistrationId { diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessor.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessor.java index 900a082b778..e33d8972252 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessor.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessor.java @@ -17,6 +17,7 @@ package org.springframework.security.oauth2.client.web.client; import java.lang.reflect.Method; +import java.util.Optional; import org.jspecify.annotations.Nullable; @@ -37,17 +38,20 @@ public final class ClientRegistrationIdProcessor implements HttpRequestValues.Pr public static ClientRegistrationIdProcessor DEFAULT_INSTANCE = new ClientRegistrationIdProcessor(); + private ClientRegistrationIdProcessor() { + } + @Override public void process(Method method, MethodParameter[] parameters, @Nullable Object[] arguments, HttpRequestValues.Builder builder) { - ClientRegistrationId registeredId = AnnotationUtils.findAnnotation(method, ClientRegistrationId.class); + ClientRegistrationId registeredId = Optional + .ofNullable(AnnotationUtils.findAnnotation(method, ClientRegistrationId.class)) + .orElseGet(() -> AnnotationUtils.findAnnotation(method.getDeclaringClass(), ClientRegistrationId.class)); + if (registeredId != null) { String registrationId = registeredId.registrationId(); builder.configureAttributes(ClientAttributes.clientRegistrationId(registrationId)); } } - private ClientRegistrationIdProcessor() { - } - } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessorTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessorTests.java index 78f28e223ad..b23f133607c 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessorTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessorTests.java @@ -39,6 +39,8 @@ */ class ClientRegistrationIdProcessorTests { + private static final String REGISTRATION_ID = "registrationId"; + ClientRegistrationIdProcessor processor = ClientRegistrationIdProcessor.DEFAULT_INSTANCE; @Test @@ -48,32 +50,42 @@ void processWhenClientRegistrationIdPresentThenSet() { this.processor.process(hasClientRegistrationId, null, null, builder); String registrationId = ClientAttributes.resolveClientRegistrationId(builder.build().getAttributes()); - assertThat(registrationId).isEqualTo(RestService.REGISTRATION_ID); + assertThat(registrationId).isEqualTo(REGISTRATION_ID); } @Test void processWhenMetaClientRegistrationIdPresentThenSet() { HttpRequestValues.Builder builder = HttpRequestValues.builder(); - Method hasClientRegistrationId = ReflectionUtils.findMethod(RestService.class, "hasMetaClientRegistrationId"); - this.processor.process(hasClientRegistrationId, null, null, builder); + Method hasMetaClientRegistrationId = ReflectionUtils.findMethod(RestService.class, + "hasMetaClientRegistrationId"); + this.processor.process(hasMetaClientRegistrationId, null, null, builder); String registrationId = ClientAttributes.resolveClientRegistrationId(builder.build().getAttributes()); - assertThat(registrationId).isEqualTo(RestService.REGISTRATION_ID); + assertThat(registrationId).isEqualTo(REGISTRATION_ID); } @Test void processWhenNoClientRegistrationIdPresentThenNull() { HttpRequestValues.Builder builder = HttpRequestValues.builder(); - Method hasClientRegistrationId = ReflectionUtils.findMethod(RestService.class, "noClientRegistrationId"); - this.processor.process(hasClientRegistrationId, null, null, builder); + Method noClientRegistrationId = ReflectionUtils.findMethod(RestService.class, "noClientRegistrationId"); + this.processor.process(noClientRegistrationId, null, null, builder); String registrationId = ClientAttributes.resolveClientRegistrationId(builder.build().getAttributes()); assertThat(registrationId).isNull(); } - interface RestService { + @Test + void processWhenClientRegistrationIdPresentOnDeclaringClassThenSet() { + HttpRequestValues.Builder builder = HttpRequestValues.builder(); + Method declaringClassHasClientRegistrationId = ReflectionUtils.findMethod(AnnotatedRestService.class, + "declaringClassHasClientRegistrationId"); + this.processor.process(declaringClassHasClientRegistrationId, null, null, builder); + + String registrationId = ClientAttributes.resolveClientRegistrationId(builder.build().getAttributes()); + assertThat(registrationId).isEqualTo(REGISTRATION_ID); + } - String REGISTRATION_ID = "registrationId"; + interface RestService { @ClientRegistrationId(REGISTRATION_ID) void hasClientRegistrationId(); @@ -86,9 +98,16 @@ interface RestService { } @Retention(RetentionPolicy.RUNTIME) - @ClientRegistrationId(RestService.REGISTRATION_ID) + @ClientRegistrationId(REGISTRATION_ID) @interface MetaClientRegistrationId { } + @ClientRegistrationId(REGISTRATION_ID) + interface AnnotatedRestService { + + void declaringClassHasClientRegistrationId(); + + } + } From 00f80efe3a39ff008dbda1af8bb4c003646a6c11 Mon Sep 17 00:00:00 2001 From: Bernard Budano Date: Wed, 10 Sep 2025 19:06:22 +0200 Subject: [PATCH 2/3] Address reviewer requested changes Closes gh-17806 Signed-off-by: Bernard Budano --- .../integrations/rest/http-interface.adoc | 2 +- docs/modules/ROOT/pages/whats-new.adoc | 1 + .../client/ClientRegistrationIdProcessor.java | 15 +++---- .../ClientRegistrationIdProcessorTests.java | 41 +++++++++++++++---- 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/docs/modules/ROOT/pages/features/integrations/rest/http-interface.adoc b/docs/modules/ROOT/pages/features/integrations/rest/http-interface.adoc index db69fd591d6..1d818f520b6 100644 --- a/docs/modules/ROOT/pages/features/integrations/rest/http-interface.adoc +++ b/docs/modules/ROOT/pages/features/integrations/rest/http-interface.adoc @@ -5,7 +5,7 @@ Spring Security's OAuth Support can integrate with `RestClient` and `WebClient` [[configuration]] == Configuration -After xref:features/integrations/rest/http-interface.adoc#configuration-restclient[RestClient] or xref:features/integrations/rest/http-interface.adoc#configuration-webclient[WebClient] specific configuration, usage of xref:features/integrations/rest/http-interface.adoc[] only requires adding a xref:features/integrations/rest/http-interface.adoc#client-registration-id[`@ClientRegistrationId`] to methods that require OAuth. +After xref:features/integrations/rest/http-interface.adoc#configuration-restclient[RestClient] or xref:features/integrations/rest/http-interface.adoc#configuration-webclient[WebClient] specific configuration, usage of xref:features/integrations/rest/http-interface.adoc[] only requires adding a xref:features/integrations/rest/http-interface.adoc#client-registration-id[`@ClientRegistrationId`] to methods that require OAuth or their declaring HTTP interface. Since the presence of xref:features/integrations/rest/http-interface.adoc#client-registration-id[`@ClientRegistrationId`] determines if and how the OAuth token will be resolved, it is safe to add Spring Security's OAuth support any configuration. diff --git a/docs/modules/ROOT/pages/whats-new.adoc b/docs/modules/ROOT/pages/whats-new.adoc index be7c6daddf7..a613e8fbec0 100644 --- a/docs/modules/ROOT/pages/whats-new.adoc +++ b/docs/modules/ROOT/pages/whats-new.adoc @@ -49,6 +49,7 @@ http.csrf((csrf) -> csrf.spa()); * Added OAuth2 Support for xref:features/integrations/rest/http-interface.adoc[HTTP Interface Integration] * Added support for custom `JwkSource` in `NimbusJwtDecoder`, allowing usage of Nimbus's `JwkSourceBuilder` API * Added builder for `NimbusJwtEncoder`, supports specifying an EC or RSA key pair or a secret key +* Added support for `@ClientRegistrationId` at class level, eliminating the need for method level repetition == SAML 2.0 diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessor.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessor.java index e33d8972252..ba6e6786b95 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessor.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessor.java @@ -17,12 +17,12 @@ package org.springframework.security.oauth2.client.web.client; import java.lang.reflect.Method; -import java.util.Optional; import org.jspecify.annotations.Nullable; import org.springframework.core.MethodParameter; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.security.core.annotation.SecurityAnnotationScanner; +import org.springframework.security.core.annotation.SecurityAnnotationScanners; import org.springframework.security.oauth2.client.annotation.ClientRegistrationId; import org.springframework.security.oauth2.client.web.ClientAttributes; import org.springframework.web.service.invoker.HttpRequestValues; @@ -38,15 +38,13 @@ public final class ClientRegistrationIdProcessor implements HttpRequestValues.Pr public static ClientRegistrationIdProcessor DEFAULT_INSTANCE = new ClientRegistrationIdProcessor(); - private ClientRegistrationIdProcessor() { - } + private SecurityAnnotationScanner securityAnnotationScanner = SecurityAnnotationScanners + .requireUnique(ClientRegistrationId.class); @Override public void process(Method method, MethodParameter[] parameters, @Nullable Object[] arguments, HttpRequestValues.Builder builder) { - ClientRegistrationId registeredId = Optional - .ofNullable(AnnotationUtils.findAnnotation(method, ClientRegistrationId.class)) - .orElseGet(() -> AnnotationUtils.findAnnotation(method.getDeclaringClass(), ClientRegistrationId.class)); + ClientRegistrationId registeredId = this.securityAnnotationScanner.scan(method, method.getDeclaringClass()); if (registeredId != null) { String registrationId = registeredId.registrationId(); @@ -54,4 +52,7 @@ public void process(Method method, MethodParameter[] parameters, @Nullable Objec } } + private ClientRegistrationIdProcessor() { + } + } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessorTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessorTests.java index b23f133607c..7f5f12e17e5 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessorTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/client/ClientRegistrationIdProcessorTests.java @@ -22,12 +22,14 @@ import org.junit.jupiter.api.Test; +import org.springframework.core.annotation.AnnotationConfigurationException; import org.springframework.security.oauth2.client.annotation.ClientRegistrationId; import org.springframework.security.oauth2.client.web.ClientAttributes; import org.springframework.util.ReflectionUtils; import org.springframework.web.service.invoker.HttpRequestValues; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; /** * Unit tests for {@link ClientRegistrationIdProcessor}. @@ -56,9 +58,8 @@ void processWhenClientRegistrationIdPresentThenSet() { @Test void processWhenMetaClientRegistrationIdPresentThenSet() { HttpRequestValues.Builder builder = HttpRequestValues.builder(); - Method hasMetaClientRegistrationId = ReflectionUtils.findMethod(RestService.class, - "hasMetaClientRegistrationId"); - this.processor.process(hasMetaClientRegistrationId, null, null, builder); + Method hasClientRegistrationId = ReflectionUtils.findMethod(RestService.class, "hasMetaClientRegistrationId"); + this.processor.process(hasClientRegistrationId, null, null, builder); String registrationId = ClientAttributes.resolveClientRegistrationId(builder.build().getAttributes()); assertThat(registrationId).isEqualTo(REGISTRATION_ID); @@ -67,8 +68,8 @@ void processWhenMetaClientRegistrationIdPresentThenSet() { @Test void processWhenNoClientRegistrationIdPresentThenNull() { HttpRequestValues.Builder builder = HttpRequestValues.builder(); - Method noClientRegistrationId = ReflectionUtils.findMethod(RestService.class, "noClientRegistrationId"); - this.processor.process(noClientRegistrationId, null, null, builder); + Method hasClientRegistrationId = ReflectionUtils.findMethod(RestService.class, "noClientRegistrationId"); + this.processor.process(hasClientRegistrationId, null, null, builder); String registrationId = ClientAttributes.resolveClientRegistrationId(builder.build().getAttributes()); assertThat(registrationId).isNull(); @@ -77,7 +78,7 @@ void processWhenNoClientRegistrationIdPresentThenNull() { @Test void processWhenClientRegistrationIdPresentOnDeclaringClassThenSet() { HttpRequestValues.Builder builder = HttpRequestValues.builder(); - Method declaringClassHasClientRegistrationId = ReflectionUtils.findMethod(AnnotatedRestService.class, + Method declaringClassHasClientRegistrationId = ReflectionUtils.findMethod(TypeAnnotatedRestService.class, "declaringClassHasClientRegistrationId"); this.processor.process(declaringClassHasClientRegistrationId, null, null, builder); @@ -85,6 +86,16 @@ void processWhenClientRegistrationIdPresentOnDeclaringClassThenSet() { assertThat(registrationId).isEqualTo(REGISTRATION_ID); } + @Test + void processWhenDuplicateClientRegistrationIdPresentOnAggregateServiceThenException() { + HttpRequestValues.Builder builder = HttpRequestValues.builder(); + Method shouldFailDueToDuplicateClientRegistrationId = ReflectionUtils.findMethod(AggregateRestService.class, + "shouldFailDueToDuplicateClientRegistrationId"); + + assertThatExceptionOfType(AnnotationConfigurationException.class).isThrownBy( + () -> this.processor.process(shouldFailDueToDuplicateClientRegistrationId, null, null, builder)); + } + interface RestService { @ClientRegistrationId(REGISTRATION_ID) @@ -104,10 +115,26 @@ interface RestService { } @ClientRegistrationId(REGISTRATION_ID) - interface AnnotatedRestService { + interface TypeAnnotatedRestService { void declaringClassHasClientRegistrationId(); } + @ClientRegistrationId("a") + interface ARestService { + + } + + @ClientRegistrationId("b") + interface BRestService { + + } + + interface AggregateRestService extends ARestService, BRestService { + + void shouldFailDueToDuplicateClientRegistrationId(); + + } + } From c73a53375b7fef3e9385e61f609cc1426585e6a7 Mon Sep 17 00:00:00 2001 From: Rob Winch <362503+rwinch@users.noreply.github.com> Date: Fri, 12 Sep 2025 15:04:44 -0500 Subject: [PATCH 3/3] Document @ClientRegistrationId on types Issue gh-17806 --- .../integrations/rest/http-interface.adoc | 7 ++++ docs/modules/ROOT/pages/whats-new.adoc | 2 +- .../integrations/rest/type/Hovercard.java | 25 +++++++++++ .../integrations/rest/type/UserService.java | 41 +++++++++++++++++++ .../integrations/rest/type/Hovercard.kt | 24 +++++++++++ .../integrations/rest/type/UserService.kt | 39 ++++++++++++++++++ 6 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 docs/src/test/java/org/springframework/security/docs/features/integrations/rest/type/Hovercard.java create mode 100644 docs/src/test/java/org/springframework/security/docs/features/integrations/rest/type/UserService.java create mode 100644 docs/src/test/kotlin/org/springframework/security/kt/docs/features/integrations/rest/type/Hovercard.kt create mode 100644 docs/src/test/kotlin/org/springframework/security/kt/docs/features/integrations/rest/type/UserService.kt diff --git a/docs/modules/ROOT/pages/features/integrations/rest/http-interface.adoc b/docs/modules/ROOT/pages/features/integrations/rest/http-interface.adoc index 1d818f520b6..3ee8ef1520a 100644 --- a/docs/modules/ROOT/pages/features/integrations/rest/http-interface.adoc +++ b/docs/modules/ROOT/pages/features/integrations/rest/http-interface.adoc @@ -51,6 +51,13 @@ include-code::./UserService[tag=getAuthenticatedUser] The xref:features/integrations/rest/http-interface.adoc#client-registration-id[`@ClientRegistrationId`] will be processed by xref:features/integrations/rest/http-interface.adoc#client-registration-id-processor[`ClientRegistrationIdProcessor`] +[[type]] +=== Type Level Declarations + +`@ClientRegistrationId` can also be added at the type level to avoid repeating the declaration on every method. + +include-code::./UserService[tag=type] + [[client-registration-id-processor]] == `ClientRegistrationIdProcessor` diff --git a/docs/modules/ROOT/pages/whats-new.adoc b/docs/modules/ROOT/pages/whats-new.adoc index a613e8fbec0..2955ebb821f 100644 --- a/docs/modules/ROOT/pages/whats-new.adoc +++ b/docs/modules/ROOT/pages/whats-new.adoc @@ -49,7 +49,7 @@ http.csrf((csrf) -> csrf.spa()); * Added OAuth2 Support for xref:features/integrations/rest/http-interface.adoc[HTTP Interface Integration] * Added support for custom `JwkSource` in `NimbusJwtDecoder`, allowing usage of Nimbus's `JwkSourceBuilder` API * Added builder for `NimbusJwtEncoder`, supports specifying an EC or RSA key pair or a secret key -* Added support for `@ClientRegistrationId` at class level, eliminating the need for method level repetition +* Added support for `@ClientRegistrationId` at the xref:features/integrations/rest/http-interface.adoc#type[type level], eliminating the need for method level repetition == SAML 2.0 diff --git a/docs/src/test/java/org/springframework/security/docs/features/integrations/rest/type/Hovercard.java b/docs/src/test/java/org/springframework/security/docs/features/integrations/rest/type/Hovercard.java new file mode 100644 index 00000000000..32dae93cedb --- /dev/null +++ b/docs/src/test/java/org/springframework/security/docs/features/integrations/rest/type/Hovercard.java @@ -0,0 +1,25 @@ +/* + * Copyright 2004-present the original author or authors. + * + * 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 + * + * https://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.springframework.security.docs.features.integrations.rest.type; + +/** + * Used to ensure {@link UserService} compiles, but not show in the documentation. + * + * @author Rob Winch + */ +public record Hovercard() { +} diff --git a/docs/src/test/java/org/springframework/security/docs/features/integrations/rest/type/UserService.java b/docs/src/test/java/org/springframework/security/docs/features/integrations/rest/type/UserService.java new file mode 100644 index 00000000000..7b0e527e1db --- /dev/null +++ b/docs/src/test/java/org/springframework/security/docs/features/integrations/rest/type/UserService.java @@ -0,0 +1,41 @@ +/* + * Copyright 2004-present the original author or authors. + * + * 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 clients copy of the License at + * + * https://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.springframework.security.docs.features.integrations.rest.type; + +import org.springframework.security.docs.features.integrations.rest.clientregistrationid.User; +import org.springframework.security.oauth2.client.annotation.ClientRegistrationId; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.service.annotation.GetExchange; +import org.springframework.web.service.annotation.HttpExchange; + +/** + * Demonstrates a service for {@link ClientRegistrationId} at the type level. + * @author Rob Winch + */ +// tag::type[] +@HttpExchange +@ClientRegistrationId("github") +public interface UserService { + + @GetExchange("/user") + User getAuthenticatedUser(); + + @GetExchange("/users/{username}/hovercard") + Hovercard getHovercard(@PathVariable String username); + +} +// end::type[] diff --git a/docs/src/test/kotlin/org/springframework/security/kt/docs/features/integrations/rest/type/Hovercard.kt b/docs/src/test/kotlin/org/springframework/security/kt/docs/features/integrations/rest/type/Hovercard.kt new file mode 100644 index 00000000000..e4af07ffd2b --- /dev/null +++ b/docs/src/test/kotlin/org/springframework/security/kt/docs/features/integrations/rest/type/Hovercard.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2004-present the original author or authors. + * + * 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 + * + * https://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.springframework.security.kt.docs.features.integrations.rest.type + +/** + * Used to ensure [UserService] compiles, but not show in the documentation. + * + * @author Rob Winch + */ +class Hovercard diff --git a/docs/src/test/kotlin/org/springframework/security/kt/docs/features/integrations/rest/type/UserService.kt b/docs/src/test/kotlin/org/springframework/security/kt/docs/features/integrations/rest/type/UserService.kt new file mode 100644 index 00000000000..531a8a50e72 --- /dev/null +++ b/docs/src/test/kotlin/org/springframework/security/kt/docs/features/integrations/rest/type/UserService.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2004-present the original author or authors. + * + * 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 clients copy of the License at + * + * https://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.springframework.security.kt.docs.features.integrations.rest.type + +import org.springframework.security.kt.docs.features.integrations.rest.clientregistrationid.User +import org.springframework.security.oauth2.client.annotation.ClientRegistrationId +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.service.annotation.GetExchange +import org.springframework.web.service.annotation.HttpExchange + +/** + * Demonstrates a service for [ClientRegistrationId] at the type level. + * @author Rob Winch + */ +// tag::type[] +@HttpExchange +@ClientRegistrationId("github") +interface UserService { + @GetExchange("/user") + fun getAuthenticatedUser(): User + + @GetExchange("/users/{username}/hovercard") + fun getHovercard(@PathVariable username: String): Hovercard +} +// end::type[]