diff --git a/services-api/src/main/java/io/scalecube/services/ServiceInfo.java b/services-api/src/main/java/io/scalecube/services/ServiceInfo.java index c6b7238fb..017765a91 100644 --- a/services-api/src/main/java/io/scalecube/services/ServiceInfo.java +++ b/services-api/src/main/java/io/scalecube/services/ServiceInfo.java @@ -15,8 +15,8 @@ public class ServiceInfo { private final Map tags; private final ServiceProviderErrorMapper errorMapper; private final ServiceMessageDataDecoder dataDecoder; - private final Authenticator authenticator; - private final PrincipalMapper principalMapper; + private final Authenticator authenticator; + private final PrincipalMapper principalMapper; private ServiceInfo(Builder builder) { this.serviceInstance = builder.serviceInstance; @@ -51,11 +51,11 @@ public ServiceMessageDataDecoder dataDecoder() { return dataDecoder; } - public Authenticator authenticator() { + public Authenticator authenticator() { return authenticator; } - public PrincipalMapper principalMapper() { + public PrincipalMapper principalMapper() { return principalMapper; } @@ -77,8 +77,8 @@ public static class Builder { private Map tags = new HashMap<>(); private ServiceProviderErrorMapper errorMapper; private ServiceMessageDataDecoder dataDecoder; - private Authenticator authenticator; - private PrincipalMapper principalMapper; + private Authenticator authenticator; + private PrincipalMapper principalMapper; private Builder(ServiceInfo serviceInfo) { this.serviceInstance = serviceInfo.serviceInstance; @@ -108,14 +108,16 @@ public Builder dataDecoder(ServiceMessageDataDecoder dataDecoder) { return this; } - public Builder authenticator(Authenticator authenticator) { - this.authenticator = authenticator; + @SuppressWarnings("unchecked") + public Builder authenticator(Authenticator authenticator) { + this.authenticator = (Authenticator) authenticator; return this; } @SuppressWarnings("unchecked") - public Builder principalMapper(PrincipalMapper principalMapper) { - this.principalMapper = (PrincipalMapper) principalMapper; + public Builder principalMapper( + PrincipalMapper principalMapper) { + this.principalMapper = (PrincipalMapper) principalMapper; return this; } @@ -133,14 +135,14 @@ Builder dataDecoderIfAbsent(ServiceMessageDataDecoder dataDecoder) { return this; } - Builder authenticatorIfAbsent(Authenticator authenticator) { + Builder authenticatorIfAbsent(Authenticator authenticator) { if (this.authenticator == null) { this.authenticator = authenticator; } return this; } - Builder principalMapperIfAbsent(PrincipalMapper principalMapper) { + Builder principalMapperIfAbsent(PrincipalMapper principalMapper) { if (this.principalMapper == null) { this.principalMapper = principalMapper; } diff --git a/services-api/src/main/java/io/scalecube/services/auth/Authenticator.java b/services-api/src/main/java/io/scalecube/services/auth/Authenticator.java index f3f8f0966..ca120f00e 100644 --- a/services-api/src/main/java/io/scalecube/services/auth/Authenticator.java +++ b/services-api/src/main/java/io/scalecube/services/auth/Authenticator.java @@ -4,7 +4,13 @@ import reactor.core.publisher.Mono; @FunctionalInterface -public interface Authenticator { +public interface Authenticator { + + /** + * Key in {@link reactor.util.context.Context} to represent authentication result after call to + * {@link #authenticate(Map)}. + */ + String AUTH_CONTEXT_KEY = "auth.context"; /** * Returns {@code authData} by given credentials. @@ -12,5 +18,5 @@ public interface Authenticator { * @param credentials credentials * @return async result with obtained {@code authData} */ - Mono> authenticate(Map credentials); + Mono authenticate(Map credentials); } diff --git a/services-api/src/main/java/io/scalecube/services/auth/PrincipalMapper.java b/services-api/src/main/java/io/scalecube/services/auth/PrincipalMapper.java index 9769a2820..1c1875d71 100644 --- a/services-api/src/main/java/io/scalecube/services/auth/PrincipalMapper.java +++ b/services-api/src/main/java/io/scalecube/services/auth/PrincipalMapper.java @@ -1,9 +1,7 @@ package io.scalecube.services.auth; -import java.util.Map; - @FunctionalInterface -public interface PrincipalMapper { +public interface PrincipalMapper { /** * Turns {@code authData} to concrete principal object. @@ -11,5 +9,5 @@ public interface PrincipalMapper { * @param authData auth data * @return converted principle object */ - O map(Map authData); + P map(A authData); } diff --git a/services-api/src/main/java/io/scalecube/services/methods/ServiceMethodInvoker.java b/services-api/src/main/java/io/scalecube/services/methods/ServiceMethodInvoker.java index 1a4196913..29e6b9c1e 100644 --- a/services-api/src/main/java/io/scalecube/services/methods/ServiceMethodInvoker.java +++ b/services-api/src/main/java/io/scalecube/services/methods/ServiceMethodInvoker.java @@ -11,7 +11,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collections; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.StringJoiner; @@ -31,8 +30,8 @@ public final class ServiceMethodInvoker { private final MethodInfo methodInfo; private final ServiceProviderErrorMapper errorMapper; private final ServiceMessageDataDecoder dataDecoder; - private final Authenticator authenticator; - private final PrincipalMapper principalMapper; + private final Authenticator authenticator; + private final PrincipalMapper principalMapper; /** * Constructs a service method invoker out of real service object instance and method info. @@ -51,8 +50,8 @@ public ServiceMethodInvoker( MethodInfo methodInfo, ServiceProviderErrorMapper errorMapper, ServiceMessageDataDecoder dataDecoder, - Authenticator authenticator, - PrincipalMapper principalMapper) { + Authenticator authenticator, + PrincipalMapper principalMapper) { this.method = Objects.requireNonNull(method, "method"); this.service = Objects.requireNonNull(service, "service"); this.methodInfo = Objects.requireNonNull(methodInfo, "methodInfo"); @@ -69,7 +68,7 @@ public ServiceMethodInvoker( * @return mono of service message */ public Mono invokeOne(ServiceMessage message) { - return Mono.defer(() -> authenticate(message)) + return Mono.deferWithContext(context -> authenticate(message, context)) .flatMap(authData -> deferWithContextOne(message, authData)) .map(response -> toResponse(response, message.dataFormat())) .onErrorResume(throwable -> Mono.just(errorMapper.toMessage(throwable))); @@ -82,7 +81,7 @@ public Mono invokeOne(ServiceMessage message) { * @return flux of service messages */ public Flux invokeMany(ServiceMessage message) { - return Mono.defer(() -> authenticate(message)) + return Mono.deferWithContext(context -> authenticate(message, context)) .flatMapMany(authData -> deferWithContextMany(message, authData)) .map(response -> toResponse(response, message.dataFormat())) .onErrorResume(throwable -> Flux.just(errorMapper.toMessage(throwable))); @@ -98,24 +97,23 @@ public Flux invokeBidirectional(Publisher publis return Flux.from(publisher) .switchOnFirst( (first, messages) -> - Mono.defer(() -> authenticate(first.get())) + Mono.deferWithContext(context -> authenticate(first.get(), context)) .flatMapMany(authData -> deferWithContextBidirectional(messages, authData)) .map(response -> toResponse(response, first.get().dataFormat()))) .onErrorResume(throwable -> Flux.just(errorMapper.toMessage(throwable))); } - private Mono deferWithContextOne(ServiceMessage message, Map authData) { + private Mono deferWithContextOne(ServiceMessage message, Object authData) { return Mono.deferWithContext(context -> Mono.from(invoke(toRequest(message)))) .subscriberContext(context -> newPrincipalContext(authData, context)); } - private Flux deferWithContextMany(ServiceMessage message, Map authData) { + private Flux deferWithContextMany(ServiceMessage message, Object authData) { return Flux.deferWithContext(context -> Flux.from(invoke(toRequest(message)))) .subscriberContext(context -> newPrincipalContext(authData, context)); } - private Flux deferWithContextBidirectional( - Flux messages, Map authData) { + private Flux deferWithContextBidirectional(Flux messages, Object authData) { return Flux.deferWithContext(context -> messages.map(this::toRequest).transform(this::invoke)) .subscriberContext(context -> newPrincipalContext(authData, context)); } @@ -149,10 +147,13 @@ private Object[] prepareArguments(Object request) { return arguments; } - private Mono> authenticate(ServiceMessage message) { + private Mono authenticate(ServiceMessage message, Context context) { if (!methodInfo.isAuth()) { return Mono.just(Collections.emptyMap()); } + if (context.hasKey(Authenticator.AUTH_CONTEXT_KEY)) { + return Mono.just(context.get(Authenticator.AUTH_CONTEXT_KEY)); + } if (authenticator == null) { throw new UnauthorizedException("Authenticator not found"); } @@ -201,12 +202,12 @@ private ServiceMessage toResponse(Object response, String dataFormat) { .build(); } - private Context newPrincipalContext(Map authData, Context context) { + private Context newPrincipalContext(Object authData, Context context) { if (principalMapper == null) { return context; } Object value = principalMapper.map(authData); - return Context.of(value.getClass(), value); + return Context.of(Authenticator.AUTH_CONTEXT_KEY, value); } public Object service() { diff --git a/services-api/src/test/java/io/scalecube/services/methods/ServiceMethodInvokerTest.java b/services-api/src/test/java/io/scalecube/services/methods/ServiceMethodInvokerTest.java index d833a183c..9d17cbfbf 100644 --- a/services-api/src/test/java/io/scalecube/services/methods/ServiceMethodInvokerTest.java +++ b/services-api/src/test/java/io/scalecube/services/methods/ServiceMethodInvokerTest.java @@ -7,8 +7,12 @@ import io.scalecube.services.exceptions.DefaultErrorMapper; import io.scalecube.services.transport.api.ServiceMessageDataDecoder; import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Map; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.Mockito; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -17,11 +21,15 @@ class ServiceMethodInvokerTest { private static final String qualifierPrefix = "io.scalecube.services.methods.StubService/"; - private static final boolean AUTH = false; + private static final boolean AUTH = true; + public static final boolean IS_RETURN_TYPE_SERVICE_MESSAGE = false; + public static final boolean IS_REQUEST_TYPE_SERVICE_MESSAGE = false; + public static final Map AUTH_DATA = + Collections.singletonMap("token", "asdjf9asdjf0as9fkasdf9afkds"); private final ServiceMessageDataDecoder dataDecoder = (message, type) -> message; - private final PrincipalMapper principalMapper = authData -> authData; - private final Authenticator authenticator = Mono::just; + private final PrincipalMapper principalMapper = authData -> authData; + private final Authenticator authenticator = Mono::just; private final StubService stubService = new StubServiceImpl(); private ServiceMethodInvoker serviceMethodInvoker; @@ -38,11 +46,11 @@ void testInvokeOneWhenReturnNull() throws Exception { serviceClass.getName(), methodName, method.getReturnType(), - false, + IS_RETURN_TYPE_SERVICE_MESSAGE, CommunicationMode.REQUEST_RESPONSE, method.getParameterCount(), Void.TYPE, - false, + IS_REQUEST_TYPE_SERVICE_MESSAGE, AUTH); serviceMethodInvoker = @@ -73,11 +81,11 @@ void testInvokeManyWhenReturnNull() throws Exception { serviceClass.getName(), methodName, method.getReturnType(), - false, + IS_RETURN_TYPE_SERVICE_MESSAGE, CommunicationMode.REQUEST_STREAM, method.getParameterCount(), Void.TYPE, - false, + IS_REQUEST_TYPE_SERVICE_MESSAGE, AUTH); serviceMethodInvoker = @@ -108,11 +116,11 @@ void testInvokeBidirectionalWhenReturnNull() throws Exception { serviceClass.getName(), methodName, method.getReturnType(), - false, + IS_RETURN_TYPE_SERVICE_MESSAGE, CommunicationMode.REQUEST_CHANNEL, method.getParameterCount(), Void.TYPE, - false, + IS_REQUEST_TYPE_SERVICE_MESSAGE, AUTH); serviceMethodInvoker = @@ -144,11 +152,11 @@ void testInvokeOneWhenThrowException() throws Exception { serviceClass.getName(), methodName, method.getReturnType(), - false, + IS_RETURN_TYPE_SERVICE_MESSAGE, CommunicationMode.REQUEST_RESPONSE, method.getParameterCount(), Void.TYPE, - false, + IS_REQUEST_TYPE_SERVICE_MESSAGE, AUTH); serviceMethodInvoker = @@ -182,11 +190,11 @@ void testInvokeManyWhenThrowException() throws Exception { serviceClass.getName(), methodName, method.getReturnType(), - false, + IS_RETURN_TYPE_SERVICE_MESSAGE, CommunicationMode.REQUEST_STREAM, method.getParameterCount(), Void.TYPE, - false, + IS_REQUEST_TYPE_SERVICE_MESSAGE, AUTH); serviceMethodInvoker = @@ -219,11 +227,11 @@ void testInvokeBidirectionalWhenThrowException() throws Exception { serviceClass.getName(), methodName, method.getReturnType(), - false, + IS_RETURN_TYPE_SERVICE_MESSAGE, CommunicationMode.REQUEST_CHANNEL, method.getParameterCount(), Void.TYPE, - false, + IS_REQUEST_TYPE_SERVICE_MESSAGE, AUTH); serviceMethodInvoker = @@ -245,4 +253,131 @@ void testInvokeBidirectionalWhenThrowException() throws Exception { StepVerifier.create(invokeOne).assertNext(ServiceMessage::isError).verifyComplete(); } + + @Test + @DisplayName( + "invocation of auth method should return error " + + "if there're no auth.context and no authenticator") + void testAuthMethodWhenNoContextAndNoAuthenticator() throws Exception { + final String methodName = "throwException"; + final Class serviceClass = stubService.getClass(); + final Method method = serviceClass.getMethod(methodName); + + final MethodInfo methodInfo = + new MethodInfo( + serviceClass.getName(), + methodName, + method.getReturnType(), + IS_RETURN_TYPE_SERVICE_MESSAGE, + CommunicationMode.REQUEST_RESPONSE, + method.getParameterCount(), + Void.TYPE, + IS_REQUEST_TYPE_SERVICE_MESSAGE, + true /*auth*/); + + serviceMethodInvoker = + new ServiceMethodInvoker( + method, + stubService, + methodInfo, + DefaultErrorMapper.INSTANCE, + dataDecoder, + null /*authenticator*/, + principalMapper); + + ServiceMessage message = + ServiceMessage.builder().qualifier(qualifierPrefix + methodName).build(); + + // invokeOne + final Mono invokeOne = serviceMethodInvoker.invokeOne(message); + + StepVerifier.create(invokeOne).assertNext(ServiceMessage::isError).verifyComplete(); + } + + @Test + @DisplayName( + "invocation of auth method should return empty response " + + "if auth.context exists and no authenticator") + void testAuthMethodWhenThereIsContextAndNoAuthenticator() throws Exception { + final String methodName = "helloAuthContext"; + final Class serviceClass = stubService.getClass(); + final Method method = serviceClass.getMethod(methodName); + + final MethodInfo methodInfo = + new MethodInfo( + serviceClass.getName(), + methodName, + method.getReturnType(), + IS_RETURN_TYPE_SERVICE_MESSAGE, + CommunicationMode.REQUEST_RESPONSE, + method.getParameterCount(), + Void.TYPE, + IS_REQUEST_TYPE_SERVICE_MESSAGE, + true /*auth*/); + + serviceMethodInvoker = + new ServiceMethodInvoker( + method, + stubService, + methodInfo, + DefaultErrorMapper.INSTANCE, + dataDecoder, + null /*authenticator*/, + principalMapper); + + ServiceMessage message = + ServiceMessage.builder().qualifier(qualifierPrefix + methodName).build(); + + StepVerifier.create( + Mono.deferWithContext(context -> serviceMethodInvoker.invokeOne(message)) + .subscriberContext( + context -> context.put(Authenticator.AUTH_CONTEXT_KEY, AUTH_DATA))) + .verifyComplete(); + } + + @Test + @DisplayName( + "invocation of auth method should return empty response " + + "if there're no auth.context but authenticator exists") + void testAuthMethodWhenNoContextButThereIsAuthenticator() throws Exception { + final String methodName = "helloAuthContext"; + final Class serviceClass = stubService.getClass(); + final Method method = serviceClass.getMethod(methodName); + + final MethodInfo methodInfo = + new MethodInfo( + serviceClass.getName(), + methodName, + method.getReturnType(), + IS_RETURN_TYPE_SERVICE_MESSAGE, + CommunicationMode.REQUEST_RESPONSE, + method.getParameterCount(), + Void.TYPE, + IS_REQUEST_TYPE_SERVICE_MESSAGE, + true /*auth*/); + + //noinspection unchecked + Authenticator mockedAuthenticator = Mockito.mock(Authenticator.class); + Mockito.when(mockedAuthenticator.authenticate(ArgumentMatchers.anyMap())) + .thenReturn(Mono.just(AUTH_DATA)); + + serviceMethodInvoker = + new ServiceMethodInvoker( + method, + stubService, + methodInfo, + DefaultErrorMapper.INSTANCE, + dataDecoder, + mockedAuthenticator, + principalMapper); + + ServiceMessage message = + ServiceMessage.builder().qualifier(qualifierPrefix + methodName).build(); + + StepVerifier.create( + Mono.deferWithContext(context -> serviceMethodInvoker.invokeOne(message)) + .subscriberContext( + context -> context.put(Authenticator.AUTH_CONTEXT_KEY, AUTH_DATA))) + .verifyComplete(); + } } diff --git a/services-api/src/test/java/io/scalecube/services/methods/StubService.java b/services-api/src/test/java/io/scalecube/services/methods/StubService.java index 9d48c0d1a..7acc50f09 100644 --- a/services-api/src/test/java/io/scalecube/services/methods/StubService.java +++ b/services-api/src/test/java/io/scalecube/services/methods/StubService.java @@ -25,4 +25,7 @@ public interface StubService { @ServiceMethod Flux throwException3(Flux request); + + @ServiceMethod + Mono helloAuthContext(); } diff --git a/services-api/src/test/java/io/scalecube/services/methods/StubServiceImpl.java b/services-api/src/test/java/io/scalecube/services/methods/StubServiceImpl.java index 45922ab07..2c7f5ae63 100644 --- a/services-api/src/test/java/io/scalecube/services/methods/StubServiceImpl.java +++ b/services-api/src/test/java/io/scalecube/services/methods/StubServiceImpl.java @@ -1,5 +1,6 @@ package io.scalecube.services.methods; +import io.scalecube.services.auth.Authenticator; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -34,4 +35,10 @@ public Flux throwException2() { public Flux throwException3(Flux request) { throw new RuntimeException(); } + + @Override + public Mono helloAuthContext() { + return Mono.deferWithContext( + context -> Mono.fromRunnable(() -> context.get(Authenticator.AUTH_CONTEXT_KEY))); + } } diff --git a/services/src/main/java/io/scalecube/services/Microservices.java b/services/src/main/java/io/scalecube/services/Microservices.java index 5e513116a..d47fcae37 100644 --- a/services/src/main/java/io/scalecube/services/Microservices.java +++ b/services/src/main/java/io/scalecube/services/Microservices.java @@ -119,14 +119,14 @@ public final class Microservices { private final List serviceProviders; private final ServiceRegistry serviceRegistry; private final ServiceMethodRegistry methodRegistry; - private final Authenticator authenticator; + private final Authenticator authenticator; private final ServiceTransportBootstrap transportBootstrap; private final GatewayBootstrap gatewayBootstrap; private final ServiceDiscoveryBootstrap discoveryBootstrap; private final ServiceProviderErrorMapper errorMapper; private final ServiceMessageDataDecoder dataDecoder; private final String contentType; - private final PrincipalMapper principalMapper; + private final PrincipalMapper principalMapper; private final MonoProcessor shutdown = MonoProcessor.create(); private final MonoProcessor onShutdown = MonoProcessor.create(); @@ -312,7 +312,7 @@ public static final class Builder { private List serviceProviders = new ArrayList<>(); private ServiceRegistry serviceRegistry = new ServiceRegistryImpl(); private ServiceMethodRegistry methodRegistry = new ServiceMethodRegistryImpl(); - private Authenticator authenticator = null; + private Authenticator authenticator = null; private ServiceDiscoveryBootstrap discoveryBootstrap = new ServiceDiscoveryBootstrap(); private ServiceTransportBootstrap transportBootstrap = new ServiceTransportBootstrap(); private GatewayBootstrap gatewayBootstrap = new GatewayBootstrap(); @@ -321,7 +321,7 @@ public static final class Builder { Optional.ofNullable(ServiceMessageDataDecoder.INSTANCE) .orElse((message, dataType) -> message); private String contentType = "application/json"; - private PrincipalMapper principalMapper; + private PrincipalMapper principalMapper = authData -> authData; public Mono start() { return Mono.defer(() -> new Microservices(this).start()); @@ -377,9 +377,10 @@ public Builder methodRegistry(ServiceMethodRegistry methodRegistry) { * @param authenticator authenticator * @return this builder with applied parameter */ + @SuppressWarnings("unchecked") @Deprecated - public Builder authenticator(Authenticator authenticator) { - this.authenticator = authenticator; + public Builder authenticator(Authenticator authenticator) { + this.authenticator = (Authenticator) authenticator; return this; } @@ -454,8 +455,9 @@ public Builder defaultContentType(String contentType) { * @param authenticator authenticator * @return this builder with applied parameter */ - public Builder defaultAuthenticator(Authenticator authenticator) { - this.authenticator = authenticator; + @SuppressWarnings("unchecked") + public Builder defaultAuthenticator(Authenticator authenticator) { + this.authenticator = (Authenticator) authenticator; return this; } @@ -463,12 +465,14 @@ public Builder defaultAuthenticator(Authenticator authenticator) { * Setter for default {@code principalMapper}. * * @param principalMapper principalMapper + * @param auth data type * @param principal type * @return this builder with applied parameter */ - public Builder defaultPrincipalMapper(PrincipalMapper principalMapper) { - //noinspection unchecked - this.principalMapper = (PrincipalMapper) principalMapper; + @SuppressWarnings("unchecked") + public Builder defaultPrincipalMapper( + PrincipalMapper principalMapper) { + this.principalMapper = (PrincipalMapper) principalMapper; return this; } } diff --git a/services/src/test/java/io/scalecube/services/ServiceAuthRemoteTest.java b/services/src/test/java/io/scalecube/services/ServiceAuthRemoteTest.java index 0d9435a48..4ed7d98b2 100644 --- a/services/src/test/java/io/scalecube/services/ServiceAuthRemoteTest.java +++ b/services/src/test/java/io/scalecube/services/ServiceAuthRemoteTest.java @@ -35,7 +35,7 @@ final class ServiceAuthRemoteTest extends BaseTest { CREDENTIALS.put("password", "qwerty"); } - private static final Authenticator authenticator = + private static final Authenticator> authenticator = headers -> { String username = headers.get("username"); String password = headers.get("password"); @@ -52,7 +52,7 @@ final class ServiceAuthRemoteTest extends BaseTest { private static Microservices caller; private static Microservices service; - public static PrincipalMapper principalMapper; + public static PrincipalMapper, UserProfile> principalMapper; @BeforeAll static void beforeAll() { diff --git a/services/src/test/java/io/scalecube/services/sut/security/SecuredServiceImpl.java b/services/src/test/java/io/scalecube/services/sut/security/SecuredServiceImpl.java index 9151c9643..942fb7179 100644 --- a/services/src/test/java/io/scalecube/services/sut/security/SecuredServiceImpl.java +++ b/services/src/test/java/io/scalecube/services/sut/security/SecuredServiceImpl.java @@ -1,5 +1,6 @@ package io.scalecube.services.sut.security; +import io.scalecube.services.auth.Authenticator; import io.scalecube.services.exceptions.ForbiddenException; import reactor.core.publisher.Mono; @@ -12,7 +13,8 @@ public Mono helloWithRequest(String name) { @Override public Mono helloWithPrincipal() { - return Mono.deferWithContext(context -> Mono.just(context.get(UserProfile.class))) + return Mono.deferWithContext(context -> Mono.just(context.get(Authenticator.AUTH_CONTEXT_KEY))) + .cast(UserProfile.class) .flatMap( user -> { checkPrincipal(user); @@ -22,7 +24,8 @@ public Mono helloWithPrincipal() { @Override public Mono helloWithRequestAndPrincipal(String name) { - return Mono.deferWithContext(context -> Mono.just(context.get(UserProfile.class))) + return Mono.deferWithContext(context -> Mono.just(context.get(Authenticator.AUTH_CONTEXT_KEY))) + .cast(UserProfile.class) .flatMap( user -> { checkPrincipal(user);